Files
freemo ca3399e177
ci.yml / fix(acms): invoke SkeletonCompressor in ContextAssembler.assemble() to propagate skeleton context to child plans (pull_request) Failing after 0s
CI / lint (pull_request) Failing after 31s
CI / quality (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 51s
CI / security (pull_request) Successful in 58s
CI / coverage (pull_request) Has been skipped
CI / build (pull_request) Successful in 19s
CI / helm (pull_request) Successful in 24s
CI / unit_tests (pull_request) Failing after 6m58s
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 17m14s
CI / integration_tests (pull_request) Failing after 22m37s
CI / status-check (pull_request) Failing after 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
fix(acms): invoke SkeletonCompressor in ContextAssembler.assemble() to propagate skeleton context to child plans
Added skeleton_fragments: tuple[ContextFragment, ...] field to ContextPayload in context_fragment.py
- Enables carrying compressed skeleton fragments along with normal context.

Extended ACMSPipeline.assemble() in acms_service.py
- Introduced skeleton_ratio: float = 0.15 (default matching spec) and parent_fragments: tuple[ContextFragment, ...] | None = None parameters.
- These same parameters are also added to ContextAssemblyPipeline.assemble() in acms_pipeline.py for consistency.

Skeleton compression integration
- In Phase 3 of both assemble() methods, computed skeleton_budget = int(budget.available_tokens * skeleton_ratio) and invoked self._skeleton_compressor.compress(parent_fragments, skeleton_budget).
- Compressed skeleton fragments are included in the returned ContextPayload.skeleton_fragments, enabling propagation of skeleton context to child plans.

Tests and behavior coverage
- Added a TDD issue-capture Behave scenario (@tdd_issue @tdd_issue_3563) to demonstrate the fix.
- Added four Behave unit test scenarios asserting: compressor invocation, correct arguments, skeleton presence in output, and skeleton_ratio budget enforcement.
- Added a Robot Framework integration test: parent plan accumulates context → child plan spawned → child plan context contains non-empty skeleton.
- Added skeleton-context-inheritance command to helper_acms_pipeline.py to support testing and manual verification.

Key design decisions
- skeleton_ratio defaults to 0.15 to align with the spec's --skeleton-ratio default.
- parent_fragments is None by default to maintain backward compatibility (no skeleton compression when no parent context).
- skeleton_budget is computed as skeleton_budget = int(budget.available_tokens * skeleton_ratio), deriving the skeleton budget from the total token budget.
- Both ACMSPipeline and ContextAssemblyPipeline are fixed to maintain consistency across the codepath.

ISSUES CLOSED: #3563
2026-04-05 21:26:33 +00:00

676 lines
32 KiB
Gherkin

@phase2 @acms @acms_pipeline
Feature: ACMS v1 Context Assembly Pipeline
As a CleverAgents developer
I want to assemble context fragments into budget-constrained payloads
So that actors receive relevant, prioritized context within token limits
# ---------------------------------------------------------------------------
# ContextFragment Domain Model
# ---------------------------------------------------------------------------
@acms_fragment
Scenario: Create a context fragment with defaults
Given a context fragment with uko_node "project://app/main.py" and content "hello world" and token_count 10
Then the fragment uko_node should be "project://app/main.py"
And the fragment content should be "hello world"
And the fragment relevance score should be 0.5
And the fragment token count should be 10
And the fragment detail depth should be 0
And the fragment tier should be "warm"
And the fragment metadata should be empty
And the fragment id should be set
And the fragment created_at should be a datetime
And the fragment provenance resource_uri should be set
@acms_fragment
Scenario: Create a fragment with all fields specified
Given a context fragment with all fields:
| field | value |
| uko_node | project://app/io |
| content | Use async IO pattern |
| score | 0.95 |
| tokens | 150 |
| detail_depth | 5 |
| tier | hot |
| meta_key | priority |
| meta_val | high |
| resource_uri | project://app/io |
Then the fragment uko_node should be "project://app/io"
And the fragment content should be "Use async IO pattern"
And the fragment relevance score should be 0.95
And the fragment token count should be 150
And the fragment detail depth should be 5
And the fragment tier should be "hot"
And the fragment metadata key "priority" should be "high"
@acms_fragment @validation
Scenario: Invalid relevance score rejected
When I create a fragment with relevance score 1.5
Then an ACMS validation error should be raised
@acms_fragment @validation
Scenario: Invalid token count rejected
When I create a fragment with token count -1
Then an ACMS validation error should be raised
@acms_fragment
Scenario: Invalid tier rejected
Given the ACMS pipeline modules are available
When I create a fragment with invalid tier "lukewarm"
Then an ACMS validation error should be raised
@acms_fragment @validation
Scenario: Missing uko_node rejected
When I create a fragment with empty uko_node
Then an ACMS validation error should be raised
@acms_fragment @validation
Scenario: Detail depth out of range rejected
When I create a fragment with detail depth 10
Then an ACMS validation error should be raised
# ---------------------------------------------------------------------------
# Structural field assertions — verify payload fields exist
# ---------------------------------------------------------------------------
@acms_fragment @structural
Scenario: ContextFragment has all spec-required fields
Given a context fragment with uko_node "project://app/main.py" and content "x" and token_count 1
Then the fragment should have field "fragment_id"
And the fragment should have field "uko_node"
And the fragment should have field "content"
And the fragment should have field "detail_depth"
And the fragment should have field "token_count"
And the fragment should have field "relevance_score"
And the fragment should have field "provenance"
And the fragment should have field "tier"
And the fragment should have field "metadata"
And the fragment should have field "created_at"
@acms_payload @structural
Scenario: ContextPayload has all spec-required fields
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/main.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 4096 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload should have field "payload_id"
And the payload should have field "plan_id"
And the payload should have field "fragments"
And the payload should have field "total_tokens"
And the payload should have field "budget"
And the payload should have field "budget_used"
And the payload should have field "strategies_used"
And the payload should have field "context_hash"
And the payload should have field "preamble"
And the payload should have field "provenance_map"
And the payload should have field "assembled_at"
# ---------------------------------------------------------------------------
# ContextBudget — Domain Model
# ---------------------------------------------------------------------------
@acms_budget
Scenario: Context budget calculates available tokens
Given a context budget with max_tokens 4096 and reserved_tokens 512
Then the available tokens should be 3584
@acms_budget
Scenario: Context budget with zero reserved tokens
Given a context budget with max_tokens 2048 and reserved_tokens 0
Then the available tokens should be 2048
@acms_budget
Scenario: Budget rejects reserved_tokens >= max_tokens
Given the ACMS pipeline modules are available
When I create a budget with reserved_tokens equal to max_tokens
Then an ACMS validation error should be raised
@acms_budget @validation
Scenario: Budget rejects reserved_tokens greater than max_tokens
When I create a budget with reserved_tokens greater than max_tokens
Then an ACMS validation error should be raised
# ---------------------------------------------------------------------------
# ACMSPipeline — Assembly
# ---------------------------------------------------------------------------
@acms_assemble
Scenario: Assemble with relevance strategy selects highest-relevance fragments
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/file.py | alpha | 0.9 | 100 |
| project://app/decision/1 | beta | 0.3 | 100 |
| project://app/resource/1 | gamma | 0.7 | 100 |
And a context budget with max_tokens 250 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload should contain 2 fragments
And the first fragment content should be "alpha"
And the second fragment content should be "gamma"
@acms_assemble
Scenario: Assemble with recency strategy selects most recent fragments
Given context fragments with different timestamps:
| uko_node | content | tokens | created_at |
| project://app/old.py | old | 100 | 2024-01-01T00:00:00+00:00 |
| project://app/mid.py | mid | 100 | 2024-06-01T00:00:00+00:00 |
| project://app/new.py | new | 100 | 2025-01-01T00:00:00+00:00 |
And a context budget with max_tokens 200 and reserved_tokens 0
When I assemble with strategy "recency"
Then the payload should contain 2 fragments
And the first fragment content should be "new"
And the second fragment content should be "mid"
@acms_assemble
Scenario: Assemble with tiered strategy prioritizes hot tier
Given the following tiered context fragments:
| uko_node | content | score | tokens | tier |
| project://app/cold.py | cold-item | 0.9 | 100 | cold |
| project://app/hot.py | hot-item | 0.85 | 100 | hot |
| project://app/warm.py | warm-item | 0.8 | 100 | warm |
And a context budget with max_tokens 200 and reserved_tokens 0
When I assemble with strategy "tiered"
Then the payload should contain 2 fragments
And the first fragment content should be "hot-item"
And the second fragment content should be "warm-item"
@acms_assemble
Scenario: Assembly respects token budget
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 500 |
| project://app/b.py | b | 0.8 | 500 |
| project://app/c.py | c | 0.7 | 500 |
And a context budget with max_tokens 1000 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload should contain 2 fragments
And the payload total tokens should be 1000
And the payload should be within budget
@acms_assemble
Scenario: All fragments exceed budget individually
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/huge.py | huge | 0.9 | 5000 |
And a context budget with max_tokens 100 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload should contain 0 fragments
And the payload total tokens should be 0
@acms_assemble
Scenario: Assembly with empty fragments returns empty payload
Given no context fragments
And a context budget with max_tokens 4096 and reserved_tokens 512
When I assemble with strategy "relevance"
Then the payload should contain 0 fragments
And the payload total tokens should be 0
@acms_assemble
Scenario: Assembly with single fragment within budget includes it
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/only.py | only | 0.5 | 100 |
And a context budget with max_tokens 4096 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload should contain 1 fragments
And the first fragment content should be "only"
@acms_assemble
Scenario: Register and use custom context strategy
Given a custom context strategy that reverses fragment order
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/first.py | first | 0.5 | 50 |
| project://app/second.py | second | 0.9 | 50 |
And a context budget with max_tokens 200 and reserved_tokens 0
When I assemble with strategy "reverse"
Then the payload should contain 2 fragments
And the first fragment content should be "second"
@acms_assemble
Scenario: Assemble with default strategy when none specified
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/alpha.py | alpha | 0.9 | 100 |
| project://app/beta.py | beta | 0.3 | 100 |
And a context budget with max_tokens 250 and reserved_tokens 0
When I assemble without specifying a strategy
Then the payload strategies used should include "relevance"
And the payload should contain 2 fragments
@acms_assemble @validation
Scenario: Non-existent strategy name raises error
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/alpha.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 4096 and reserved_tokens 0
When I assemble with strategy "nonexistent"
Then an ACMS strategy error should be raised
# ---------------------------------------------------------------------------
# Strategy overwrite test
# ---------------------------------------------------------------------------
@acms_assemble
Scenario: Registering strategy with existing name overwrites it
Given a custom context strategy that reverses fragment order
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/first.py | first | 0.5 | 50 |
| project://app/second.py | second | 0.9 | 50 |
And a context budget with max_tokens 200 and reserved_tokens 0
When I register the reverse strategy as "relevance"
And I assemble with strategy "relevance"
Then the first fragment content should be "second"
# ---------------------------------------------------------------------------
# ContextPayload — Properties
# ---------------------------------------------------------------------------
@acms_payload
Scenario: Payload is_within_budget property is correct
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 100 |
And a context budget with max_tokens 200 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload should be within budget
@acms_payload
Scenario: Payload remaining_tokens property is correct
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload remaining tokens should be 400
@acms_payload
Scenario: Payload budget_used is correct
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 250 |
And a context budget with max_tokens 1000 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload budget used should be 0.25
@acms_payload
Scenario: Payload context_hash is non-empty
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload context hash should be non-empty
@acms_payload
Scenario: Payload provenance_map maps fragment IDs
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload provenance map should map each fragment ID
@acms_payload
Scenario: Payload strategies_used lists the strategy name
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "tiered"
Then the payload strategies used should include "tiered"
# ---------------------------------------------------------------------------
# Payload structural assertions
# ---------------------------------------------------------------------------
@acms_payload @structural
Scenario: Payload plan_id, payload_id, and assembled_at are populated
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | a | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload plan_id should be "01JQTESTPN00000000000000AA"
And the payload payload_id should be a non-empty ULID
And the payload assembled_at should be a recent UTC datetime
# ---------------------------------------------------------------------------
# Budget validation edge case
# ---------------------------------------------------------------------------
@acms_budget @validation
Scenario: Budget rejects reserved_tokens strictly greater than max_tokens
When I create a budget with reserved_tokens 200 and max_tokens 100
Then an ACMS validation error should be raised
# ---------------------------------------------------------------------------
# Pipeline component DI injection (T1)
# ---------------------------------------------------------------------------
@acms_assemble @di
Scenario: Custom strategy selector is invoked during assembly
Given a pipeline with a custom tracking strategy selector
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the custom strategy selector should have been called
# v1: strategies_used is hardcoded from the caller's input, not the
# selector output. This assertion validates the v1 pass-through path;
# multi-strategy fusion in a future milestone will make it meaningful.
And the payload strategies used should include "relevance"
@acms_assemble @di
Scenario: Custom budget allocator is invoked during assembly
Given a pipeline with a custom budget allocator that halves the budget
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the custom budget allocator should have been called
@acms_assemble @di
Scenario: Custom strategy executor is invoked during assembly
Given a pipeline with a custom strategy executor that reverses fragments
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.5 | 50 |
| project://app/b.py | beta | 0.9 | 50 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the custom strategy executor should have been called
And the first fragment content should be "beta"
@acms_assemble @di
Scenario: Custom deduplicator is invoked during assembly
Given a pipeline with a custom deduplicator that removes duplicates by uko_node
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
| project://app/a.py | alpha2 | 0.5 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the custom deduplicator should have been called
And the payload should contain 1 fragments
@acms_assemble @di
Scenario: Custom preamble generator is invoked during assembly
Given a pipeline with a custom preamble generator
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload preamble should be "Custom preamble: 1 fragments"
# ---------------------------------------------------------------------------
# SkeletonCompressor (T2)
# ---------------------------------------------------------------------------
@acms_assemble @skeleton
Scenario: DefaultSkeletonCompressor returns fragments unchanged
Given the ACMS pipeline modules are available
When I compress fragments with the default skeleton compressor and budget 100
Then the compressed fragments should equal the original fragments
@acms_assemble @skeleton
Scenario: Custom skeleton compressor can truncate fragments
Given the ACMS pipeline modules are available
When I compress fragments with a truncating skeleton compressor and budget 50
Then the compressed fragments should have reduced content
# ---------------------------------------------------------------------------
# Multi-candidate allocation (T3)
# ---------------------------------------------------------------------------
@acms_assemble @allocation
Scenario: Budget allocator distributes proportionally across multiple candidates
Given the ACMS pipeline modules are available
When I allocate budget 1000 across candidates with confidences 0.8 and 0.2
Then the first candidate should receive approximately 800 tokens
And the second candidate should receive approximately 200 tokens
And the total allocated should not exceed 1000
@acms_assemble @allocation
Scenario: Strategy selector returns all strategies when no hint given
Given the ACMS pipeline modules are available
When I select strategies with no strategy hint from 3 registered strategies
Then all 3 strategies should be returned with confidence scores
# ---------------------------------------------------------------------------
# plan_id ULID validation (SEC-1, SPEC-1, TEST-GAP-1)
# ---------------------------------------------------------------------------
@acms_payload @validation
Scenario: Payload rejects empty plan_id
Given the ACMS pipeline modules are available
When I create a payload with an empty plan_id
Then an ACMS validation error should be raised
@acms_payload @validation
Scenario: Payload accepts valid ULID plan_id
Given the ACMS pipeline modules are available
When I create a payload with plan_id "01JQTESTPN00000000000000AA"
Then the created payload plan_id should be "01JQTESTPN00000000000000AA"
@acms_payload @validation
Scenario: Payload rejects 25-character plan_id (too short for ULID)
Given the ACMS pipeline modules are available
When I create a payload with plan_id "01JQTESTPN0000000000000AA"
Then an ACMS validation error should be raised
@acms_payload @validation
Scenario: Payload rejects 27-character plan_id (too long for ULID)
Given the ACMS pipeline modules are available
When I create a payload with plan_id "01JQTESTPN000000000000000AA"
Then an ACMS validation error should be raised
@acms_payload @validation
Scenario: Payload rejects lowercase plan_id
Given the ACMS pipeline modules are available
When I create a payload with plan_id "01jqtestpn00000000000000aa"
Then an ACMS validation error should be raised
@acms_payload @validation
Scenario Outline: Payload rejects plan_id with excluded Crockford char <char>
Given the ACMS pipeline modules are available
When I create a payload with plan_id "<plan_id>"
Then an ACMS validation error should be raised
Examples:
| char | plan_id |
| I | 01JQTESTPN000000000000I0AA |
| L | 01JQTESTPN000000000000L0AA |
| O | 01JQTESTPN000000000000O0AA |
| U | 01JQTESTPN000000000000U0AA |
@acms_payload @validation @security
Scenario: Payload rejects path traversal in plan_id
Given the ACMS pipeline modules are available
When I create a payload with plan_id "01JQTESTPN0000000/../00AAA"
Then an ACMS validation error should be raised
@acms_payload @validation @security
Scenario: Payload rejects plan_id with slashes
Given the ACMS pipeline modules are available
When I create a payload with plan_id "01JQTESTPN00000/0000000AAA"
Then an ACMS validation error should be raised
@acms_payload @validation
Scenario: Payload rejects plan_id with whitespace
Given the ACMS pipeline modules are available
When I create a payload with plan_id " "
Then an ACMS validation error should be raised
# ---------------------------------------------------------------------------
# Strategy introspection (coverage: capabilities, can_handle, explain)
# ---------------------------------------------------------------------------
@acms_strategy @introspection
Scenario: Each built-in strategy declares distinct capabilities
Given the ACMS pipeline modules are available
When I inspect the capabilities of "relevance"
Then the strategy should declare supports_semantic_search as true
When I inspect the capabilities of "recency"
Then the strategy should declare supports_temporal_archaeology as true
When I inspect the capabilities of "tiered"
Then the strategy should declare supports_semantic_search as false
@acms_strategy @introspection
Scenario: Strategy confidence ranking determines selector priority
Given the ACMS pipeline modules are available
When I query can_handle on all three built-in strategies
Then "relevance" should have the highest confidence
And "tiered" should have higher confidence than "recency"
@acms_strategy @introspection
Scenario: Strategy explain returns strategy-specific descriptions
Given the ACMS pipeline modules are available
When I call explain on each built-in strategy
Then the "relevance" explanation should mention "relevance score"
And the "recency" explanation should mention "creation time"
And the "tiered" explanation should mention "tier priority"
# ---------------------------------------------------------------------------
# Fragment metadata overflow
# ---------------------------------------------------------------------------
@acms_fragment @validation
Scenario: Fragment rejects metadata with more than 64 entries
Given the ACMS pipeline modules are available
When I create a fragment with 65 metadata entries
Then an ACMS validation error should be raised
@acms_fragment @validation
Scenario: Fragment accepts metadata with exactly 64 entries
Given the ACMS pipeline modules are available
When I create a fragment with 64 metadata entries
Then the fragment should have 64 metadata entries
# ---------------------------------------------------------------------------
# Early plan_id rejection in assemble()
# ---------------------------------------------------------------------------
@acms_assemble @validation
Scenario: Pipeline rejects invalid plan_id before executing the pipeline
Given the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with plan_id "not-a-valid-ulid"
Then an ACMS ValueError should be raised mentioning "ULID"
# ---------------------------------------------------------------------------
# Provenance map immutability (TEST-2)
# ---------------------------------------------------------------------------
@acms_payload @immutability
Scenario: Provenance map is immune to external mutation after payload creation
Given the ACMS pipeline modules are available
When I create a payload with a mutable provenance map and mutate the source
Then the payload provenance map should be unchanged
# ---------------------------------------------------------------------------
# Allocator rounding edge cases (TEST-3)
# ---------------------------------------------------------------------------
@acms_assemble @allocation
Scenario: Budget allocator uses full budget with odd split across 3 candidates
Given the ACMS pipeline modules are available
When I allocate budget 999 across 3 candidates with equal confidence
Then the total allocated should equal exactly 999
And each allocation should be 333 or 334
@acms_assemble @allocation
Scenario: Budget allocator handles zero-confidence equal split without token loss
Given the ACMS pipeline modules are available
When I allocate budget 100 across 3 candidates with zero confidence
Then the total allocated should equal exactly 100
@acms_assemble @allocation
Scenario: Budget allocator distributes full budget with unequal confidences
Given the ACMS pipeline modules are available
When I allocate budget 10 across 3 candidates with confidences 0.5 0.3 0.2
Then the total allocated should equal exactly 10
# ---------------------------------------------------------------------------
# Strategy selector fallback (TEST-4)
# ---------------------------------------------------------------------------
@acms_assemble @di
Scenario: Assembly falls back when custom selector omits the requested strategy
Given a pipeline with a custom strategy selector that returns no candidates
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
| project://app/b.py | beta | 0.5 | 100 |
And a context budget with max_tokens 500 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the empty strategy selector should have been called
And the payload strategies used should include "relevance"
And the payload should contain 2 fragments
# ---------------------------------------------------------------------------
# Issue #3563 — SkeletonCompressor invocation in assemble() (TDD capture)
# ---------------------------------------------------------------------------
@acms_assemble @skeleton @tdd_issue @tdd_issue_3563 @issue_3563
Scenario: TDD capture — assemble() invokes skeleton compressor when parent_fragments provided
Given the ACMS pipeline modules are available
And a spy skeleton compressor is registered
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 1000 and reserved_tokens 0
And parent fragments for skeleton compression:
| uko_node | content | score | tokens |
| project://parent/x.py | parent code | 0.8 | 200 |
When I assemble with parent fragments and skeleton_ratio 0.15
Then the spy skeleton compressor should have been called
And the spy compressor should have received the parent fragments
And the spy compressor skeleton_budget should be 150
@acms_assemble @skeleton @issue_3563
Scenario: assemble() includes skeleton_fragments in the returned ContextPayload
Given the ACMS pipeline modules are available
And a spy skeleton compressor is registered
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 1000 and reserved_tokens 0
And parent fragments for skeleton compression:
| uko_node | content | score | tokens |
| project://parent/x.py | parent code | 0.8 | 200 |
When I assemble with parent fragments and skeleton_ratio 0.15
Then the payload skeleton_fragments should be non-empty
And the payload skeleton_fragments should equal the spy compressor output
@acms_assemble @skeleton @issue_3563
Scenario: assemble() skeleton_fragments is empty when no parent_fragments provided
Given the ACMS pipeline modules are available
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 1000 and reserved_tokens 0
When I assemble with strategy "relevance"
Then the payload skeleton_fragments should be empty
@acms_assemble @skeleton @issue_3563
Scenario: skeleton_ratio governs the skeleton budget passed to the compressor
Given the ACMS pipeline modules are available
And a spy skeleton compressor is registered
And the following context fragments:
| uko_node | content | score | tokens |
| project://app/a.py | alpha | 0.9 | 100 |
And a context budget with max_tokens 2000 and reserved_tokens 0
And parent fragments for skeleton compression:
| uko_node | content | score | tokens |
| project://parent/x.py | parent code | 0.8 | 200 |
When I assemble with parent fragments and skeleton_ratio 0.25
Then the spy compressor skeleton_budget should be 500
And the first fragment content should be "alpha"