Files
aditya 37b6d27d0b feat(acms): implement builtin/context skill for CRP
Wire the three CRP tool handlers in context_ops.py to the ACMS pipeline
and ContextTierService, replacing NotImplementedError stubs with
functional implementations:

- request_context: Sources fragments from ContextTierService, filters
  by query/focus keywords, and delegates to ACMSPipeline.assemble()
  for budget-constrained context assembly. Accepts optional plan_id
  for actor-context invocation.

- query_history: Searches across hot/warm/cold tiers for fragments
  matching the query string via case-insensitive substring matching.
  Returns results sorted by last-accessed timestamp.

- get_context_budget: Returns current token budget state (max, reserved,
  available, used) computed from ContextBudget defaults and hot-tier
  fragment token counts.

Additionally:
- Register ACMSPipeline as a Singleton in the DI container
- Create build_context_skill_definition() factory for SkillRegistry
  auto-registration of the builtin/context skill
- Replace 3 obsolete NotImplementedError test scenarios with 8 new
  functional BDD scenarios covering all handler paths

Lint, typecheck, and coverage (98%) all pass.

ISSUES CLOSED: #873
2026-03-26 07:52:09 +00:00

255 lines
12 KiB
Gherkin

Feature: CRP (Context Request Protocol) Domain Models
As a developer
I want CRP domain models for the Advanced Context Management System
So that actors can issue structured context requests during reasoning
# ---- DetailLevelMap ----
Scenario: Create a DetailLevelMap with valid levels
Given a DetailLevelMap for domain "uko-code:" with max depth 9
And the map has level "MODULE_LISTING" at depth 0
And the map has level "FULL_SOURCE" at depth 9
Then the map should resolve "MODULE_LISTING" to 0
And the map should resolve "FULL_SOURCE" to 9
Scenario: DetailLevelMap clamps integer depth to max_depth
Given a DetailLevelMap for domain "uko-code:" with max depth 9
Then the map should resolve integer 15 to 9
And the map should resolve integer 0 to 0
And the map should resolve integer 5 to 5
Scenario: DetailLevelMap resolves from parent map
Given a parent DetailLevelMap for domain "uko-code:" with max depth 9
And the parent map has level "MODULE_LISTING" at depth 0
And a child DetailLevelMap for domain "uko-oo:" with max depth 11
And the child map has level "CLASS_HIERARCHY" at depth 3
Then the child map should resolve "MODULE_LISTING" to 0
And the child map should resolve "CLASS_HIERARCHY" to 3
Scenario: DetailLevelMap raises on unknown named level
Given a DetailLevelMap for domain "uko-code:" with max depth 9
Then resolving "NONEXISTENT" should raise ValueError
Scenario: DetailLevelMap register adds custom level
Given a DetailLevelMap for domain "uko-code:" with max depth 9
When I register level "CUSTOM_LEVEL" at depth 7
Then the map should resolve "CUSTOM_LEVEL" to 7
Scenario: DetailLevelMap rejects negative depth in levels
Then creating a DetailLevelMap with level "BAD" at depth -1 should raise ValueError
Scenario: DetailLevelMap register rejects negative depth
Given a DetailLevelMap for domain "uko-code:" with max depth 9
Then registering level "BAD" at depth -1 should raise ValueError
Scenario: DetailLevelMap effective_levels merges parent and child
Given a parent DetailLevelMap for domain "uko-code:" with max depth 9
And the parent map has level "MODULE_LISTING" at depth 0
And the parent map has level "FULL_SOURCE" at depth 9
And a child DetailLevelMap for domain "uko-oo:" with max depth 11
And the child map has level "CLASS_HIERARCHY" at depth 3
Then the child effective_levels should contain "MODULE_LISTING" at 0
And the child effective_levels should contain "FULL_SOURCE" at 9
And the child effective_levels should contain "CLASS_HIERARCHY" at 3
Scenario: DetailLevelMap created with pre-populated levels dict
Given a DetailLevelMap for domain "uko-code:" with max depth 9 and levels "SIGNATURES" at 4
Then the map should resolve "SIGNATURES" to 4
# ---- FragmentProvenance ----
Scenario: Create a valid FragmentProvenance
Given a FragmentProvenance with resource URI "uko-py:module/auth"
Then the provenance resource_uri should be "uko-py:module/auth"
And the provenance location should be empty
And the provenance strategy should be empty
Scenario: FragmentProvenance rejects empty resource_uri
Then creating a FragmentProvenance with empty resource_uri should raise ValueError
# ---- ContextBudget ----
Scenario: Create a valid ContextBudget
Given a ContextBudget with max_tokens 8000 and reserved_tokens 1000
Then the budget available_tokens should be 7000
And the budget max_tokens should be 8000
And the budget reserved_tokens should be 1000
Scenario: ContextBudget with zero reservation
Given a ContextBudget with max_tokens 4000 and reserved_tokens 0
Then the budget available_tokens should be 4000
Scenario: ContextBudget rejects negative max_tokens
Then creating a ContextBudget with max_tokens -1 should raise ValueError
Scenario: ContextBudget rejects negative reserved_tokens
Then creating a ContextBudget with max_tokens 100 and reserved_tokens -5 should raise ValueError
Scenario: ContextBudget rejects reserved exceeding max
Then creating a ContextBudget with max_tokens 100 and reserved_tokens 200 should raise ValueError
# ---- ContextFragment ----
Scenario: Create a valid ContextFragment
Given a ContextFragment with uko_node "uko-py:class/AuthManager" and content "class AuthManager: ..."
And the fragment has detail_depth 9 and token_count 800
And the fragment has relevance_score 0.95
Then the fragment uko_node should be "uko-py:class/AuthManager"
And the fragment token_count should be 800
And the fragment relevance_score should be 0.95
And the fragment detail_depth should be 9
Scenario: ContextFragment rejects negative token_count
Then creating a ContextFragment with token_count -1 should raise ValueError
Scenario: ContextFragment rejects relevance_score above 1.0
Then creating a ContextFragment with relevance_score 1.5 should raise ValueError
Scenario: ContextFragment rejects relevance_score below 0.0
Then creating a ContextFragment with relevance_score -0.1 should raise ValueError
Scenario: ContextFragment rejects negative detail_depth
Then creating a ContextFragment with detail_depth -1 should raise ValueError
Scenario: ContextFragment rejects empty uko_node
Then creating a ContextFragment with empty uko_node should raise ValueError
# ---- ContextRequest ----
Scenario: Create a ContextRequest with defaults
Given a ContextRequest with purpose "Understand the auth module"
Then the request purpose should be "Understand the auth module"
And the request breadth should be 2
And the request depth should be 3
And the request depth_gradient should be true
And the request priority should be 0.5
And the request temporal should be "current"
And the request query should be None
And the request max_tokens should be None
Scenario: Create a ContextRequest with all fields
Given a full ContextRequest with query "auth flow" and focus "uko-py:class/AuthManager"
And the request has breadth 3 and depth "SIGNATURES"
And the request has priority 0.8 and purpose "Review auth"
Then the request query should be "auth flow"
And the request breadth should be 3
And the request depth should be "SIGNATURES"
And the request priority should be 0.8
Scenario: ContextRequest rejects negative breadth
Then creating a ContextRequest with breadth -1 should raise ValueError
Scenario: ContextRequest rejects negative integer depth
Then creating a ContextRequest with depth -1 should raise ValueError
Scenario: ContextRequest rejects priority above 1.0
Then creating a ContextRequest with priority 1.5 should raise ValueError
Scenario: ContextRequest rejects priority below 0.0
Then creating a ContextRequest with priority -0.1 should raise ValueError
Scenario: ContextRequest accepts string depth
Given a ContextRequest with depth "FULL_SOURCE"
Then the request depth should be "FULL_SOURCE"
Scenario: ContextRequest accepts zero breadth
Given a ContextRequest with breadth 0
Then the request breadth should be 0
Scenario: ContextRequest accepts max_tokens
Given a ContextRequest with max_tokens 4096
Then the request max_tokens should be 4096
Scenario: ContextRequest rejects negative max_tokens
Then creating a ContextRequest with max_tokens -100 should raise ValueError
# ---- AssembledContext ----
Scenario: Create a valid AssembledContext
Given an AssembledContext with total_tokens 1500 and budget_used 0.75
And the assembled context has hash "abc123def"
Then the assembled total_tokens should be 1500
And the assembled budget_used should be 0.75
And the assembled context_hash should be "abc123def"
Scenario: AssembledContext rejects budget_used above 1.0
Then creating an AssembledContext with budget_used 1.5 should raise ValueError
Scenario: AssembledContext rejects negative total_tokens
Then creating an AssembledContext with total_tokens -1 should raise ValueError
# ---- Context Skill Tools (Wired to ACMS Pipeline) ----
Scenario: request_context returns assembled context with fragments
Given a context tier service with sample fragments
When I call the request_context tool with purpose "test context"
Then the request_context result should contain "fragments" key
And the request_context result should contain "total_tokens" key
Scenario: request_context returns empty result with no stored fragments
Given an empty context tier service
When I call the request_context tool with purpose "test context"
Then the request_context result "fragments" should be an empty list
And the request_context result "total_tokens" should be 0
Scenario: request_context filters fragments by query
Given a context tier service with sample fragments
When I call the request_context tool filtering by query "auth"
Then the request_context result "fragments" should only contain matching content
Scenario: request_context filters fragments by focus targets
Given a context tier service with sample fragments
When I call the request_context tool with focus on "auth"
Then the request_context result "fragments" should only contain matching content
Scenario: query_history returns entries for stored fragments
Given a context tier service with sample fragments
When I call the query_history tool with query "auth"
Then the query_history result should contain "entries" key
And the query_history result "entries" should not be empty
Scenario: query_history returns empty entries when no match
Given a context tier service with sample fragments
When I call the query_history tool with query "nonexistent_xyz_content"
Then the query_history result "entries" should be an empty list
Scenario: get_context_budget returns budget state
Given an empty context tier service
When I call the get_context_budget tool
Then the budget result should contain "max_tokens" key
And the budget result should contain "reserved_tokens" key
And the budget result should contain "available_tokens" key
And the budget result should contain "used_tokens" key
And the budget result "used_tokens" should be 0
Scenario: get_context_budget reports used tokens from hot tier
Given a context tier service with sample fragments
When I call the get_context_budget tool
Then the budget result "used_tokens" should be greater than 0
Scenario: builtin/context SkillDefinition can be built
When I build the builtin/context SkillDefinition
Then the skill definition name should be "builtin/context"
And the skill definition should have 3 resolved tools
And the skill definition should be read-only
Scenario: Context skill tools are registered in the tool registry
Given a CRP tool registry with context tools registered
Then the CRP registry should contain tool "builtin/request-context"
And the CRP registry should contain tool "builtin/query-history"
And the CRP registry should contain tool "builtin/get-context-budget"
Scenario: Context skill tools have read-only capability
Given a CRP tool registry with context tools registered
Then CRP tool "builtin/request-context" should have read_only capability
And CRP tool "builtin/query-history" should have read_only capability
And CRP tool "builtin/get-context-budget" should have read_only capability
Scenario: request_context tool requires purpose in schema
Given a CRP tool registry with context tools registered
Then CRP tool "builtin/request-context" should require "purpose" in input schema
Scenario: query_history tool requires query in schema
Given a CRP tool registry with context tools registered
Then CRP tool "builtin/query-history" should require "query" in input schema