"""Robot Framework helper for ACMS v1 pipeline integration tests. Provides a CLI-style interface for Robot to invoke ACMS pipeline operations and verify the results. Exit code 0 = success, 1 = failure. Usage: python robot/helper_acms_pipeline.py fragment-create python robot/helper_acms_pipeline.py budget-calc python robot/helper_acms_pipeline.py assemble-relevance python robot/helper_acms_pipeline.py assemble-recency python robot/helper_acms_pipeline.py assemble-tiered python robot/helper_acms_pipeline.py payload-budget-check python robot/helper_acms_pipeline.py assemble-size-budget """ from __future__ import annotations import sys from collections.abc import Callable from datetime import UTC, datetime from pathlib import Path _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC in sys.path: sys.path.remove(_SRC) sys.path.insert(0, _SRC) from cleveragents.application.services.acms_service import ACMSPipeline # noqa: E402 from cleveragents.domain.models.core.context_fragment import ( # noqa: E402 ContextBudget, ContextFragment, FragmentProvenance, ) from cleveragents.domain.models.core.context_policy import ContextView # noqa: E402 # Default provenance for test fragments. _DEFAULT_PROV = FragmentProvenance(resource_uri="test://robot") def _cmd_fragment_create() -> int: """Create a ContextFragment and verify defaults.""" frag = ContextFragment( uko_node="project://app/main.py", content="hello world", token_count=10, provenance=FragmentProvenance(resource_uri="project://app/main.py"), ) if frag.uko_node != "project://app/main.py": print( f"acms-fail: expected uko_node=project://app/main.py, got {frag.uko_node}" ) return 1 if frag.relevance_score != 0.5: print(f"acms-fail: expected score=0.5, got {frag.relevance_score}") return 1 if not frag.fragment_id: print("acms-fail: fragment_id is empty") return 1 if not isinstance(frag.created_at, datetime): print(f"acms-fail: created_at is not datetime, got {type(frag.created_at)}") return 1 if frag.token_count != 10: print(f"acms-fail: expected token_count=10, got {frag.token_count}") return 1 print(f"acms-fragment-ok: id={frag.fragment_id}") return 0 def _cmd_budget_calc() -> int: """Create ContextBudget and verify available_tokens.""" budget = ContextBudget(max_tokens=4096, reserved_tokens=512) expected = 3584 if budget.available_tokens != expected: print( f"acms-fail: expected available={expected}, got {budget.available_tokens}" ) return 1 print(f"acms-budget-ok: available={budget.available_tokens}") return 0 def _cmd_assemble_relevance() -> int: """Assemble fragments with relevance strategy.""" frags = [ ContextFragment( uko_node="project://app/high.py", content="high", relevance_score=0.9, token_count=100, provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/low.py", content="low", relevance_score=0.2, token_count=100, provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/mid.py", content="mid", relevance_score=0.6, token_count=100, provenance=_DEFAULT_PROV, ), ] budget = ContextBudget(max_tokens=200, reserved_tokens=0) pipeline = ACMSPipeline() payload = pipeline.assemble( plan_id="01JQTESTPN00000000000000AA", fragments=frags, budget=budget, strategy="relevance", ) if len(payload.fragments) != 2: print(f"acms-fail: expected 2 fragments, got {len(payload.fragments)}") return 1 if payload.fragments[0].content != "high": print(f"acms-fail: expected first=high, got {payload.fragments[0].content}") return 1 if not payload.strategies_used: print("acms-fail: strategies_used is empty") return 1 print(f"acms-assemble-relevance-ok: fragments={len(payload.fragments)}") return 0 def _cmd_assemble_recency() -> int: """Assemble fragments with recency strategy.""" frags = [ ContextFragment( uko_node="project://app/old.py", content="old", token_count=100, created_at=datetime(2024, 1, 1, tzinfo=UTC), provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/mid.py", content="mid", token_count=100, created_at=datetime(2024, 6, 1, tzinfo=UTC), provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/new.py", content="new", token_count=100, created_at=datetime(2025, 1, 1, tzinfo=UTC), provenance=_DEFAULT_PROV, ), ] budget = ContextBudget(max_tokens=200, reserved_tokens=0) pipeline = ACMSPipeline() payload = pipeline.assemble( plan_id="01JQTESTPN00000000000000AA", fragments=frags, budget=budget, strategy="recency", ) if len(payload.fragments) != 2: print(f"acms-fail: expected 2 fragments, got {len(payload.fragments)}") return 1 if payload.fragments[0].content != "new": print(f"acms-fail: expected first=new, got {payload.fragments[0].content}") return 1 if payload.fragments[1].content != "mid": print(f"acms-fail: expected second=mid, got {payload.fragments[1].content}") return 1 print(f"acms-assemble-recency-ok: fragments={len(payload.fragments)}") return 0 def _cmd_assemble_tiered() -> int: """Assemble fragments with tiered strategy.""" frags = [ ContextFragment( uko_node="project://app/cold.py", content="cold-item", relevance_score=0.9, token_count=100, tier="cold", provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/hot.py", content="hot-item", relevance_score=0.85, token_count=100, tier="hot", provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/warm.py", content="warm-item", relevance_score=0.8, token_count=100, tier="warm", provenance=_DEFAULT_PROV, ), ] budget = ContextBudget(max_tokens=200, reserved_tokens=0) pipeline = ACMSPipeline() payload = pipeline.assemble( plan_id="01JQTESTPN00000000000000AA", fragments=frags, budget=budget, strategy="tiered", ) if len(payload.fragments) != 2: print(f"acms-fail: expected 2 fragments, got {len(payload.fragments)}") return 1 if payload.fragments[0].content != "hot-item": print(f"acms-fail: expected first=hot-item, got {payload.fragments[0].content}") return 1 print(f"acms-assemble-tiered-ok: fragments={len(payload.fragments)}") return 0 def _cmd_payload_budget_check() -> int: """Verify is_within_budget property on assembled payload.""" frags = [ ContextFragment( uko_node="project://app/item.py", content="item", relevance_score=0.9, token_count=100, provenance=_DEFAULT_PROV, ), ] budget = ContextBudget(max_tokens=500, reserved_tokens=0) pipeline = ACMSPipeline() payload = pipeline.assemble( plan_id="01JQTESTPN00000000000000AA", fragments=frags, budget=budget, strategy="relevance", ) if not payload.is_within_budget: print("acms-fail: expected is_within_budget=True") return 1 if payload.remaining_tokens != 400: print(f"acms-fail: expected remaining=400, got {payload.remaining_tokens}") return 1 if not payload.context_hash: print("acms-fail: expected non-empty context_hash") return 1 if len(payload.context_hash) != 64: print( f"acms-fail: expected SHA-256 hex (64 chars), " f"got {len(payload.context_hash)}" ) return 1 try: int(payload.context_hash, 16) except ValueError: print(f"acms-fail: context_hash is not valid hex: {payload.context_hash!r}") return 1 print( f"acms-payload-budget-ok: within_budget={payload.is_within_budget} " f"remaining={payload.remaining_tokens}" ) return 0 def _cmd_assemble_size_budget() -> int: """Verify ACMSPipeline budget pre-filter with ContextView limits.""" frags = [ ContextFragment( uko_node="project://app/small.py", content="a" * 50, relevance_score=0.9, token_count=60, provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/too_big.py", content="b" * 200, relevance_score=0.8, token_count=60, provenance=_DEFAULT_PROV, ), ContextFragment( uko_node="project://app/fit.py", content="c" * 80, relevance_score=0.7, token_count=60, provenance=_DEFAULT_PROV, ), ] budget = ContextBudget(max_tokens=1000, reserved_tokens=0) pipeline = ACMSPipeline() payload = pipeline.assemble( plan_id="01JQTESTPN00000000000000AA", fragments=frags, budget=budget, strategy="relevance", context_view=ContextView(max_file_size=100), ) result = pipeline.last_enforcement_result if result is None: print("acms-fail: expected non-empty enforcement result") return 1 if len(payload.fragments) != 2: print(f"acms-fail: expected 2 fragments, got {len(payload.fragments)}") return 1 if len(result.accepted) != 2: print(f"acms-fail: expected 2 accepted ids, got {len(result.accepted)}") return 1 if len(result.violations) != 1: print(f"acms-fail: expected 1 violation, got {len(result.violations)}") return 1 if result.violations[0].violation_type != "max_file_size": print( "acms-fail: expected violation_type=max_file_size, " f"got {result.violations[0].violation_type}" ) return 1 payload_ids = {fragment.fragment_id for fragment in payload.fragments} if payload_ids != set(result.accepted): print( "acms-fail: payload fragment ids do not match accepted ids " f"payload={sorted(payload_ids)} accepted={sorted(result.accepted)}" ) return 1 if result.total_size != 130: print(f"acms-fail: expected total_size=130, got {result.total_size}") return 1 print("acms-assemble-size-budget-ok: accepted=2 violations=1 total_size=130") return 0 def _cmd_skeleton_context_inheritance() -> int: """Verify parent plan context is compressed and propagated to child plan. Simulates the context inheritance scenario: 1. Parent plan accumulates context fragments. 2. Child plan is spawned — assemble() is called with parent_fragments. 3. The assembled ContextPayload.skeleton_fragments must be non-empty, confirming the SkeletonCompressor was invoked and the skeleton is available for the child plan. This is the integration test for issue #3563. """ from cleveragents.application.services.acms_service import DefaultSkeletonCompressor # --- Parent plan accumulates context --- parent_fragments = ( ContextFragment( uko_node="project://parent/strategy.py", content="class PlanStrategy: pass # parent plan strategy", token_count=50, relevance_score=0.9, provenance=FragmentProvenance(resource_uri="project://parent/strategy.py"), ), ContextFragment( uko_node="project://parent/executor.py", content="class PlanExecutor: pass # parent plan executor", token_count=40, relevance_score=0.7, provenance=FragmentProvenance(resource_uri="project://parent/executor.py"), ), ContextFragment( uko_node="project://parent/context.py", content="class ContextManager: pass # parent context manager", token_count=35, relevance_score=0.6, provenance=FragmentProvenance(resource_uri="project://parent/context.py"), ), ) # --- Child plan is spawned: assemble with parent_fragments --- child_fragments = [ ContextFragment( uko_node="project://child/task.py", content="class ChildTask: pass # child plan task", token_count=30, relevance_score=0.8, provenance=FragmentProvenance(resource_uri="project://child/task.py"), ), ] budget = ContextBudget(max_tokens=1000, reserved_tokens=0) skeleton_ratio = 0.15 pipeline = ACMSPipeline(skeleton_compressor=DefaultSkeletonCompressor()) payload = pipeline.assemble( plan_id="01JQTESTPN00000000000000AB", fragments=child_fragments, budget=budget, parent_fragments=parent_fragments, skeleton_ratio=skeleton_ratio, ) # Verify skeleton_fragments is non-empty (compressor was invoked) if not payload.skeleton_fragments: print( "acms-fail: payload.skeleton_fragments is empty — " "SkeletonCompressor was not invoked in assemble(). " "Child plan has no inherited parent context (issue #3563)." ) return 1 # Verify skeleton budget was computed correctly expected_budget = int(budget.available_tokens * skeleton_ratio) actual_tokens = sum(f.token_count for f in payload.skeleton_fragments) if actual_tokens > expected_budget: print( f"acms-fail: skeleton tokens {actual_tokens} " f"exceed budget {expected_budget}" ) return 1 # Verify child plan fragments are also present if not payload.fragments: print("acms-fail: child plan fragments are missing from payload") return 1 print( f"acms-skeleton-inheritance-ok: " f"skeleton_fragments={len(payload.skeleton_fragments)} " f"skeleton_tokens={actual_tokens} " f"skeleton_budget={expected_budget} " f"child_fragments={len(payload.fragments)}" ) return 0 _COMMANDS: dict[str, Callable[[], int]] = { "fragment-create": _cmd_fragment_create, "budget-calc": _cmd_budget_calc, "assemble-relevance": _cmd_assemble_relevance, "assemble-recency": _cmd_assemble_recency, "assemble-tiered": _cmd_assemble_tiered, "payload-budget-check": _cmd_payload_budget_check, "assemble-size-budget": _cmd_assemble_size_budget, "skeleton-context-inheritance": _cmd_skeleton_context_inheritance, } def main() -> int: """Entry point called by Robot Framework ``Run Process``.""" if len(sys.argv) < 2: print( "Usage: helper_acms_pipeline.py " "" ) return 1 command = sys.argv[1] handler = _COMMANDS.get(command) if handler is None: print(f"Unknown command: {command}") return 1 return handler() if __name__ == "__main__": sys.exit(main())