Fix lint failures that were causing CI to fail (the primary blocker in
review cycles 3 and 4):
1. Fix missing 'when' import in cli_lifecycle_e2e_assertions_steps.py
- The @when decorators for plan status/plan list steps were using an
undefined name, causing F821 lint errors and test module load failures
2. Auto-fix unsorted imports (I001) across all 4 step definition files:
- cli_lifecycle_e2e_assertions_steps.py
- cli_lifecycle_e2e_plan_execute_steps.py
- cli_lifecycle_e2e_plan_use_steps.py
- cli_lifecycle_e2e_setup_steps.py
3. Remove unused 'cli' import from cli_lifecycle_e2e_setup_steps.py (F401)
Additional compliance items:
- Add CHANGELOG.md entry for cli_lifecycle_e2e feature tests (#9459)
- Add CONTRIBUTORS.md detail for HAL 9000's test contribution (#9459)
ISSUES CLOSED: #9459
---
Addresses PR review feedback cycles 3 and 4 from HAL9001.
Refactor cli_lifecycle_e2e tests to address all review feedback from cycles 1-2:
- Split the 752-line cli_lifecycle_e2e_steps.py into 4 modular step files (each under 500 lines):
. cli_lifecycle_e2e_setup_steps.py: context init, mock env, action setup (80 lines)
. cli_lifecycle_e2e_plan_use_steps.py: plan use step definitions (290 lines)
. cli_lifecycle_e2e_plan_execute_steps.py: plan execute/apply steps (168 lines)
. cli_lifecycle_e2e_assertions_steps.py: all assertions (286 lines)
- Fix tautological phase assertions: step definitions now verify actual CLI output
(context.last_output) instead of creating new Plan objects with hardcoded values
- Fix JSON envelope validation: validates all spec-required keys (command, status,
exit_code, data, timing, messages) instead of checking only status string presence
- Add missing plan list invocations: each plan list assertion is now preceded by
'And cli_lifecycle_e2e plan list' to run the plan list command
- Add missing plan status invocations: each plan status assertion is now preceded by
'And cli_lifecycle_e2e plan status with the created plan ID' to run status command
- Fix os.environ cleanup: mock LLM actors env var tracked and restored after scenario
- Updated feature file with 824→823 lines of Gherkin, adding When-steps for plan
status and plan list invocations in scenarios 1, 3, and 4
Closes#9459
Covers uncovered paths in acms_context.py:
- Empty view name rejection (context show error path)
- Clear without filters warning and auto-confirmation path
This improves code coverage by exercising the following functions:
- acms_context_show empty view validation branch (line 91-93)
- acms_context_clear no-filters warning with confirmation bypass (line 215-219)
Closes#9586
- Fix acms CLI command hierarchy: nested acms_context.app under main.py
'acms' Typer as 'context' sub-command to form canonical path
[H[2J[3J (was incorrectly at ).
- Warm/cold tier budget labels now say 'decisions' instead of fragments
for clarity (warm/cold use decision budgets, not token budgets).
- Simplify _remove_fragments: ContextTierService is already thread-safe
with RLock; manual lock detection/holding was unnecessary and fragile.
- Update CHANGELOG to clarify warm/cold tiers use decision budget limits.
Extends the budget utilization summary in `context show` to display
hot/warm/cold tier utilization percentages individually, satisfying
the spec requirement for a per-tier breakdown. Previously only hot
tier utilization was shown.
- Hot tier: tokens used vs. max_tokens_hot budget
- Warm tier: fragment count vs. max_decisions_warm budget
- Cold tier: fragment count vs. max_decisions_cold budget
Also updates Robot Framework helper to verify per-tier breakdown
is present in output, and updates CHANGELOG/CONTRIBUTORS.
ISSUES CLOSED: #9586
- Fix integration test failure: Context Show Validates Empty View Name
- typer.Exit is click.Exit (RuntimeError subclass), not SystemExit
- Robot helper now catches typer.Exit using exit_code attribute
- Helper path insertion now always places clone src at sys.path[0]
to prevent /app/src from shadowing the PR branch source
- Fix information disclosure: CleverAgentsError handler now logs
exception internally via _logger.exception() and shows generic
user-facing message instead of str(e)
- Fix budget utilization: use actual per-tier token counts instead
of hot_count * 100 (fragment count * arbitrary factor)
- Fix type safety: _remove_fragments now uses _TierServiceProtocol
instead of object, enabling proper static type checking
- Fix overly broad except: cancellation handled with early return
instead of catching typer.Exit(0) in the except block
- Add broad glob pattern warning when --path matches > 50 entries
- Remove duplicate HAL 9000 entry from CONTRIBUTORS.md
- Fix Behave steps to catch typer.Exit in addition to SystemExit
ISSUES CLOSED: #9586
The ACMS context CLI commands ('context show' / 'context clear') were fully
implemented in 'acms_context.py' with comprehensive tests, mocks, benchmarks,
and documentation — but the module was never imported or registered in
'cli/main.py'. This commit wires up the 'acms_context.app' Typer sub-app
so that 'agents acms context show' and 'agents acms context clear' are
actually accessible from the CLI.
Changes:
- Import acms_context in _register_subcommands()
- Register acms_context.app as the 'acms' sub-app on the main Typer app
- Add 'acms' to valid_cmds list in main() for fast-path validation
- Add 'acms context' entry to _print_basic_help() output
- Minor formatting cleanup applied by ruff
ISSUES CLOSED: #9586
Refs: #9675
- Rewrote production CLI to use real ContextTierService (get_scoped_view, get_all_fragments, evict_lru) instead of non-existent ACMSService
- Removed unused imports (Path, Panel, ScopedView) from production code
- Fixed all lint issues: trailing whitespace, import ordering, nested with statements
- Replaced typer.Abort() with typer.Exit(code=1) for error exits
- Added input validation for empty/whitespace view parameter
- Fixed error handling to use str(e) instead of e.message
- Added guards against negative budget values in _format_budget_utilization
- Added warning when clearing context with no filters (clear ALL)
- Removed module-level console side effect
- Moved mocks to features/mocks/acms_context_mocks.py per CONTRIBUTING.md
- Fixed test assertions to capture real CLI output (not placeholder)
- Fixed duplicate step definitions (AmbiguousStep errors)
- Fixed feature file step mismatch for tier count parameter
- Added Robot Framework integration tests in robot/acms_context_cli.robot
- Added performance benchmarks in benchmarks/acms_context_cli_bench.py
- Updated CHANGELOG.md with ACMS context CLI feature entry
- Updated CONTRIBUTORS.md with ACMS context CLI contribution
ISSUES CLOSED: #9586
Restore all five required Rich output panels to _print_lifecycle_plan():
- Plan Status panel: Processing State, Projects, Arguments, Automation Profile,
actors (Strategy/Execution/Estimation/Invariant), Execution Environment,
Created/Updated timestamps, Description, Definition of Done, DoD evaluation,
Invariants, resume metadata, multi-project scopes, error message
- Progress panel: Strategize/Execute/Apply step indicators
- Timing panel: Started, Elapsed, ETA (using estimation_result when available),
and all phase timestamps (Strategize Started/Completed, Execute Started/Completed,
Applied At)
- Execution Detail panel: Sandbox, Tool Calls (N/A), Files Modified (N/A),
Child Plans, Checkpoints
- Cost panel: Tokens Used, Cost So Far, Estimated Total Cost
- Footer: ✓ OK Status refreshed
Also fixes:
- tool_calls semantic bug: display N/A instead of total_tokens
- files_modified: display N/A (not available in cost_metadata)
- ETA calculation: use estimation_result.estimated_time_seconds or N/A
- In-function import: moved Plan as LifecyclePlan to top of file
- Import sorting: split aliased import per ruff isort rules
Adds BDD scenarios for all five panels in plan_lifecycle_cli_coverage.feature
with step definitions in plan_lifecycle_cli_coverage_steps.py.
Updates CHANGELOG.md with user-facing output changes.
ISSUES CLOSED: #9341
Implements the missing output panels for the 'agents plan status' command as specified in the product specification. The command now renders five panels:
1. Plan Status - Plan ID, Phase, State, Action, Project, Automation, Attempt
2. Progress - Strategize/Execute/Apply step progress with status indicators (✓, ⏳, •)
3. Timing - Started, Elapsed, ETA
4. Execution Detail - Sandbox strategy, Tool Calls, Files Modified, Child Plans, Checkpoints
5. Cost - Tokens Used, Cost So Far, Estimated
Also adds the '✓ OK Status refreshed' footer line as required by the specification.
ISSUES CLOSED: #9341
Extended features/mocks/test_uow_factory.py with:
- use_test_uow(context) function to attach test UoW to Behave context with automatic cleanup
- cleanup_test_uow(context) function for teardown
- Comprehensive docstrings and examples
Updated features/environment.py:
- Added cleanup_test_uow() call in after_scenario hook
ISSUES CLOSED: #9541
The artifacts() method routes JSON through format_output() which wraps
the payload in a spec-required envelope {"data": ..., "status": ...}.
The two @tdd_issue_4253 step assertions were checking parsed["key"]
directly, but the actual fields live at parsed["data"]["key"].
Update step_artifacts_json_validation and step_artifacts_json_apply_summary
to extract parsed["data"] before asserting on validation_summary and
apply_summary respectively.
ISSUES CLOSED: #9084
- Add changelog entry under [Unreleased]/Fixed for plan artifacts JSON completeness (#9084)
- Add contributors detail for validation_summary and apply_summary fix work.
ISSUES CLOSED: #9084
The _build_artifacts_dict function in plan_apply_service.py already includes
validation_summary and apply_summary in the artifacts output. These two test
scenarios were marked as @tdd_expected_fail but now pass with the current
implementation. Removing the tags to reflect the actual passing status.
Also added the missing unit-tests.yaml validation config file that the tests
require.
ISSUES CLOSED: #9084
The tdd_expected_fail listener (robot/tdd_expected_fail_listener.py and
features/environment.py) inverts test results: a test tagged
@tdd_expected_fail that PASSES is forced to FAIL with the message
"Bug appears to be fixed. Remove the tdd_expected_fail tag…".
After rebasing onto master, 22 scenarios across 11 files were triggering
that forced-failure path because the underlying bugs (#4199, #4201,
#4202, #4203, #4205, #4206, #4243, #4252, #4301, #4303, #4304) have
been fixed on master but the @tdd_expected_fail tags were never removed.
This commit removes the stale tag (keeping @tdd_issue and
@tdd_issue_<N> for traceability, per the master-side pattern in
robot/tdd_skill_add_regression.robot:9 "tag removed after bug fix").
Files touched (lines: where the stale tag was):
features/plan_cli_spec_alignment.feature (121, 128, 136)
robot/a2a_facade.robot (40)
robot/actor_cli_show.robot (13, 30)
robot/actor_configuration.robot (8)
robot/actor_context_export_import.robot (20, 71, 92)
robot/cli_extensions.robot (61)
robot/cli_formats.robot (14, 23, 30, 48, 57, 85)
robot/cli_lifecycle_e2e.robot (77, 87)
robot/config_project_scope.robot (37)
robot/config_resolution.robot (13)
robot/container_tool_exec.robot (137)
Verified locally: targeted unit_tests run on
features/plan_cli_spec_alignment.feature now passes 20/20 (was 17/20
with 3 scenarios forced-failed by the inversion).
ISSUES CLOSED: #3677
- 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
The prior commits added `timing.started` to a local envelope dict in
`prompt_plan_cmd`, but that dict was passed as the `data` argument to
`format_output()`, which builds its OWN envelope from `data`. Result:
the test-asserted `command="plan prompt"` ended up as an empty string
at the JSON root, and `timing.started` was buried in
`data.timing.started` instead of `timing.started`.
This commit:
- Extends `format_output` and `_build_envelope` with an optional
`started_at: datetime | None` parameter. When provided, the envelope's
`timing` dict includes a `started` ISO-8601 field alongside
`duration_ms`. Backward compatible: default `None` preserves the
existing timing shape for all current callers.
- Refactors `prompt_plan_cmd` to call
`format_output(prompt_data, fmt, command="plan prompt", ...,
started_at=started_at)` for the JSON and YAML formats so the envelope
keys are populated at the document root. Table/plain/color formats
retain the legacy envelope-wrapping behavior — the existing scenario
outline relies on the envelope being passed directly to the table
renderer.
- Removes 48 unrelated files accidentally committed to the repo root
by the original PR commit (`_issue_state.py`, `_pr_dep*.py`,
`_pr_labels.py`, `_pr_setup.py`, `coverage_boost_steps*.py`,
`cross_plan_correction_*.py`, `parse_*.py`, `search_*.py`,
`retry_policy_updated.py`, `run_behave_parallel.py`,
`acms_context_analysis_steps.py`, `aggregate_all.py`,
`check_issues.py`, `check_last_page.py`, `fix_timing.py`,
`helper_cross_plan_correction*.py`, `groom_prompt.txt`,
`label_result.txt`, `prompt_auto_rev_sup.txt`, `tmp/uat_worker_*`,
`tmp/update_issue_labels.sh`). These contained hardcoded API tokens
and broke `ruff format --check`. The exposed token
(`92224acff675c50c5958d1eaca9a688abd405e06`) should be rotated
separately.
- Adds CHANGELOG.md entry under [Unreleased] > Fixed.
ISSUES CLOSED: #9353
- Capture started_at timestamp using datetime.now(UTC) before service call
- Add timing.started field to JSON envelope with ISO 8601 format
- Update step definitions to verify timing.started is present and valid
- Remove @tdd_expected_fail tag from plan_prompt_command.feature scenario
- Remove unrelated files accidentally committed to repo root
- Move datetime import to top of plan_prompt_command_steps.py
- Add CHANGELOG.md entry under [Unreleased] > Fixed
ISSUES CLOSED: #9353
- Capture started_at timestamp using datetime.now(UTC) before service call
- Add timing.started field to JSON envelope with ISO 8601 format
- Update step definitions to verify timing.started is present and valid
- Remove @tdd_expected_fail tag from plan_prompt_command.feature scenario
Fixes#9353
The benchmark suite was trying to import CloudSandboxStrategy from
the cloud handler, but it's only defined in cloud_aws. Update the
benchmark to import directly from cloud_aws where the class is defined.
This fixes the CI / benchmark-publish ImportError.
The benchmark suite was trying to import CloudSandboxStrategy from
the cloud handler, but it's only defined in cloud_aws. Update the
benchmark to import directly from cloud_aws where the class is defined.
This fixes the CI / benchmark-publish ImportError.
The benchmark suite imports CloudSandboxStrategy from the cloud handler,
but it was only defined in cloud_aws.py and not re-exported. Add the
import, export list, and ensure proper import ordering.
Fixes CI / benchmark-publish import error.
The benchmark suite imports CloudSandboxStrategy from the cloud handler,
but it was only defined in cloud_aws.py and not re-exported. Add the
import and export so benchmarks can import it from the canonical location.
Fixes CI / benchmark-publish import error.
CloudSandboxStrategy is defined in cloud_aws.py, not cloud.py. The
benchmark was importing it from the wrong module, causing an ImportError
during benchmark discovery.
The benchmark-regression CI job's "Compute base commit" step used
forgejo.base_ref which is empty for push events, causing
`git merge-base HEAD "origin/"` to fail with exit 128. Add a guard
that falls back to HEAD~1 when base_ref is unset.
Fix the failing unit tests in agent_task_memory_leak_fix.feature by
replacing the broken event loop management with a persistent background
asyncio event loop running in a daemon thread. The original implementation
called asyncio.create_task() from synchronous Behave step code, which
requires a running event loop — causing RuntimeError: no running event loop.
The fix introduces a _BackgroundLoop class that keeps a dedicated asyncio
event loop alive in a background thread. All agent instantiation and
message sending now happens via asyncio.run_coroutine_threadsafe(), ensuring
the event loop is always running when asyncio.create_task() is called.
Also adds the missing step definition for 'I send {count:d} messages to
the agent' (without 'in rapid succession') to match the feature file.
Updates CONTRIBUTORS.md with the agent task memory leak fix contribution.
ISSUES CLOSED: #9044
This fix addresses issue #9044 by adding a done callback to each asyncio.Task
created in the Agent._setup_processing_pipeline method. The callback removes
the task from the _tasks set upon completion, preventing unbounded memory
growth in long-lived agent instances.
The fix uses task.add_done_callback(self._tasks.discard) to ensure that
completed tasks are promptly removed from the set, allowing them to be
garbage collected. Using set.discard is safe as it never raises ValueError
on double-removal.
ISSUES CLOSED: #9044