@acms @acms_scope_resolution @scope_resolution Feature: ACMS Pipeline Scope Resolution and Context Inheritance As a CleverAgents developer I want pipeline components to be overridable at global, project, and plan scope So that context assembly can be customized per-plan or per-project with correct precedence # --------------------------------------------------------------------------- # PipelineScopeResolver -- Global scope # --------------------------------------------------------------------------- @scope_chain @global Scenario: Global default skeleton compressor is resolved when no overrides exist Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered When I resolve the skeleton compressor with no plan or project context Then the resolved skeleton compressor should be the global default And the pipeline scope resolution scope should be "global" @scope_chain @global Scenario: Global default preamble generator is resolved when no overrides exist Given a fresh PipelineScopeResolver And a global default PreambleGenerator registered When I resolve the preamble generator with no plan or project context Then the resolved preamble generator should be the global default And the pipeline scope resolution scope should be "global" @scope_chain @global Scenario: Global default deduplicator is resolved when no overrides exist Given a fresh PipelineScopeResolver And a global default FragmentDeduplicator registered When I resolve the deduplicator with no plan or project context Then the resolved deduplicator should be the global default And the pipeline scope resolution scope should be "global" @scope_chain @global Scenario: Global default depth resolver is resolved when no overrides exist Given a fresh PipelineScopeResolver And a global default DetailDepthResolver registered When I resolve the depth resolver with no plan or project context Then the resolved depth resolver should be the global default And the pipeline scope resolution scope should be "global" # --------------------------------------------------------------------------- # PipelineScopeResolver -- Project scope # --------------------------------------------------------------------------- @scope_chain @project Scenario: Project override takes precedence over global for skeleton compressor Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered And a project-level SkeletonCompressor override for project "my-project" When I resolve the skeleton compressor for project "my-project" Then the resolved skeleton compressor should be the project override And the pipeline scope resolution scope should be "project" @scope_chain @project Scenario: Project without override falls through to global for skeleton compressor Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered When I resolve the skeleton compressor for project "other-project" Then the resolved skeleton compressor should be the global default And the pipeline scope resolution scope should be "global" @scope_chain @project Scenario: Project override takes precedence over global for preamble generator Given a fresh PipelineScopeResolver And a global default PreambleGenerator registered And a project-level PreambleGenerator override for project "proj-a" When I resolve the preamble generator for project "proj-a" Then the resolved preamble generator should be the project override And the pipeline scope resolution scope should be "project" # --------------------------------------------------------------------------- # PipelineScopeResolver -- Plan scope (highest priority) # --------------------------------------------------------------------------- @scope_chain @plan Scenario: Plan override takes highest precedence over project and global Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered And a project-level SkeletonCompressor override for project "my-project" And a plan-level SkeletonCompressor override for plan "plan-001" When I resolve the skeleton compressor for plan "plan-001" and project "my-project" Then the resolved skeleton compressor should be the plan override And the pipeline scope resolution scope should be "plan" @scope_chain @plan Scenario: Plan without override falls through to project then global Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered And a project-level SkeletonCompressor override for project "my-project" When I resolve the skeleton compressor for plan "plan-002" and project "my-project" Then the resolved skeleton compressor should be the project override And the pipeline scope resolution scope should be "project" @scope_chain @plan Scenario: Plan override for deduplicator takes highest precedence Given a fresh PipelineScopeResolver And a global default FragmentDeduplicator registered And a plan-level FragmentDeduplicator override for plan "plan-dedup" When I resolve the deduplicator for plan "plan-dedup" Then the resolved deduplicator should be the plan override And the pipeline scope resolution scope should be "plan" @scope_chain @plan Scenario: Plan override for depth resolver takes highest precedence Given a fresh PipelineScopeResolver And a global default DetailDepthResolver registered And a plan-level DetailDepthResolver override for plan "plan-depth" When I resolve the depth resolver for plan "plan-depth" Then the resolved depth resolver should be the plan override And the pipeline scope resolution scope should be "plan" # --------------------------------------------------------------------------- # PipelineScopeResolver -- resolution_scope introspection # --------------------------------------------------------------------------- @scope_chain @introspection Scenario: resolution_scope returns global when only global is registered Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered When I check the resolution scope for SkeletonCompressor with no context Then the pipeline scope resolution scope should be "global" @scope_chain @introspection Scenario: resolution_scope returns plan when plan override exists Given a fresh PipelineScopeResolver And a global default SkeletonCompressor registered And a plan-level SkeletonCompressor override for plan "scope-plan" When I check the resolution scope for SkeletonCompressor for plan "scope-plan" Then the pipeline scope resolution scope should be "plan" # --------------------------------------------------------------------------- # ContextInheritanceService -- basic inheritance # --------------------------------------------------------------------------- @context_inheritance @basic Scenario: Child plan receives skeleton context from parent payload Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.15 And a parent payload with 3 fragments totaling 1000 tokens When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 2000 Then the skeleton fragments should be non-empty @context_inheritance @basic Scenario: Empty parent payload produces empty skeleton Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.15 And an empty parent payload When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 2000 Then the skeleton fragments should be empty @context_inheritance @basic Scenario: Zero skeleton budget produces empty skeleton Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.0 And a parent payload with 3 fragments totaling 1000 tokens When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 2000 Then the skeleton fragments should be empty @context_inheritance @basic Scenario: inherit_from_fragments compresses raw fragments for child plan Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.15 And parent fragments with 2 fragments totaling 500 tokens When I inherit from fragments for child plan "01JQTESTPN00000000000000AA" with budget 1000 Then the skeleton fragments should be non-empty @context_inheritance @basic Scenario: Empty parent fragments produces empty skeleton from inherit_from_fragments Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.15 And empty parent fragments When I inherit from fragments for child plan "01JQTESTPN00000000000000AA" with budget 1000 Then the skeleton fragments should be empty # --------------------------------------------------------------------------- # ContextInheritanceService -- scope resolution during inheritance # --------------------------------------------------------------------------- @context_inheritance @scope_chain Scenario: Context inheritance uses plan-level compressor when registered Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a plan-level tracking SkeletonCompressor for plan "01JQTESTPN00000000000000AA" And a ContextInheritanceService with default skeleton ratio 0.15 And a parent payload with 2 fragments totaling 500 tokens When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 1000 Then the plan-level tracking compressor should have been called @context_inheritance @scope_chain Scenario: Context inheritance uses project-level compressor when registered Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a project-level tracking SkeletonCompressor for project "my-project" And a ContextInheritanceService with default skeleton ratio 0.15 And a parent payload with 2 fragments totaling 500 tokens When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 1000 and project "my-project" Then the project-level tracking compressor should have been called # --------------------------------------------------------------------------- # ContextInheritanceService -- skeleton ratio # --------------------------------------------------------------------------- @context_inheritance @skeleton_ratio Scenario: Custom skeleton ratio governs the skeleton budget Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.15 And a parent payload with 1 fragment of 100 tokens When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 1000 and skeleton ratio 0.5 Then the skeleton fragments should be non-empty @context_inheritance @skeleton_ratio Scenario: Default skeleton ratio is used when none specified Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a ContextInheritanceService with default skeleton ratio 0.2 Then the service default skeleton ratio should be 0.2 # --------------------------------------------------------------------------- # ContextInheritanceService -- budget-respecting compressor # --------------------------------------------------------------------------- @context_inheritance @budget Scenario: Tracking compressor respects budget during inheritance Given a PipelineScopeResolver with a global DefaultSkeletonCompressor And a plan-level tracking SkeletonCompressor for plan "01JQTESTPN00000000000000AA" And a ContextInheritanceService with default skeleton ratio 0.15 And a parent payload with 3 fragments totaling 1000 tokens When I inherit context for child plan "01JQTESTPN00000000000000AA" with budget 2000 Then the plan-level tracking compressor should have been called And the skeleton fragments total tokens should be at most 300