- What was implemented
- Added PLAN_ROLLED_BACK event type to the EventType enum at src/cleveragents/infrastructure/events/types.py to properly represent successful rollbacks in the domain model.
- Implemented rollback_plan(plan_id: str, checkpoint_id: str) -> RollbackResult in PlanLifecycleService (src/cleveragents/application/services/plan_lifecycle_service.py) with:
- Plan state validation: rejects rollback when the plan is in terminal APPLIED or CANCELLED states.
- Delegation to CheckpointService.selective_rollback() to perform the actual rollback logic and obtain a RollbackResult.
- Emission of PLAN_ROLLED_BACK as a domain event to reflect the completed rollback.
- checkpoint_service is accepted as an optional constructor parameter; if not provided, a PlanError is raised to preserve backward compatibility.
- Updated CLI behavior in src/cleveragents/cli/commands/plan.py so agents plan rollback routes through PlanLifecycleService.rollback_plan() rather than calling CheckpointService.selective_rollback() directly.
- Updated PlanLifecycleService module docstring to include rollback_plan in the documented API.
- Added Behave feature file features/plan_lifecycle_rollback.feature with 11 scenarios covering state validation, domain events, and delegation.
- Added step implementations in features/steps/plan_lifecycle_rollback_steps.py to support the new scenarios.
- Key design decisions
- rollback_plan returns RollbackResult (the same result type produced by CheckpointService.selective_rollback) so the CLI can display rollback details consistently.
- Terminal states APPLIED and CANCELLED are disallowed for rollback to prevent inconsistent or invalid state transitions.
- checkpoint_service is optional in the PlanLifecycleService constructor; when omitted (None), a PlanError is raised to retain backward compatibility while signaling explicit dependency requirements.
- CLI UI remains powered by CheckpointService for metadata enrichment (e.g., confirmation prompts), but the actual rollback action is performed via PlanLifecycleService to ensure proper domain workflow and event emission.
- Technical implications
- All rollback logic now flows through the domain service layer (PlanLifecycleService) to preserve invariants and emit domain events, rather than allowing ad-hoc UI routes to bypass service validation.
- The UI can still retrieve checkpoint metadata for user confirmation, but the operation that modifies state uses the new rollback_plan pathway.
- Tests and behavior coverage were expanded via the new Behave feature and step implementations to validate state handling, events, and delegation.
- Affected modules/components
- src/cleveragents/infrastructure/events/types.py
- src/cleveragents/application/services/plan_lifecycle_service.py
- src/cleveragents/cli/commands/plan.py
- PlanLifecycleService module docstring
- features/plan_lifecycle_rollback.feature
- features/steps/plan_lifecycle_rollback_steps.py
ISSUES CLOSED: #3677
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
Route the 'agents actor add' CLI command through ActorRegistry.add() instead
of the legacy registry.upsert_actor() path. This ensures the original YAML
text, schema_version, and compiled_metadata are preserved in the database.
Changes:
- src/cleveragents/cli/commands/actor.py: Add _load_config_text() helper that
returns both raw text and parsed dict. Refactor add() to call registry.add()
with the raw yaml_text and update=update_existing flag when a registry is
available. The service fallback path (no registry) is unchanged.
- features/steps/actor_cli_steps.py: Update add command step definitions to
mock registry.add() instead of registry.upsert_actor(). Update 'the actor
add should pass the loaded config' assertion to verify registry.add() is
called with a non-empty yaml_text string.
- features/steps/actor_cli_yaml_steps.py: Update add command steps to mock
registry.add() instead of registry.upsert_actor().
- features/steps/actor_add_rich_output_steps.py: Update add command steps to
mock registry.add() instead of registry.upsert_actor().
- robot/helper_actor_add_rich_output.py: Update helper to mock registry.add()
instead of registry.upsert_actor().
- features/actor_add_yaml_first_path.feature: New Behave feature verifying
the YAML-first persistence path is used by actor add.
- features/steps/actor_add_yaml_first_path_steps.py: Step definitions for
the new YAML-first path feature.
- robot/actor_add_yaml_first_path.robot: New Robot integration tests verifying
yaml_text is preserved and upsert_actor is not called.
- robot/helper_actor_add_yaml_first_path.py: Helper script for Robot tests.
Fixes#3426
ISSUES CLOSED: #3426
Implements all four automatic checkpoint triggers defined in the specification
for the Execute phase of the plan lifecycle:
- before_tool_execute: Checkpoint created before any tool with writes=True runs
- after_tool_execute: Checkpoint created after a write tool completes successfully
- on_subplan_spawn: Checkpoint created immediately after a child plan is spawned
- on_error: Checkpoint created after any unrecoverable error in the Execute phase
Changes:
- tool/runner.py: Added optional CheckpointService and auto_checkpoint_triggers
parameters to ToolRunner. Checkpoint hooks fire around write-tool execution
when a CheckpointService is wired. Exported DEFAULT_AUTO_TRIGGERS as a public
constant (single source of truth). Made is_trigger_active() public so callers
can query the active trigger set without accessing private attributes.
- application/services/subplan_execution_service.py: Added optional
CheckpointService, auto_checkpoint_triggers, and parent_plan_id parameters.
on_subplan_spawn checkpoint fires in _execute_one_with_retry before the
first execution attempt. Now imports DEFAULT_AUTO_TRIGGERS from runner.py
(DRY fix).
- application/services/plan_executor.py: Added _is_auto_trigger_active() helper
and on_error checkpoint hooks in both _run_execute_with_stub() and
_run_execute_with_runtime() error paths. Delegates to
ToolRunner.is_trigger_active() instead of accessing private attributes
(module boundary fix).
- application/services/config_service.py: Registered new config key
core.checkpoints.auto_create_on (default: all four triggers enabled) with
env var CLEVERAGENTS_CHECKPOINT_AUTO_CREATE_ON.
- application/services/llm_actors.py: Replaced Any type for lifecycle_service
with PlanLifecycleProtocol (typed Protocol) and tool_runner with ToolRunner
type annotation. Eliminates Any usage for injected dependencies.
Tests:
- features/checkpoint_auto_triggers.feature: 15 Behave scenarios covering all
four triggers, disable-trigger behavior, no-checkpoint-service fallback, and
config key registration.
- features/steps/checkpoint_auto_triggers_tool_steps.py: Step definitions for
ToolRunner and config service scenarios (split from original 519-line file).
- features/steps/checkpoint_auto_triggers_executor_steps.py: Step definitions
for SubplanExecutionService and PlanExecutor scenarios (split from original).
Closes#3439
ISSUES CLOSED: #3439