Commit Graph

464 Commits

Author SHA1 Message Date
freemo 3556481681 feat(resource): implement AWS SDK integration for CloudResourceHandler
Implements real AWS SDK integration for CloudResourceHandler using boto3
as an optional dependency. Key changes:

- Add boto3/botocore as optional [aws] dependency in pyproject.toml
- Implement CloudResourceHandler.resolve() for AWS: builds boto3 session,
  verifies credentials via STS get_caller_identity for account-level types,
  and returns a BoundResource with the resource ARN as sandbox_path
- Implement discover_aws_resources() to enumerate VPCs, subnets, instances,
  S3 buckets, IAM roles, RDS instances, ECS clusters, Lambda functions,
  and EKS clusters via the AWS API
- Implement CloudResourceHandler.discover_children() for AWS resource types
  using the new discovery function
- Implement CloudSandboxStrategy.create/commit/rollback for AWS using a
  tag-based isolation strategy (CleverAgents:PlanId tag)
- GCP and Azure providers still raise NotImplementedError (pending)
- boto3 is optional: handler raises ImportError with helpful install message
  when boto3 is not installed
- Credentials are never logged (existing redaction infrastructure preserved)
- Update cloud_resources.feature to reflect new AWS behavior
- Add comprehensive cloud_aws_sdk.feature with 47 BDD scenarios covering
  all new code paths with mocked boto3

Closes #1021
2026-04-02 08:42:46 +00:00
brent.edwards 0ae733d01f feat(tui): implement help panel (F1) with context-sensitive help
Add a dedicated TUI help overlay toggled by F1 and resolve its content from the current prompt mode for main-screen, slash-command, reference, and shell contexts.

Extend Behave and Robot coverage for the new help-panel widget, app wiring, context switching, and toggle behavior.

ISSUES CLOSED: #1013
2026-04-01 21:51:14 +00:00
hamza.khyari 18b9d61e35 feat(resource): implement ResourceHandler content_hash method
Add content_hash(resource, *, algorithm='sha256') -> str to the
ResourceHandler protocol and all handler implementations:

- Protocol: new content_hash method on ResourceHandler (protocol.py)
- BaseResourceHandler: default impl hashes file content or directory
  entry names; returns EMPTY_CONTENT_HASH sentinel for missing
  resources
- GitCheckoutHandler: hashes git rev-parse HEAD through the requested
  algorithm for consistent digest format
- FsDirectoryHandler: recursive walk hashing sorted relative paths
  and file contents (content-only, ignores metadata)
- DevcontainerHandler: hashes devcontainer.json config file
- DatabaseResourceHandler: hashes connection string; for SQLite
  file-based DBs, hashes the database file content
- _DefaultHandler: delegates to BaseResourceHandler

EMPTY_CONTENT_HASH sentinel is the SHA-256 of empty input
(e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855).

Hash algorithm is configurable via the algorithm parameter (default
sha256, accepts any hashlib.new()-compatible name).

Behave tests (10 scenarios): sentinel for missing/nonexistent,
determinism, different content produces different hash, fs-directory
recursive hash with change detection, git-checkout hash, configurable
algorithm (sha256 vs sha512), protocol compliance for all 4 handlers.

ISSUES CLOSED: #837
2026-04-01 13:02:24 +00:00
brent.edwards dc1359fc07 feat(tui): enumerate full slash command set (60+ commands, 14 groups)
Add a specification-aligned slash command catalog and wire it into TUI overlay initialization so command discovery reflects the complete command surface. Cover command and group cardinality plus representative command presence in Behave tests.

ISSUES CLOSED: #1002
2026-04-01 09:23:21 +00:00
aditya 137d040c4d feat(acms): implement DepthReductionCompressor for skeleton compression
Add a production skeleton compressor that re-renders inherited fragments to overview depths via the UKO detail-level map chain, fits the result within the configured skeleton budget, and wires the pipeline default to the new compressor.

Address prior review feedback by extracting the render visitors into a dedicated module, restoring projected metadata to native runtime types, constraining builtin component resolution with an allowlist, and keeping child-context inheritance compatible with CRP context fragments for the Robot integration path.

Reproduced the Forgejo lint job in a clean python:3.13-slim container with the CI commands All checks passed! and 1740 files already formatted; both passed, so the earlier lint failure appears to have been transient runner behavior rather than a source-level defect.

ISSUES CLOSED: #919
2026-04-01 06:16:41 +00:00
brent.edwards 34dcf5226f feat(autonomy): automation profile resolution precedence correct (#1196)
## Summary
- Fix automation profile resolution precedence at plan-creation time to follow plan > action > project > global.
- Narrow exception handling in `_resolve_plan_profile_ref` to catch only `(KeyError, ValueError, OSError)` with warning-level structured logging, replacing bare `except Exception:` per CONTRIBUTING.md §Exception Propagation.
- Fix TDD feature file tag/title inconsistency: align to `@tdd_bug @tdd_bug_1076` / "TDD Bug #1076" referencing the original bug ticket.
- Add BDD scenarios for plan-level profile override and config service error fallback.
- Harden lifecycle and Robot/Behave test paths for auto-progression and parallel CI stability (including pre-seeded DB initialization for `tdd_plan_explain_plan_id` helper).
- Update Robot helper timing/session patterns to reduce false negatives in long-running integration/E2E validations.

## Approach
The automation profile resolution is implemented in `PlanLifecycleService._resolve_plan_profile_ref`, which evaluates the four-level precedence chain (plan > action > project > global) at `use_action()` time. The resolved profile is stored as an `AutomationProfileRef` on the Plan with provenance tracking. The `ConfigService` is used for project-scoped and global config lookups, with narrowed exception handling that logs warnings on failure and falls back to the settings default.

## Validation
- `nox -s lint` 
- `nox -s typecheck` 
- `nox -s unit_tests` 
- `nox -s coverage_report`  (97%)

## Review Fix Round (Review #2963)
1. **Bare `except Exception:` → narrowed to `(KeyError, ValueError, OSError)` with warning logging** — addresses CONTRIBUTING.md §Exception Propagation violation
2. **Tag inconsistency fixed** — `@tdd_bug @tdd_bug_1076` with title "TDD Bug #1076"
3. **Added 2 new BDD scenarios** — plan-level override and config error fallback
4. **Rebased onto latest master** (532ea100)

Closes #854

Reviewed-on: cleveragents/cleveragents-core#1196
Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com>
2026-04-01 04:27:09 +00:00
brent.edwards a8d91b94a7 feat(autonomy): guard enforcement works (denylist, budget caps, tool call limits) (#1204)
## Summary
- Enforced autonomy guard behavior for denylist/allowlist checks, budget caps, tool-call limits, and write/apply approval gates.
- Added scope-aware guard evaluation (plan vs subplan) using `GuardScope(StrEnum)` for type-safe scope handling.
- Extracted remediation guidance strings as module-level constants (`REMEDIATION_DENYLIST`, `REMEDIATION_ALLOWLIST`, etc.) for consistency and testability.
- Added Behave coverage to validate guard remediation messaging and subplan-scoped tool-call limit behavior.

## Review Fix Round (v2)
Addressed review #2910 by @freemo:
1. **Reverted `resource_dag.robot`** — Unrelated Robot SQLite/cycle-detection changes removed from this commit; will be submitted as a separate PR.
2. **`scope` parameter → `GuardScope` enum** — Created `GuardScope(StrEnum)` in `automation_guard.py` with `PLAN` and `SUBPLAN` members. Updated `check_guard()` signature and all call sites.
3. **Removed redundant `scope_label`** — Now uses `scope.value` directly.
4. **Extracted remediation constants** — Guidance strings moved to module-level constants in `automation_guard.py`.

## Validation
- `nox -s lint` — passed
- `nox -s typecheck` — passed (0 errors, 0 warnings)
- `nox -s unit_tests` — passed (508 features, 12989 scenarios, 0 failures)
- `nox -s coverage_report` — passed (97% coverage)
- Rebased onto latest `master` (532ea100)

Closes #853

Reviewed-on: cleveragents/cleveragents-core#1204
Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com>
2026-04-01 01:46:37 +00:00
brent.edwards 01b6eb1804 feat(autonomy): parallel execution scales to 10+ concurrent subplans (#1201)
## Summary

Add M6 parallel-scaling coverage for 10+ concurrent subplans:

- **15-subplan parallel scenario** with explicit peak-concurrency bound checks (`max_parallel=10`) and thread-safe concurrency tracking via `_build_executor()`.
- **Deep hierarchical decomposition** coverage (4+ levels) with adjusted leaf condition that only stops early when hitting `max_depth` or when the workset is trivially small (`min_files_per_subplan`).
- **Non-progress guard** in `_build_hierarchy` to prevent pathological recursion when clustering cannot meaningfully split the file set.
- **Small-project regression test** (< 50 files) verifying decomposition depth does not increase unexpectedly with the relaxed leaf condition.
- **ASV benchmark** for 15-subplan parallel execution with `max_parallel=10` to track scaling behavior.

### Removed from this PR

The `_build_hierarchy` child-linkage correctness fix (returning `node_id` from recursive calls instead of using `nodes[-1].node_id`) has been **removed** per review feedback — it is a separate bug fix and will be submitted as an independent issue/PR per CONTRIBUTING.md §Atomic Commits.

## Approach

- **Concurrency tracking:** The `_build_executor()` closure in step definitions detects `context.concurrency_counter` / `context.concurrency_lock` and performs thread-safe peak tracking in a try/finally block.
- **Leaf condition:** Replaced the `max_files_per_subplan` / `max_tokens_per_subplan` leaf check with a `min_files_per_subplan` check to allow deeper decomposition for large projects. Added a non-progress guard so clustering that cannot split the file set terminates immediately rather than recursing to `max_depth`.
- **Deterministic IDs:** `_ids_for_count()` preserves legacy fixed IDs for the first 5 subplans and generates additional deterministic IDs for scale scenarios.

## Validation

### Passing
- `nox -s lint` — all checks passed
- `nox -s typecheck` — 0 errors, 0 warnings
- `nox -s unit_tests` — 12,988 scenarios passed, 0 failed
- `nox -s coverage_report` — 97% (passes `--fail-under=97`)

Closes #855

Reviewed-on: cleveragents/cleveragents-core#1201
Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
2026-03-31 23:57:39 +00:00
hamza.khyari 5e96b4bf80 feat(resource): implement 6-level execution environment precedence chain
Implement the spec's 6-level execution environment precedence chain
(spec lines 19324-19386):

1. Plan override (priority=override) — always wins
2. Project override (priority=override) — wins over devcontainer
3. Nearest-ancestor devcontainer — auto-discovered
4. Plan fallback (priority=fallback) — defers to devcontainer
5. Project fallback (priority=fallback) — defers to closer scopes
6. Host default — final fallback

- New resolve_with_precedence() API on ExecutionEnvironmentResolver
- Added execution_env_priority field to ContextConfig (project model)
- has_devcontainer() helper for devcontainer-instance detection
- Legacy 4-level resolve() preserved for backward compatibility
- _parse_priority() defaults missing priority to FALLBACK
- 13 new Behave scenarios testing all 6 levels + edge cases
- Updated CHANGELOG

ISSUES CLOSED: #877
2026-03-31 16:01:58 +00:00
hamza.khyari cd9cb9e889 fix(cli): honour project-level execution-env-priority in resolution
Thread plan_env and project_env through the tool execution chain so
the ExecutionEnvironmentResolver receives project-level execution
environment values stored in ContextConfig.execution_environment.

The resolver's precedence logic (tool > plan > project > default) was
already correct, but callers never passed project_env — tools always
fell through to the global HOST default, ignoring project-level
overrides entirely.

Changes:
- PlanExecutionContext: add plan_env and project_env fields + properties
- ToolCallRouter: accept plan_env/project_env in constructor, pass to
  runner.execute() in route() and route_streaming()
- ToolCallingRuntime: accept plan_env/project_env, pass to
  runner.execute() in the direct-runner fallback path
- Update monkey-patched execute stubs in tool_router_steps.py to accept
  explicit plan_env/project_env keyword arguments
- Add 4 BDD scenarios proving plan_env/project_env reach the resolver
  via ToolCallRouter (project-only, plan-only, both, neither)

Dependency: requires PR #1135 to be merged first (adds CLI flag and
persistence for project-level execution-env-priority).

Closes #1080

ISSUES CLOSED: #1080
2026-03-31 11:31:00 +00:00
brent.edwards 84b0c10dbf fix(cli): plan correct active-plan resolution in isolated environments (#1184)
## Summary
- tighten `plan correct` active-plan fallback so it only runs for isolated `CLEVERAGENTS_HOME` mismatch cases and never when explicit DB env overrides are configured
- narrow fallback exception handling in `_resolve_active_plan_id()` to expected DB/path/service failures; unexpected errors now surface instead of being silently swallowed
- add BDD regression coverage for both safeguards in `features/consolidated_plan_misc.feature` + `features/steps/plan_cli_legacy_r2_steps.py`

## Validation
- `nox -e lint`: PASS
- `nox -e typecheck`: PASS
- `nox -e unit_tests`: PASS
- `nox -e integration_tests`: FAIL in current branch baseline (29 failing Robot integration tests in this environment)
- `nox -e e2e_tests`: FAIL in current branch baseline (45 failing E2E tests in this environment)
- `nox -e coverage_report`: PASS (97%)

Closes #1025

Reviewed-on: cleveragents/cleveragents-core#1184
Reviewed-by: Jeffrey Phillips Freeman <jeffrey.freeman@cleverthis.com>
Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
2026-03-31 01:49:16 +00:00
CoreRasurae a4a6b061a6 fix(db): align v3_plans schema with specification DDL
Aligned v3_plans table with specification DDL:

1. Added effective_profile_snapshot column (TEXT NOT NULL) for
   storing frozen JSON snapshot of automation profile at plan
   creation time.  Added Pydantic field_validator ensuring the
   value is well-formed JSON.  Validator catches RecursionError
   for deeply nested JSON, consistent with automation_profile
   deserialization hardening.  Validator error message uses
   length-only to avoid potential information disclosure.
   Documented that the default "{}" exists for backward
   compatibility; new plans should explicitly set the snapshot.

2. Made root_plan_id NOT NULL — root plans self-reference their
   own plan_id, child plans reference the root ancestor.  Added
   explicit ondelete="RESTRICT" FK policy for consistency with
   other FKs in the model.  Documented known FK policy drift
   between ORM model (RESTRICT) and migrated databases (retained
   SET NULL) in the migration; data integrity is preserved by
   the NOT NULL constraint regardless.  Moved root_plan_id
   self-reference resolution into a PlanIdentity model_validator
   so the domain model is consistent with the DB NOT NULL
   constraint before and after persistence (previously the
   resolution only happened in from_domain(), creating an
   asymmetry where root_plan_id was None in-memory but non-null
   after round-tripping through the database).

3. Made automation_profile NOT NULL with default "balanced".

4. Documented intentional deviation: phase default is "action"
   (code) vs "strategize" (spec) because the Action phase was
   added as a pre-Strategize setup step.

5. Created Alembic migration with backfill logic for existing
   rows.  Root-ancestor backfill uses level-by-level propagation
   with a parent-readiness guard to correctly resolve plans at
   arbitrary hierarchy depth (3+ levels).  Added safety bound
   (max 100 iterations) with logged error on exhaustion to guard
   against cycles in parent_plan_id.  Merged batch_alter_table
   operations to avoid redundant full-table copies in SQLite
   batch mode.  Migration backfill also handles empty-string
   automation_profile values.  Documented downgrade limitation
   (backfill is not reversible).  Orphan-row fallback now logs
   affected row count at WARNING level.  Migration cycle-detection
   now logs affected plan_id values before the orphan fallback
   overwrites them.  All migration SQL uses sa.text() for
   consistency with SQLAlchemy best practices.

6. Hardened automation_profile deserialization in to_domain() to
   catch ValueError (invalid StrEnum provenance), Pydantic
   ValidationError, and RecursionError (deeply nested JSON) in
   addition to JSONDecodeError and KeyError, preventing
   unreadable plans from corrupted DB rows.  Applied the same
   defensive deserialization pattern to effective_profile_snapshot
   in to_domain(): corrupted JSON falls back to '{}' with a
   WARNING log instead of crashing the read path.  Added TypeError
   to the effective_profile_snapshot exception list in to_domain()
   for consistency with the Pydantic validator.  Logging of
   unparseable values uses length only to avoid potential
   information disclosure.

7. Used explicit None check (is not None) instead of truthiness
   for root_plan_id resolution in from_domain(), for
   effective_profile_snapshot in to_domain(), and in
   _serialize_automation_profile() for consistency.

8. Documented intentional column naming conventions vs spec DDL
   (e.g. automation_profile vs automation_profile_name, *_actor
   vs *_actor_name, processing_state vs state, v3_plans vs
   plans).  Documented the semantic difference: automation_profile
   stores either a bare name or structured JSON with provenance,
   whereas the spec automation_profile_name stores a plain name.

9. Fixed benchmark plan constructors
   (plan_phase_migration_bench.py) that were missing the now-
   required root_plan_id and effective_profile_snapshot fields.

10. Replaced defensive getattr() with direct attribute access for
    effective_profile_snapshot in from_domain() and update(),
    since the field is now defined on the Plan domain model.

11. Fixed Any type annotation in test helper _make_plan() to use
    AutomationProfileRef | None for proper type safety.

12. Added BDD scenarios for PlanIdentity self-reference
    resolution, NULL effective_profile_snapshot constraint
    enforcement, valid-JSON-missing-profile_name-key
    deserialization, invalid-JSON and empty-string snapshot
    rejection by Pydantic validator, and corrupted
    effective_profile_snapshot DB fallback in to_domain().

13. Extracted default automation profile name to a module-level
    constant (DEFAULT_AUTOMATION_PROFILE) to reduce sentinel
    duplication across models.py and repositories.py.

14. Centralised automation-profile serialisation into
    LifecyclePlanModel._serialize_automation_profile() to
    eliminate duplication between from_domain() and
    LifecyclePlanRepository.update().

15. Fixed to_domain() root_plan_id type cast from str | None
    to str, reflecting the NOT NULL column constraint.

16. Added PlanIdentity model_validator that resolves None
    root_plan_id to plan_id at domain construction time, ensuring
    the domain model honours the spec DDL NOT NULL constraint
    regardless of persistence state.  Simplified from_domain()
    root resolution accordingly.

ISSUES CLOSED: #921
2026-03-30 23:40:36 +01:00
freemo 7b3fcaf466 feat(config): implement three-scope config resolution with local config file support
Implement three-scope configuration resolution (local > project > global)
with deep merge. Add ConfigScope enum, project-root discovery, and
config.local.toml file support.

Changes:
- Add LOCAL to ConfigLevel enum and new ConfigScope enum (GLOBAL, PROJECT, LOCAL)
- Implement discover_project_root() walking up from CWD for cleveragents.toml/.cleveragents
- Implement config.local.toml file loading in ConfigService
- Implement three-scope deep merge via _deep_merge() helper
- Add read_project_config(), read_local_config(), read_merged_config() methods
- Add write_scoped_config() for writing to any scope file
- Update set_value() to accept optional scope parameter
- Update resolve() with six-level precedence chain (CLI > env > local > project > global > default)
- Update CLI config set with --scope flag (global/project/local)
- Add config.local.toml to .gitignore and DEFAULT_IGNORE_PATTERNS
- Add Behave BDD scenarios for three-scope resolution, deep merge, and project-root discovery

ISSUES CLOSED: #937
2026-03-30 21:48:01 +00:00
freemo 38e05ac45a feat(correction): wire checkpoint rollback into correction service revert flow
Implement the full correction-checkpoint rollback pipeline:

- Workspace snapshots: CheckpointService.create_workspace_snapshot()
  creates diff-based checkpoints before decision execution, storing
  only changed file paths in metadata.extra["diff_paths"]

- CorrectionService.revert_decisions(): new high-level entry point
  that creates a correction, computes impact, invokes checkpoint
  rollback, and archives artifacts in a single call

- Physical artifact archival: CheckpointService.archive_artifacts()
  moves files to .cleveragents/archived_artifacts/ instead of just
  flagging metadata. CorrectionService._archive_decision_artifacts()
  delegates to this during revert execution

- Selective rollback: CheckpointService.selective_rollback() wraps
  rollback_to_checkpoint with atomic semantics — captures HEAD before
  rollback and recovers on failure

- Diff-based storage: _compute_diff_snapshot() computes changed paths
  between checkpoints via git diff; snapshots store diff manifest and
  SHA-256 hash in metadata

- CLI: plan rollback now accepts --to-checkpoint <id> in addition to
  the positional checkpoint_id argument; uses selective_rollback for
  atomic execution

- DI wiring: Container now injects checkpoint_service into
  CorrectionService; CLI correct command uses container-provided
  service instead of ad-hoc instance (fixes bug #986)

- Checkpoint model: pre_decision added to allowed checkpoint_type
  values

- TDD: Removed @tdd_expected_fail from wiring test feature since the
  DI bug is now fixed

ISSUES CLOSED: #943
2026-03-30 17:06:49 -04:00
freemo afe6b849fe feat(cli): implement missing output element types and handles
Implement LiveMaterializationStrategy as the third materialization
strategy type (alongside sequential_buffer and accumulate) per spec
§26456-26492.  The live strategy renders element updates in-place at
~15 fps by tracking a dirty-element set and coalescing updates into
frame redraws.

RichMaterializer now extends LiveMaterializationStrategy instead of
_BaseBufferStrategy, enabling supports_incremental_updates=True.
Element rendering still delegates to the colour renderer for visual
formatting, maintaining backward compatibility.

Changes:
- Add _LiveMaterializationStrategy class with frame-rate throttling,
  dirty-element tracking, and per-frame composition in declaration
  order
- Update RichMaterializer to extend _LiveMaterializationStrategy
- Export LiveMaterializationStrategy from materializers.py and
  __init__.py
- Update SD-2 and SD-7 documentation in __init__.py
- Add vulture whitelist entry for LiveMaterializationStrategy
- Add 5 Behave scenarios covering live strategy rendering, incremental
  update support, dirty-element coalescing, all-element-type rendering,
  and frame rate validation
- Add step definitions for the new scenarios

ISSUES CLOSED: #903
2026-03-30 19:52:51 +00:00
freemo 297823c291 feat(cli): add actor context remove, export, and import commands
Implement three new actor context subcommands as specified in the v3
CLI specification:

- `agents actor context remove [--yes|-y] (--all|-a|<NAME>)`:
  Remove a named context or all contexts with interactive confirmation.
  Supports --all for bulk removal and --yes to skip prompts.

- `agents actor context export (--output|-o) <FILE> <NAME>`:
  Export a named context to JSON or YAML (detected from file extension).
  Includes sha256 integrity checksum in output metadata.

- `agents actor context import [--update] (--input|-i) <FILE> [<NAME>]`:
  Import a context from JSON or YAML. Name is inferred from file metadata
  if omitted. Refuses to overwrite existing contexts unless --update is
  specified.

All commands:
- Support 6 output formats (json, yaml, plain, table, rich, color) via
  the shared format_output framework
- Use ContextManager for persistence (messages, metadata, state,
  global_context)
- Follow existing error handling patterns (typer.Exit for user errors)
- Are wired as `agents actor context` subcommand group via add_typer

New files:
- src/cleveragents/cli/commands/actor_context.py (command module)
- features/actor_context_cmds.feature (Behave BDD scenarios)
- features/steps/actor_context_cmds_steps.py (BDD step definitions)
- robot/actor_context_export_import.robot (Robot integration test)

ISSUES CLOSED: #869
2026-03-30 19:35:23 +00:00
freemo a9465c4865 fix(cli): derive actor name from config file instead of positional argument
Remove the <NAME> positional argument from `actor add` command. The actor
name is now derived from the `name` field inside the config YAML file, per
the specification: `agents actor add (--config|-c) <FILE> [--update]`.
Updated all BDD and integration tests to pass name via config file.

ISSUES CLOSED: #914
2026-03-30 18:27:59 +00:00
freemo 1c1f477208 fix(cli): align output envelope with specification structure
Align the structured output envelope (JSON/YAML modes) with the specification:
add `status` field, rename `elements` to `data`, rename `metadata` to
`messages`, and fix the fallback chain to include the table step
(rich→table→color→plain).

ISSUES CLOSED: #884
2026-03-30 17:48:48 +00:00
freemo 4eddbcf489 fix(cli): add --depth-gradient flag to project context set
Add the missing --depth-gradient repeatable flag to `project context set`
per specification. The flag accepts HOP:INT_OR_NAME format to control
context detail depth degradation with graph distance. Parsing validates
hop as integer and value as integer or recognized detail level name.

ISSUES CLOSED: #889
2026-03-30 17:12:20 +00:00
hamza.khyari 49015c6bee fix(cli): implement --execution-env-priority on project context set
Add --execution-env-priority flag to 'project context set' command,
enabling project-level execution environment priority per spec WF17.

- Add execution_env_priority field to ContextConfig domain model
- Validate flag value against ExecutionEnvPriority enum (fallback/override)
- Persist in context_policy_json, preserving existing execution_environment
- Display in 'project context show' Execution Environment section
- Merge with existing blob to avoid overwriting previously set fields

Tests: 9 Behave scenarios, 26 steps.

ISSUES CLOSED: #1079
2026-03-30 14:33:28 +00:00
hamza.khyari 2370e19da8 feat(resource): add container infrastructure resource types
Add 7 container infrastructure resource types per ADR-039:
container-runtime, container-image, container-mount, container-exec-env,
container-port, container-volume, container-network.

- YAML configs under examples/resource-types/
- Bootstrap registration via _resource_registry_container.py
- Updated container-instance parent_types (container-runtime, container-image)
  and child_types (container-mount, container-exec-env, container-port)
- container-runtime is top-level with auto-discovery rules (scan_depth: 1)
- container-mount/exec-env/port inherit snapshot sandbox from instance
- container-volume is user-addable with snapshot sandbox
- container-network is read-only, no sandbox
- Handler references are forward declarations (ADR-039)
- 33 Behave BDD scenarios, 4 Robot integration tests
- Updated BUILTIN_NAMES, service docstring, CHANGELOG

ISSUES CLOSED: #831
2026-03-30 13:44:54 +00:00
aditya eff446f5e8 feat(acms): integrate FAISS into ACMS vector backend protocol
Add FAISS-backed ACMS read and write adapters on top of the shared VectorStoreService, wire them through the DI container, and cover indexing, scoped search, removal, and benchmark behavior with Behave and ASV.

Address review feedback by keeping the commit scoped to the FAISS backend work and replacing the loose vector-store cache typing with explicit FAISS store protocols instead of Any.

ISSUES CLOSED: #871
2026-03-30 13:19:08 +00:00
CoreRasurae 007af498b8 refactor(autonomy): rename automation profile task flags to spec names
Renamed all 11 task-type confidence threshold fields in AutomationProfile
from phase-transition semantics to spec-defined task-type semantics.
Updated all 8 built-in profiles, CLI formatting, YAML schema, services,
and all Behave/Robot tests referencing the old field names.

Post-review fixes:
- Fixed 24 stale old field names in M6 fixture files
  (automation_profiles.json, autonomy_guardrails.json)
- Added model_validator(mode='before') to detect legacy field names
  and raise actionable ValueError with rename mapping
- Added semantic bridge comments in PlanLifecycleService mapping
  task-type thresholds to phase-transition gates
- Added threshold_field to structured log messages for observability
- Restored categorised CLI automation-profile show output to match
  spec (Phase Transitions / Decision Automation / Self-Repair /
  Execution Controls) instead of flat list
- Added missing access_network field to spec show output examples
  (Rich, Plain, JSON, YAML variants)
- Aligned ADR-017 profile fields table to all 11 fields with
  descriptions matching spec Automatable Tasks table
- Aligned automation_profiles.md threshold descriptions with spec
- Added spec section references in phase_reversion.md, error_recovery.md,
  and plan_execute.md for field naming context
- Extended repository roundtrip test to assert all 11 threshold fields
- Fixed benchmark _make_profile() passing safety fields as top-level
  kwargs instead of via SafetyProfile sub-model (incompatible with
  extra="forbid")
- Aligned CLI JSON/YAML output structure for automation-profile show
  with the specification grouped format (phase_transitions,
  decision_automation, self_repair, execution_controls)
- Moved safety boolean fields into the Execution Controls section
  of Rich output per spec examples
- Reverted auto profile description to "Fully automatic except apply"
  per specification (line 16703, line 28406)
- Improved bridge comments in test steps with semantic context for
  threshold-to-gate mappings

ISSUES CLOSED: #902
2026-03-30 13:18:07 +01:00
hamza.khyari c97ec273a9 feat(lsp): add missing LspServerConfig model fields
Add specification-required fields to LspServerConfig:

- description: str (max_length=1000, default='')
- transport: LspTransport enum (stdio/tcp/pipe, default=stdio)
- initialization: dict[str, Any] (LSP initializationOptions, default={})
- workspace_settings: dict[str, Any] (workspace/didChangeConfiguration, default={})

All fields have defaults for backward compatibility with existing
configs. LspTransport enum exported from lsp package.

Tests: 17 Behave scenarios, 5 Robot integration tests.
Existing LSP tests: 250/250 pass unchanged.

ISSUES CLOSED: #835
2026-03-30 11:51:27 +00:00
freemo 9cace15d6e test(e2e): workflow example 18 — container with remote repo clone (trusted profile)
E2E test for Specification Workflow Example 18: Container with Remote
Repo Clone using the trusted automation profile.  Exercises the full
container-instance workflow per the spec:

- Registers a container-instance resource with --clone-into flag (new
  CLI flag added to resource add for REPO_URL:CONTAINER_PATH).
- Two-step project creation: project create + project link-resource.
- Dynamic LLM actor selection (Anthropic/OpenAI) matching available
  API keys.
- Plan use with --execution-environment container,
  --execution-env-priority fallback, and --automation-profile trusted.
- Full plan lifecycle: strategize, execute, diff, apply, status.
- Positive assertions: non-empty output, plan ID presence, terminal
  state verification, container/clone evidence logging.
- Negative assertions: Traceback and INTERNAL error checks.
- ULID regex uses Crockford base32 character set.
- Unique suffix for resource/project names (parallel CI safe).
- Skip If No LLM Keys guard for graceful degradation.
- CHANGELOG.md updated.

ISSUES CLOSED: #764
2026-03-30 19:17:41 +08:00
aditya 4e64544aae feat(acms): projects with 10,000+ files index without timeout
Add explicit indexing timeout bounds and propagate timeout_seconds through the repo indexing service, utility walker, and CLI entrypoint so large repositories fail predictably instead of hanging.

Add 10K-scale Behave/Robot verification and a dedicated benchmark path to validate that large-project indexing completes, persists status correctly, and remains measurable for regressions.

Address review feedback by splitting oversized Behave and Robot support files into focused modules, trimming repo_indexing_service.py back under the 500-line limit, and rebasing the branch onto current master.

ISSUES CLOSED: #851

# Conflicts:
#	CHANGELOG.md
#	robot/helper_m5_e2e_verification.py
2026-03-30 10:18:19 +00:00
aditya f0442e835d feat(acms): plan execution leverages ACMS context for LLM calls
Integrate ACMS execute-phase context assembly into LLMExecuteActor and inject assembled context into execute prompts with resilient fallback when assembly fails or returns empty output.

Wire the plan CLI executor to use an ACMS-backed execute context assembler, add Behave coverage for context injection/fallback/empty context, and extend Robot M5 verification helpers to assert execute-phase ACMS context usage.

ISSUES CLOSED: #850
2026-03-30 09:21:51 +00:00
aditya ef8f9640fa feat(acms): context analysis produces meaningful summaries
Add phase-wise context analysis summaries for strategize, execute, and apply, and integrate them into `agents project context inspect` and `agents project context simulate` with per-phase resource counts, size and token utilization, and narrowing diagnostics.\n\nAdd Behave and Robot coverage for empty, single-resource, multi-resource, and budget-constrained context analysis, and tighten the M5 smoke coverage after review feedback with exact summary assertions and a missing-path regression scenario for `agents context add`.\n\nISSUES CLOSED: #849
2026-03-30 08:42:48 +00:00
Luis Mendes 7f078f75a5 fix(sandbox): make commit_all atomic per specification
Changed SandboxManager.commit_all() from partial-commit semantics to
all-or-nothing atomic operation per specification requirement. On
partial failure, already-committed sandboxes are rolled back. Error
reporting indicates which sandbox failed and what was rolled back.
Added Behave scenarios verifying atomicity guarantee.

Hardened atomicity guarantees after code review:

- commit_all and _rollback_committed catch Exception (not just
  SandboxError) so unexpected errors cannot bypass rollback.
  Non-SandboxError exceptions are wrapped in a new AtomicCommitError
  (chaining the original as __cause__) that carries rolled_back_ids
  and failed_rollback_ids attributes so callers can programmatically
  determine rollback outcomes.

- _rollback_committed returns both rolled_back_ids and
  failed_rollback_ids; the error result metadata now carries
  both "rolled_back" and "rollback_failed" keys.

- _rollback_committed iterates in reverse (LIFO) order following
  the standard transaction-log undo pattern.  Clarified in
  docstring that this is distinct from the specification DAG-based
  "top-down" rollback ordering (line 24632).

- TransactionSandbox.rollback() from COMMITTED now raises
  SandboxRollbackError (database commits are irreversible)
  instead of silently transitioning to ROLLED_BACK.

- TransactionSandbox is now classified as non-rollbackable in
  commit_all alongside NoSandbox and committed last in the batch,
  since database COMMIT is irreversible.  Docstring corrected to
  match the raising behavior.

- CopyOnWriteSandbox and OverlaySandbox rollback-from-COMMITTED
  uses rename-based safe_restore() to prevent data loss when
  the copytree step fails after the original was removed.

- CopyOnWriteSandbox and OverlaySandbox rollback() now catches
  Exception (not just OSError), matching the broader catch used
  in _rollback_committed, so non-OSError exceptions from
  safe_restore set the status to ERRORED correctly.

- Extracted shared _fs_utils module (backup_directory,
  safe_restore, compute_diff) with symlink, permission, and
  timestamp preservation (including directory timestamps),
  replacing duplicated per-class _backup_directory and
  _compute_diff methods.

- backup_directory defers directory permissions and timestamps
  to a bottom-up post-walk pass, fixing incorrect mtime
  preservation (POSIX file creation inside a directory overwrites
  its mtime) and preventing restrictive source permissions from
  blocking backup writes.

- backup_directory skips non-regular files (FIFOs, sockets,
  device files) with a warning to prevent hangs on special files.

- CopyOnWriteSandbox and OverlaySandbox commit() now attempts
  to restore the original from the pre-commit backup when the
  file-copy phase fails midway, preventing partial corruption.
  If the restore itself fails the backup is preserved for manual
  recovery (cleanup() still removes it).

- CopyOnWriteSandbox and OverlaySandbox commit() error handler
  now catches Exception (not just OSError) so that unexpected
  errors during the file-copy phase also trigger pre-commit
  backup restoration, preventing partial corruption of the
  original directory.

- Pre-commit backup exception handler catches Exception (not
  just OSError) preventing temp directory leaks on non-OSError
  failures from backup_directory.

- Fixed _pre_commit_backup assignment timing: the backup
  reference is now assigned only AFTER backup_directory()
  succeeds, preventing safe_restore() from corrupting an intact
  original with a partial backup when backup_directory() fails
  (e.g. disk full).

- Rollback from COMMITTED with no pre-commit backup (no changes
  were applied) is now a no-op instead of raising
  SandboxRollbackError, preventing false rollback-failure
  reports in commit_all error metadata.

- commit_all logs a warning when NoSandbox or TransactionSandbox
  instances are present in the batch since their changes cannot
  be rolled back, which breaks the atomicity guarantee.

- Pre-commit backup is skipped when compute_diff returns no
  changes, avoiding a full directory copy for no-op commits.

- GitWorktreeSandbox clears _pre_merge_commit on commit failure
  so the stale value cannot be used by future code.

- commit_all docstring documents Raises clause for AtomicCommitError
  exception wrapping behavior.

- GitWorktreeSandbox.rollback() docstring warns about
  multi-worktree safety when rolling back from COMMITTED.

- Updated SandboxStatus transition diagram in protocol.py to
  clearly show the COMMITTED -> ROLLED_BACK path.

- Added spec-contradiction note (line 45938 vs 19193) in
  commit_all docstring.

- OverlaySandbox rollback from COMMITTED now properly remounts
  OverlayFS for real overlay (unmount, clean upper/work dirs,
  remount) and uses dirs_exist_ok=True for userspace fallback
  to prevent FileExistsError if rmtree silently fails.  The
  merged directory is reset from the restored original,
  preventing stale pre-rollback data from being exposed on
  re-activation via get_path() (which allows ROLLED_BACK status).

- OverlaySandbox rollback from COMMITTED now raises
  SandboxRollbackError if the OverlayFS unmount fails,
  preventing a double-mount attempt that would leave the
  sandbox in an inconsistent state.

- OverlaySandbox rollback from ACTIVE now uses dirs_exist_ok=True
  for userspace fallback to prevent FileExistsError when rmtree
  with ignore_errors=True silently fails.

- CopyOnWriteSandbox rollback from ACTIVE now uses
  dirs_exist_ok=True in copytree to prevent FileExistsError
  when rmtree with ignore_errors=True silently fails, matching
  the fix already applied to OverlaySandbox.

- Non-rollbackable sandboxes (NoSandbox, TransactionSandbox) are
  committed last in the batch so that all rollbackable sandboxes
  commit first; if any rollbackable sandbox fails, none of the
  non-rollbackable sandboxes will have committed yet.

- Moved NoSandbox and TransactionSandbox imports to module level
  in manager.py (no circular dependency exists).

- Pre-commit backups are now created on the same filesystem as
  the original directory (using dir= argument to mkdtemp),
  avoiding cross-device copy overhead and ensuring os.rename
  compatibility.

- safe_restore now renames the target into the mkdtemp directory
  instead of removing the mkdtemp dir first, eliminating the
  residual TOCTOU window between rmdir and rename.

- safe_restore catches BaseException (not just OSError) to
  ensure the original directory is always renamed back on
  unexpected errors, preventing the original from being left
  in the renamed-aside state.

- Added AtomicCommitError exception class to protocol.py
  carrying rolled_back_ids and failed_rollback_ids attributes.

- Exported AtomicCommitError from sandbox package __init__.py
  so callers can import it from the public API.

- Added BDD scenarios: LIFO rollback order, AtomicCommitError
  wrapping with RuntimeError cause and rollback metadata,
  _fs_utils backup/restore coverage, no-change commit rollback
  success, directory timestamp preservation, OverlaySandbox
  merged dir reset after COMMITTED rollback,
  CopyOnWriteSandbox rollback from COMMITTED restores original,
  GitWorktreeSandbox rollback from COMMITTED undoes merge,
  TransactionSandbox rollback from COMMITTED raises
  SandboxRollbackError about irreversible commit.

ISSUES CLOSED: #925

Post-review hardening (PR #1146 review findings):

- OverlaySandbox rollback from COMMITTED with no backup (no-op)
  now skips the merged directory reset entirely, preventing
  unnecessary unmount/remount or re-copy that could fail and
  turn a harmless no-op rollback into a SandboxRollbackError
  during commit_all atomic recovery.

- OverlaySandbox rollback no longer double-wraps
  SandboxRollbackError: the outer except Exception handler
  now has a preceding except SandboxRollbackError clause that
  re-raises directly, avoiding a confusing double-wrapped
  error chain.

- CopyOnWriteSandbox.get_path() now accepts ROLLED_BACK status
  for consistency with OverlaySandbox and the protocol status
  transition table (ROLLED_BACK -> ACTIVE).

- CopyOnWriteSandbox rollback from COMMITTED now resets the
  sandbox copy from the restored original via rmtree+copytree,
  preventing stale pre-rollback modifications from being
  exposed on re-activation.

- rollback_all now catches Exception (not just SandboxError)
  so that unexpected rollback errors do not prevent remaining
  sandboxes from being rolled back, consistent with the pattern
  already used in _rollback_committed.

- commit_all docstring now documents a thread-safety warning:
  the method is not safe for concurrent calls on the same
  plan_id since sandbox commit/rollback runs outside the lock.

- Fixed CHANGELOG.md whitespace inconsistencies (double leading
  spaces on two lines).

Post-review hardening round 2 (PR #1146 automated review):

- safe_restore now uses os.rename (O(1) atomic rename) instead
  of shutil.copytree (O(n) recursive copy) for the main restore
  path, since backup and target are always on the same
  filesystem.  This eliminates the ENOTEMPTY bug where a partial
  copytree failure left target_path partially populated, causing
  the recovery os.rename to fail and strand the original in the
  stale temp directory.

- OverlaySandbox.get_path() now transitions ROLLED_BACK to
  ACTIVE, matching CopyOnWriteSandbox and the protocol
  transition table (ROLLED_BACK -> ACTIVE).

- GitWorktreeSandbox.get_path() now accepts ROLLED_BACK status
  for consistency with all other sandbox implementations and
  the protocol transition table (ROLLED_BACK -> ACTIVE).

- rollback_all now also handles sandboxes in COMMITTED status
  (not just ACTIVE), consistent with the state machine allowing
  COMMITTED -> ROLLED_BACK.

- cleanup_all now catches Exception (not just SandboxError) so
  a single unexpected error does not abort cleanup of remaining
  sandboxes, consistent with _rollback_committed and
  rollback_all.

- Restructured CHANGELOG entry from a single ~90-line paragraph
  into structured sub-bullets for readability.

- Added BDD scenarios: no-op rollback from COMMITTED for
  CopyOnWriteSandbox and OverlaySandbox (zero-change commit),
  commit ordering verification (rollbackable before
  non-rollbackable).

Post-review hardening round 3 (PR #1146 deep automated review):

- cleanup_abandoned now catches Exception (not just SandboxError)
  so that unexpected errors (e.g. raw OSError, PermissionError)
  do not crash the loop and prevent remaining abandoned sandboxes
  from being cleaned up, consistent with cleanup_all,
  rollback_all, and _rollback_committed.

- OverlaySandbox._mount_overlay() now catches
  subprocess.TimeoutExpired (in addition to CalledProcessError
  and OSError), preventing create() from leaving the sandbox in
  PENDING status when mount hangs beyond the timeout.

- OverlaySandbox._unmount_overlay() now catches
  subprocess.TimeoutExpired (in addition to CalledProcessError
  and OSError), preventing cleanup() from leaving the sandbox
  in a zombie state when umount hangs beyond the timeout.

- OverlaySandbox._mount_overlay() validates that overlay paths
  do not contain commas, which would corrupt the OverlayFS mount
  options string (comma is the mount option delimiter).

- GitWorktreeSandbox.commit() now checks git diff return code
  so that a failed diff command raises CalledProcessError instead
  of silently concluding there are no changes and skipping the
  merge.

- safe_restore cleanup of the temporary rollback container now
  runs in a finally block, preventing a temp directory leak
  when the rename fails and the exception is re-raised.

- Fixed misleading BDD step name: "backup path that will cause
  copytree to fail" renamed to "backup path that will cause
  rename to fail" since safe_restore now uses os.rename.
2026-03-29 18:57:15 +01:00
Luis Mendes f678d611bb feat(db): add correction_attempts table per specification DDL
Added CorrectionAttemptModel SQLAlchemy model with all spec-defined columns
(correction_attempt_id, plan_id, original_decision_id, new_decision_id,
mode, guidance, archived_artifacts_path, state, created_at, completed_at).
Added FK constraints to v3_plans and decisions tables. Created Alembic
migration and idx_corrections_plan index. Added repository layer for
CRUD operations.

Addressed code review feedback (rounds 1-12):
- Replaced Any type annotations with typed CorrectionAttemptRecord
  signatures using TYPE_CHECKING imports.
- Replaced fragile time.sleep with deterministic timestamps.
- Added cascade deletion test with PRAGMA foreign_keys=ON.
- Changed update_state() to accept typed enum and datetime params.
- Added guidance non-empty validator with max_length=10_000.
- Added spec-aligned server_default for created_at column.
- Fixed timezone handling in to_domain() and from_domain().
- Added spec-defined lifecycle state transition validation via
  validate_correction_state_transition() domain function.
- Improved FK-violation error messages in create() and update_state().
- Normalised timestamp to millisecond precision matching SQLite
  server_default strftime('%f') output.
- Added auto-set completed_at on terminal transitions.
- Added CorrectionAttemptRecord field validators (strip, non-empty).
- Changed original_decision_id FK from CASCADE to RESTRICT matching
  spec DDL default, preserving correction audit trail.
- Added input validation in update_state() for new_decision_id and
  archived_artifacts_path (empty/whitespace rejection).
- Fixed dirty-session bug by moving validation before ORM mutations.
- Extracted _SQLITE_TIMESTAMP_MS_LEN constant for timestamp truncation.

Addressed thirteenth code review feedback:
- Changed InvalidCorrectionStateTransitionError base class from
  DatabaseError to BusinessRuleViolation per CONTRIBUTING.md exception
  semantics (state transition is a business rule, not a database error;
  prevents incorrect retries by @database_retry decorator).
- Changed new_decision_id FK from SET NULL to RESTRICT matching the
  spec DDL default (no ON DELETE clause) and consistent with the
  RESTRICT approach used for original_decision_id.
- Changed update_state() input validation for new_decision_id and
  archived_artifacts_path from DatabaseError to ValueError per
  CONTRIBUTING.md argument validation guidelines.
- Defensive to_domain() coercion now defaults corrupted state to
  'failed' (terminal) instead of 'pending', preventing re-execution
  of completed/failed corrections with corrupted DB values.
- Extracted format_sqlite_timestamp() helper and SQLITE_TIMESTAMP_MS_LEN
  public constant, removing duplicated timestamp formatting logic
  between from_domain() and update_state().
- Added code comment explaining CASCADE on plan_id FK as a codebase
  convention deviation from spec DDL default.
- Added BDD scenario verifying RESTRICT FK on original_decision_id
  blocks decision deletion.
- Replaced weak cross-plan isolation test with stronger two-plan
  scenario verifying list_by_plan returns only each plan's attempts.
- Fixed hardcoded assertion in step_check_archived_path to use
  context variable.
- 45 BDD scenarios and 5 Robot integration tests.

Addressed fourteenth code review feedback:
- Added ORM-level relationship(cascade="all, delete-orphan") on
  LifecyclePlanModel for CorrectionAttemptModel, consistent with all
  other v3_plans child tables, ensuring ORM-level cascade deletes
  work even when SQLite FK enforcement is disabled.
- Added defensive to_domain() coercion for corrupted guidance column
  (defaults to "[corrupted]" with warning log), consistent with
  existing mode/state coercion pattern.
- Added ValueError guard in format_sqlite_timestamp() rejecting naive
  datetimes per CONTRIBUTING.md fail-fast argument validation.
- Fixed stale spec DDL line reference in CorrectionAttemptModel
  docstring.
- Fixed duplicated docstring on SQLITE_TIMESTAMP_MS_LEN constant.

Addressed fifteenth code review feedback:
- Fixed update_state() to defensively handle corrupted DB state values
  via try/except ValueError coercion to FAILED terminal state with
  warning log, consistent with to_domain() defensive coercion pattern.
- Strengthened RESTRICT FK BDD assertion to verify exception type
  (IntegrityError/DatabaseError) instead of only checking presence.
- Split multi-When/Then cross-plan isolation BDD scenario into
  idiomatic single-When/Then scenarios per Gherkin best practice.
- 53 BDD scenarios (was 45) and 5 Robot integration tests.

ISSUES CLOSED: #920
2026-03-29 16:23:04 +01:00
brent.edwards bc6a41deb6 tdd(cli): prevent actor list from triggering database updates (#1151)
## Summary

- Fix bug #797: `agents actor list` no longer triggers database writes (`upsert_actor`, `set_default_actor`) by removing `ensure_built_in_actors()` from `ActorRegistry.list()` and `ActorRegistry.list_actors()`
- Include TDD regression tests from #841 with `@tdd_expected_fail` removed per Bug Fix Workflow
- Update three existing test suites that relied on the old behavior to call `ensure_built_in_actors()` explicitly

## Root Cause

`ActorRegistry.list_actors()` and `ActorRegistry.list()` both unconditionally called `self.ensure_built_in_actors()` before delegating to the actor service. `ensure_built_in_actors()` iterates all configured providers and calls `_actor_service.upsert_actor()` for each — a database WRITE operation. It may also call `_actor_service.set_default_actor()` if no default exists — another WRITE. This means every read-only `agents actor list` command triggered database writes and could prompt for pending migrations on fresh checkouts.

## Changes

### Bug Fix
- **`src/cleveragents/actor/registry.py`** — Removed `self.ensure_built_in_actors()` from `list()` and `list_actors()`. Both methods now delegate directly to the service layer without triggering writes. All write-heavy methods (`add`, `upsert_actor`, `get`, `get_actor`, `remove`, `remove_actor`, `set_default_actor`, `get_default_actor`) still call `ensure_built_in_actors()`.

### TDD Tests (from #841, `@tdd_expected_fail` removed)
- **`features/tdd_actor_list_no_db_update.feature`** — 2 Behave scenarios verifying `upsert_actor` and `set_default_actor` are not called during `actor list`
- **`features/steps/tdd_actor_list_no_db_update_steps.py`** — Step definitions
- **`robot/tdd_actor_list_no_db_update.robot`** — 2 Robot Framework integration tests
- **`robot/helper_tdd_actor_list_no_db_update.py`** — Robot helper script

### Test Adjustments
Three existing test suites relied on the old (buggy) behavior where `list_actors()` called `ensure_built_in_actors()`:
1. **`features/consolidated_actor.feature`** — Scenario updated to explicitly call `ensure_built_in_actors()` before `list_actors()`
2. **`features/steps/tdd_actor_list_validation_steps.py`** + **`robot/helper_tdd_actor_list_validation.py`** (bug #592) — Updated to call `ensure_built_in_actors()` explicitly before CLI invocation
3. **`features/steps/actor_list_empty_steps.py`** (bug #592) — Updated with explicit `ensure_built_in_actors()` call and capturing upsert pattern

## Quality Gates

| Gate | Result |
|------|--------|
| `nox -e lint` |  Pass |
| `nox -e typecheck` |  Pass (0 errors) |
| `nox -e unit_tests` |  Pass (468 features, 12367 scenarios, 0 failures) |
| `nox -e integration_tests` | ⚠️ 6 pre-existing failures (timeouts/OOM) |
| `nox -e coverage_report` |  98% (>= 97% threshold) |

The 6 integration test failures are pre-existing infrastructure issues (SIGTERM/SIGKILL timeouts) unrelated to this change: Container Resolve Crash (3), M3 E2E Verification (2), Resource CLI (1).

Closes #797

Reviewed-on: cleveragents/cleveragents-core#1151
Reviewed-by: Jeffrey Phillips Freeman <jeffrey.freeman@cleverthis.com>
Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
2026-03-28 05:27:14 +00:00
brent.edwards b46a0e6102 feat(plan): implement real revert-mode re-execution from decision point (#1153)
## Summary

Implements the full revert-mode re-execution pipeline from the specification (§ Correction Flow, Revert Mode), enabling `agents plan correct --mode=revert` to truly re-execute from the targeted decision point rather than only logically marking decisions as superseded.

### Changes

- **Resource rollback**: `CorrectionService.execute_revert` now delegates to `CheckpointService.rollback_to_checkpoint` when a decision-aligned checkpoint exists, performing real `git reset --hard` in the sandbox. Gracefully degrades when checkpointing is unavailable.
- **Reasoning rollback**: Extracts `actor_state_ref` from the target decision's `ContextSnapshot` for downstream LangGraph actor restoration.
- **Guidance injection**: Generates a `user_intervention` decision ID for callers to record correction guidance in the decision tree.
- **Phase transition**: Signals that the plan should re-enter Strategize via `phase_transition_target="strategize"` on the result.
- **Model extension**: Added `checkpoint_restored`, `actor_state_ref`, `user_intervention_decision_id`, and `phase_transition_target` fields to `CorrectionResult` with backward-compatible defaults.
- **Dispatch update**: `execute_correction` now accepts and forwards a `decisions` parameter.

### Design Approach

Uses a signal-based architecture: rather than having `CorrectionService` directly manipulate plan state or LangGraph actors, the enhanced `execute_revert` returns signal fields on `CorrectionResult` that downstream consumers use for the full pipeline. This preserves separation of concerns.

### Tests

- **Behave**: 16 new BDD scenarios in `features/revert_re_execution.feature`
- **Robot**: 7 integration tests in `robot/revert_re_execution.robot`
- **Coverage**: 98% (above 97% threshold)

### Quality Gates

| Gate | Status |
|------|--------|
| `nox -s lint` | Pass |
| `nox -s typecheck` | Pass (0 errors) |
| `nox -s unit_tests` | Pass (12376 scenarios, 0 failed) |
| `nox -s integration_tests` | Pass (1631 passed, 3 pre-existing failures #647) |
| `nox -s e2e_tests` | Pass (37 passed) |
| `nox -s coverage_report` | Pass (98%) |

Closes #844

Reviewed-on: cleveragents/cleveragents-core#1153
Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
2026-03-28 00:57:51 +00:00
brent.edwards b51df2ee0f feat(server): add Kubernetes Helm chart for server deployment (#1085)
## Summary

This PR adds Kubernetes Helm deployment support for the CleverAgents server.

Closes #928

### What this PR includes

- Helm chart under `k8s/` with Deployment, Service, optional Ingress, ConfigMap,
  ServiceAccount, Secrets, NOTES, and optional Redis subchart configuration.
- Multi-stage `Dockerfile.server` for server runtime deployment.
- Deployment-focused docs in `k8s/README.md`.
- Behave + Robot + benchmark coverage for chart/deployment wiring.

### Review fixes applied (cycle 11 — hurui200320 review #2687)

**Critical fix:**

1. **CI SHA256 checksum verification fixed** — All 3 Helm install blocks (`unit_tests`, `integration_tests`, `helm` jobs) now save the tarball using its original filename (`helm-v3.16.4-linux-amd64.tar.gz`) instead of `helm.tgz`, so the `.sha256sum` file can correctly locate and verify it. This was causing all 3 CI Helm jobs to fail with "No such file or directory".

**Major fixes (test coverage gaps):**

2. **405 Allow header now tested** — The existing "POST to known path returns method not allowed" scenario now asserts that the `Allow: GET` header is present. Added a second 405 scenario testing `POST /live` for broader `_KNOWN_PATHS` coverage (review item #11).
3. **Security-hardening headers now tested** — New scenario "HTTP responses include security-hardening headers" verifies `content-length`, `x-content-type-options: nosniff`, and `cache-control: no-store` headers are present on HTTP responses.
4. **Lifespan warning logging now tested** — New scenario "Unrecognised lifespan message type logs warning and continues" queues `lifespan.startup` → `lifespan.bogus` → `lifespan.shutdown` and verifies: (a) the app completes the lifespan cycle cleanly, (b) a warning is logged mentioning the unrecognised type.

### Review fixes applied (cycle 10)

**Critical/Major fixes (from hurui200320's prior REQUEST_CHANGES):**

1. **Rebased branch onto master** — Removed merge commit per CONTRIBUTING.md rebase-only policy. Clean linear history restored.
2. **Fixed commit message body** — Replaced literal `\n` sequences with actual newlines. `ISSUES CLOSED: #928` footer is now on its own line after a blank separator.
3. **Moved uvicorn import to module top level** — `from uvicorn import run as uvicorn_run` is now at the top of `src/cleveragents/cli/commands/server.py` per Import Guidelines. Updated test mock target from `uvicorn.run` to `cleveragents.cli.commands.server.uvicorn_run`.
4. **Added SHA256 checksum verification** — All three Helm CLI install blocks in `.forgejo/workflows/ci.yml` now download and verify `helm.sha256sum` before extracting the binary.

**Minor fixes:**

5. **Added `Dockerfile.server` build to CI** — New "Build Docker image (Server)" step in the `docker` job validates the server Dockerfile.
6. **ASGI 405 Method Not Allowed** — Known paths (`/`, `/live`, `/ready`, `/health`) now return 405 with `Allow: GET` header for non-GET methods, per RFC 9110 §15.5.6. Added `_KNOWN_PATHS` frozenset.
7. **WebSocket close protocol fix** — App now calls `await receive()` to consume the `websocket.connect` event before closing. Changed close code from 1000 (Normal Closure) to 1008 (Policy Violation).
8. **Lifespan handler logging** — Unrecognised lifespan message types are now logged as warnings instead of silently consumed.
9. **Security-hardening headers** — `_send_response` now includes `content-length`, `x-content-type-options: nosniff`, and `cache-control: no-store` on all HTTP responses.
10. **`.dockerignore` credential patterns** — Added `*.pem`, `*.key`, `*.p12`, `*.pfx`, `credentials*.json`.
11. **`--log-level` validation** — Constrained to `click.Choice(["critical", "error", "warning", "info", "debug", "trace"])` for clean CLI validation errors.
12. **Reverted unrelated semgrep pre-commit change** — `pass_filenames` and `entry` restored to original values per atomic commit hygiene.
13. **Removed unused `ReceiveCallable` type alias** and `Callable`/`Awaitable` imports from `asgi_app_steps.py`.
14. **Fixed redundant `shutil.which("helm")` check** — `_skip_if_helm_missing` now returns `bool` to eliminate the duplicate check in `_render_chart`.
15. **Improved test deque error handling** — Lifespan test receive mock now raises descriptive `AssertionError` instead of opaque `IndexError`.
16. **Scope type dispatch** — Changed `if/if/if` to `if/elif/elif` for mutually exclusive ASGI scope types.
17. **Dockerfile.server base image** — Standardised to `python:3.13-slim` (floating minor) consistent with CLI Dockerfile.
18. **Dockerfile layer caching** — Split `uv pip install build` and `python -m build` into separate `RUN` instructions.
19. **Removed extraneous double blank line** in Dockerfile.server.

### Deferred items (acknowledged, not in scope)

- PodDisruptionBudget, HorizontalPodAutoscaler, NetworkPolicy — Follow-up for production hardening.
- `appVersion: "1.0.0"` placeholder — Needs tracking issue for release versioning alignment.
- Readiness probe with downstream dependency checks — Documented limitation.
- Cross-system test for probe paths matching ASGI routes — Test enhancement.
- Improved benchmarks (helm template timing vs PyYAML parsing) — Benchmark quality improvement.
- CI DRY violation (Helm install 3×) — Code quality improvement, consider composite action.
- File length limits exceeded (`k8s_helm_chart_steps.py` 551 lines, `helper_k8s_helm_chart.py` 678 lines) — Non-blocking, can be split in follow-up.
- `runAsGroup: 1000` in pod security context — Defense-in-depth improvement.
- HEAD method support on known paths — RFC compliance, does not affect K8s probes.
- `click.Choice` log-level validation via CLI runner test — Test gap.

### Scope note: status-check CI gate

The `status-check` job now includes `integration_tests`, `e2e_tests`, and `helm` in its `needs` list. The `helm` job is new in this PR. The `integration_tests` and `e2e_tests` additions fix previously-missing gate checks — included here since this PR modifies both of those jobs to install Helm.

### Quality gates

- `nox -e lint` 
- `nox -e typecheck` 
- `nox -e unit_tests`  (12,321 scenarios passed, 4 skipped)
- `nox -e integration_tests` — 3 pre-existing failures in unrelated areas (plan correction, resource types)
- `nox -e e2e_tests` — pre-existing failures (LLM API keys not available in local env)
- `nox -e coverage_report`  (**97.7%**)

Reviewed-on: cleveragents/cleveragents-core#1085
Reviewed-by: Jeffrey Phillips Freeman <jeffrey.freeman@cleverthis.com>
Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com>
Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
2026-03-27 23:53:22 +00:00
brent.edwards 8b8942817c feat(wf03): add plan prompt test coverage and confidence-threshold pausing verification 2026-03-27 13:35:17 -07:00
hamza.khyari ebafe8cddd fix(cli): implement --mount flag on resource add container-instance
Add --mount flag to 'resource add container-instance' supporting both
resource-reference mounts (--mount local/api-repo:/workspace) and
host-path mounts (--mount /var/config:/config:ro).

- _parse_mount_spec() parses SOURCE:TARGET and SOURCE:TARGET:MODE formats
- Resource-reference mounts (containing /) get type='resource', mode='rw'
- Host-path mounts support explicit rw/ro mode
- Mounts stored as JSON string in resource properties['mounts']
- _format_properties() renders mounts in human-readable format for 'resource show'
- Added mount to container-instance cli_args metadata
- 6 Behave scenarios testing single mount, dual mount, persistence, and error paths
- Updated CHANGELOG

ISSUES CLOSED: #1078
2026-03-27 12:57:03 +00:00
hamza.khyari 6a8f724299 feat(lsp): add missing LspCapability enum values
Align LspCapability enum with the spec's 11-capability set
(docs/specification.md lines 20705-20717):

Renamed: TYPE_INFO -> HOVER, SYMBOLS -> DOCUMENT_SYMBOLS,
         FORMAT -> FORMATTING
Added:   DEFINITIONS, SIGNATURE_HELP, WORKSPACE_SYMBOLS

Updated LspToolAdapter._CAPABILITY_TOOL_MAP (11 entries):
- code_actions suffix -> code-actions (hyphen per spec)
- RENAME gets dedicated schema with new_name parameter
- workspace_symbols gets query-based schema

Updated _input_schema_for() with 4 schema categories extracted
to module-level constants: file-only, position-based, rename
(with new_name), and query-based. Added defensive ValueError for
unmapped capabilities. Added additionalProperties: false.

Extended LspClient.initialize() to advertise all 11 capabilities
in the initialize request (hover, definition, references, rename,
codeAction, formatting, signatureHelp, documentSymbol, workspace
symbol).

Fixed _make_runtime_handler() to handle workspace_symbols as
query-only input (no file_path required), resolving the
schema/handler contract mismatch.

Fixed stale references: type_info -> hover, symbols ->
document_symbols in test fixtures, feature files, CLI docstring.

Updated docs/reference/lsp.md and CHANGELOG.md.

Behave tests (38 scenarios): enum completeness, tool spec generation
with structural validation, input schema per category, all 11
stubbed provider keys, negative tests for invalid capability and
defensive ValueError branch.

ISSUES CLOSED: #834
2026-03-27 11:10:56 +00:00
hamza.khyari 7bb77aee5c feat(resource): implement ResourceHandler sandbox and checkpoint methods
Extend the ResourceHandler protocol with four sandbox/lifecycle methods:
create_sandbox (idempotent), create_checkpoint, rollback_to, project_access.

Fixes from self-review:
- Instance-level checkpoint dict instead of ClassVar (no shared state)
- Git rollback uses 'git reset --hard' + 'git clean -fd' (not just checkout)
- FsDirectory rollback skips .git to preserve git metadata
- project_access separates ImportError (local mode) from ValueError (reject)
- Added discard_checkpoints() cleanup method
- Git rollback test verifies file content restored

ISSUES CLOSED: #836
2026-03-27 10:50:15 +00:00
CoreRasurae 7cf03f03f4 fix(action): query persisted actions from database in list_actions
list_actions() previously merged in-memory cache with the database but
used the cache as the primary source.  Actions created by previous CLI
invocations were only stored in the database and were invisible to the
current process.

When persistence is enabled and a Unit of Work is available, the method
now queries the database first — using get_by_namespace() when a
namespace filter is given, or the new list_all() otherwise — and
refreshes the in-memory cache with any newly-discovered entries.  Falls
back to the in-memory cache on DatabaseError or when no UoW is wired.

Added ActionRepository.list_all() to retrieve all persisted actions
ordered by namespace and name.

Refs: #760
2026-03-26 18:51:43 +00:00
Luis Mendes 59be111e1d fix(plan): complete apply phase inline in auto_progress and lifecycle-apply CLI
Modified auto_progress() to complete the Apply phase immediately after
transitioning from Execute to Apply, since Apply is a metadata transition
with no LLM processing.  This ensures `plan execute` drives the plan to
the terminal `applied` state when the automation profile permits (ci,
full-auto profiles with auto_apply < 1.0).

Extracted `_complete_apply_if_queued()` helper that consolidates the
Apply-completion pattern (start_apply + complete_apply) into a single
method with error recovery (calls `fail_apply` on failure) and async-job
guard (skips inline completion when async execution is enabled to avoid
orphaning enqueued jobs).  Used by `auto_progress()`,
`lifecycle_apply_plan()`, and `try_auto_run()`.

Added PlanLifecycleService.try_auto_run() that drives plans through all
lifecycle phases (strategize → execute → apply) when automation-profile
thresholds allow automatic progression.  Each phase checks the profile's
auto_* threshold before proceeding; a threshold of 1.0 stops the plan at
that phase boundary for human approval.

Fixed `lifecycle-apply` CLI leaving plans stuck in `apply/queued` without
completing.  The command now calls `_complete_apply_if_queued()` when the
plan is in Apply/queued, driving it to the terminal `applied` state.

Fixed stale RICH output in `lifecycle_apply_plan` that printed
"Plan is now in Apply phase (queued)" after the plan had already reached
terminal `applied` state; now branches on `plan.is_terminal`.

Additional fixes:
- SQLite UNIQUE constraint violation in LifecyclePlanRepository.update():
  added session.flush() after clear() on child collections (project_links,
  arguments, invariants) before re-inserting rows
- Added 'state' alias in _plan_spec_dict() JSON output for spec §Example 7
  jq compatibility
- Updated plan execute and lifecycle-apply reference documentation

Refs: #753
2026-03-26 17:35:24 +00:00
CoreRasurae e4c01492d5 refactor(cli): align actor run signature with spec positional args
Aligned the `agents actor run` command signature with the
specification by introducing positional NAME and PROMPT arguments.
The --config/-c option is preserved as an optional fallback for
direct YAML invocation. When NAME is provided without --config,
the actor is resolved from the Actor Registry.

Updated both actor_run.py and actor.py run commands. Added backward
compatibility: if --config is provided, it takes precedence over
name-based resolution.

Review fixes applied (code review round 1):
- P1-1: Narrowed bare `except Exception` to `except NotFoundError`
  in _resolve_config_files to avoid masking infrastructure errors.
- P1-2: Moved _resolve_config_files call inside the try block in
  run() so container/registry init errors get user-friendly messages.
  Added `except click.exceptions.Exit: raise` to let typer.Exit
  propagate through the broadened try scope.
- P1-3: Added atexit.register cleanup for temp files created by
  _resolve_config_files (resource leak fix).
- P1-4: Added CHANGELOG.md entry for the breaking CLI change.
- P2-1: Extracted duplicated _resolve_config_files to shared module
  `_resolve_actor.py`; both actor.py and actor_run.py now import it.
- P2-2: Added guard for actors with no configuration data
  (config_blob=None) to produce a clear error instead of invalid YAML.
- P2-3/P2-4: Added 5 BDD scenarios exercising the real
  resolve_config_files function (registry path, yaml_text path,
  config_blob fallback, no-config-data error, not-found error).
- P2-5: Added @coverage tags to all new BDD scenarios.
- P3-1: Added timeout=120s and on_timeout=kill to Robot tests.
- Fixed rxpy_route_validation.robot tests that used the removed
  --prompt/-p option (replaced with positional NAME + PROMPT args).

Review fixes applied (code review round 2):
- P2-1: Aligned actor_run.py exception handler from
  `CleverAgentsException` to `CleverAgentsError`, matching actor.py
  so infrastructure errors from registry resolution get user-friendly
  messages instead of falling through to the generic handler.
- P2-2: Changed `yaml.dump` to `yaml.safe_dump` in _resolve_actor.py
  for fail-fast behavior on unexpected types, consistent with the
  codebase's dominant pattern.
- P3-6: Replaced defensive `getattr(actor, ...)` calls with direct
  Pydantic model attribute access (`actor.yaml_text`, `actor.config_blob`)
  for type-checker coverage.
- P3-1: Switched BDD temp file cleanup from post-assertion
  `unlink()` to `context.add_cleanup()` for leak-proof teardown.
- P2-3/P3-2/P3-3/P3-4: Added 3 BDD edge-case scenarios
  (empty config_blob dict, infrastructure error propagation, empty
  string name) and 1 Robot test case (actor_app registry resolution).

Review fixes applied (code review round 3):
- P1-1: Migrated 48 remaining `-p` invocations across 9 Robot test
  files to the new positional `NAME PROMPT` pattern (context_delete_all_yes,
  load_context_test, scientific_paper_e2e_test, routing_prefix_stripping,
  scientific_paper_basic, scientific_paper_writer_test,
  context_management_test, initial_next_command_test,
  system_prompt_template_rendering).
- P2-1: Documented `--config/-c` as a spec deviation in
  `_resolve_actor.py` module docstring (spec lines 4562-4566 define
  `actor run` with no --config option; issue #901 AC accepts keeping
  it as optional).
- P2-2: Corrected `--config` help text from "fallback" to "overrides
  registry-based name resolution" — the option takes precedence, not
  the other way around.
- P3-1: Added `.strip()` to `yaml_text` emptiness check in
  `_resolve_actor.py` to handle whitespace-only values that would
  otherwise bypass the `config_blob` fallback.
- P3-2: Added `from None` to the no-configuration-data
  `typer.Exit(code=2)` for consistency with the not-found path.
- P3-3: Added BDD scenario testing `--config` precedence for
  `actor_run_app` (was only tested for `actor_app`).
- P3-4: Strengthened config_blob BDD scenario to verify generated
  YAML is parseable via `yaml.safe_load` round-trip.
- P3-7: Replaced hardcoded `/tmp/dummy.yaml` with
  `tempfile.gettempdir()` for portability.
- P3-8: Moved 5 inline imports to module level per CONTRIBUTING.md
  §1292-1294 (3x `import click`, 1x InfrastructureError in steps;
  1x `import typer` in robot helper).
- P3-9: Added `encoding="utf-8"` to `_write_yaml` in Robot helper
  for consistency with production code.

Review fixes applied (code review round 4):
- P2-1: Wrapped `yaml.safe_dump` in `_resolve_actor.py` with
  `try/except yaml.YAMLError` so non-serialisable config_blob values
  produce a user-friendly error message instead of a raw traceback.
- P2-2: Moved remaining inline `import yaml` to module level in
  `actor_run_signature_steps.py` per CONTRIBUTING.md §1292-1294.
- P2-3: Replaced 3 bare `assert` statements in Robot helper
  `helper_actor_run_signature.py` with diagnostic `if/print/sys.exit`
  pattern matching the rest of the file, improving failure diagnostics.

Review fixes applied (code review round 5):
- P3-4: Replaced per-call `atexit.register(lambda)` in
  `_resolve_actor.py` with a module-level `_temp_files` set and a
  single `atexit` handler (`_cleanup_temp_files`) to prevent
  unbounded handler accumulation in same-process usage (e.g. test
  suites running multiple CliRunner invocations).
- P3-1/P3-2/P3-3: Added 4 BDD scenarios: whitespace-only
  `yaml_text` fallback to config_blob, config-precedence
  registry-not-consulted assertion for both `actor_app` and
  `actor_run_app`, multiple `--config` files with positional NAME.
- P4-1: Strengthened error-path BDD assertions to verify error
  message content (not-found, no-config-data, serialisation-error)
  alongside exit codes via captured stderr.
- P4-2: Added Robot test case for `actor_app` unknown-name error
  path and corresponding helper function.

ISSUES CLOSED: #901
2026-03-26 13:41:31 +00:00
CoreRasurae 00881a3e5f feat(observability): implement Event System Domain Event Taxonomy (full EventType enum + DomainEvent model)
Verified and completed the Event System Domain Event Taxonomy implementation.

Gap analysis:
- EventType enum (38 types across 12 domains): ALREADY EXISTED, verified complete
- DomainEvent Pydantic model (all 9 fields): ALREADY EXISTED, verified complete
- EventBus Protocol (emit + subscribe): ALREADY EXISTED, verified complete
- ReactiveEventBus (RxPY Subject, stream property): ALREADY EXISTED, verified
- LoggingEventBus (structlog-based): ALREADY EXISTED, verified complete

Added:
- In-memory audit_log on ReactiveEventBus (list[DomainEvent] with defensive copy)
- Emit ordering aligned with specification: RxPY stream push, then audit append,
  then handler dispatch (§Event System)
- Clarified audit_log docstring: volatile in-memory log, not durable SQLite
  persistence (durable persistence wired separately via audit service layer)
- Behave feature: features/observability/event_system_taxonomy.feature (36 scenarios)
- Step definitions: features/steps/event_system_taxonomy_steps.py
- Robot integration test: robot/event_system_taxonomy_integration.robot (13 test cases)
- Robot helper: robot/helper_event_system_taxonomy.py (13 subcommands)
- ASV benchmark: benchmarks/bench_event_bus.py (5 benchmark suites)
- vulture_whitelist.py: added audit_log property

Code review fixes applied:
- Reordered emit() to match spec: on_next → audit_log → handlers (was: audit → on_next → handlers)
- Clarified audit_log docstring to distinguish volatile in-memory log from spec SQLite table
- Fixed benchmark TaxonomyAuditLogSuite: parameterized setup instead of inline construction
- Added teardown to TaxonomyLoggingEmitSuite to restore logging.disable(NOTSET)
- Clear _received list between benchmark iterations to reduce noise
- Catch specific pydantic.ValidationError in frozen model mutation tests
- Consolidated duplicate singular/plural step definitions
- Tightened EventType count threshold from >=30 to >=38

Quality gates:
- lint: PASSED
- typecheck: PASSED (0 errors)
- unit_tests: 36/36 new scenarios PASSED (pre-existing 12 flaky failures unchanged)
- integration_tests: 1483/1483 PASSED
- security_scan: PASSED
- dead_code: PASSED

ISSUES CLOSED: #587
2026-03-26 11:52:52 +00:00
aditya 1e4b6d5be3 fix(acms): address context skill review feedback
Use public ContextTierService fragment accessors in builtin/context handlers and remove the unused query_history scope input so the tool contract matches runtime behavior.
2026-03-26 07:52:09 +00:00
aditya 37b6d27d0b feat(acms): implement builtin/context skill for CRP
Wire the three CRP tool handlers in context_ops.py to the ACMS pipeline
and ContextTierService, replacing NotImplementedError stubs with
functional implementations:

- request_context: Sources fragments from ContextTierService, filters
  by query/focus keywords, and delegates to ACMSPipeline.assemble()
  for budget-constrained context assembly. Accepts optional plan_id
  for actor-context invocation.

- query_history: Searches across hot/warm/cold tiers for fragments
  matching the query string via case-insensitive substring matching.
  Returns results sorted by last-accessed timestamp.

- get_context_budget: Returns current token budget state (max, reserved,
  available, used) computed from ContextBudget defaults and hot-tier
  fragment token counts.

Additionally:
- Register ACMSPipeline as a Singleton in the DI container
- Create build_context_skill_definition() factory for SkillRegistry
  auto-registration of the builtin/context skill
- Replace 3 obsolete NotImplementedError test scenarios with 8 new
  functional BDD scenarios covering all handler paths

Lint, typecheck, and coverage (98%) all pass.

ISSUES CLOSED: #873
2026-03-26 07:52:09 +00:00
hurui200320 414abb1396 fix(cli): add missing --yes flag to plan apply command (#1127)
## Summary

Adds the `--yes`/`-y` flag to the `lifecycle-apply` CLI command as required by the specification (`agents plan apply [--yes|-y] <PLAN_ID>`). Without `--yes`, a confirmation prompt now displays before proceeding with the destructive Apply phase. With `--yes`, the apply proceeds immediately without prompting.

Closes #932

## Changes

### Source Code
- **`src/cleveragents/cli/commands/plan.py`**: Added `yes: Annotated[bool, typer.Option("--yes", "-y", help="Skip confirmation prompt")] = False` parameter to `lifecycle_apply_plan`. Added `typer.confirm()` prompt before the apply operation, consistent with the pattern used by `rollback_plan`, `correct_plan`, and other destructive commands.
  - Confirmation prompt text matches spec exactly: `"Apply changes for plan {plan_id}?"` producing `Apply changes for plan <ID>? [y/N]:`.
  - Fixed redundant plan ID display when `pre_plan` is `None` — now shows `"Apply changes for plan X?"` instead of `"Apply plan X (X)?"`.
  - Added `except ValueError` handler consistent with sibling commands `lifecycle_execute_plan` and `_lifecycle_apply_with_id`.
  - Added `except Exception` catch-all handler with `isinstance(e, (typer.Abort, typer.Exit))` re-raise guard, consistent with `lifecycle_execute_plan`.
  - Moved `PlanPhase` and `ProcessingState` imports to module level per CONTRIBUTING.md §Import Guidelines.

### TDD Tag Removal (Bug Fix Workflow)
- **`features/tdd_plan_apply_yes_flag.feature`**: Removed `@tdd_expected_fail` tag (leaving `@tdd_bug` and `@tdd_bug_932` as permanent regression guards).
- **`robot/tdd_plan_apply_yes_flag.robot`**: Removed `tdd_expected_fail` tag (leaving `tdd_bug` and `tdd_bug_932`).

### Test Updates
Updated all existing `lifecycle-apply` invocations across 17 test/benchmark files to pass `--yes`, since the new confirmation prompt would otherwise abort in non-interactive test environments:
- 9 Behave step definition files
- 3 Robot Framework helper scripts
- 2 Robot Framework e2e acceptance tests
- 3 ASV benchmark files (4 invocations: `cli_robot_flow_bench.py` ×2, `m1_sourcecode_smoke_bench.py` ×1, `plan_cli_smoke_bench.py` ×1)

### Confirmation Prompt Tests (New + Strengthened)
- **`features/tdd_plan_apply_yes_flag.feature`**: 5 scenarios total:
  - `lifecycle-apply recognises the --yes long flag` — verifies flag acceptance, prompt suppression, exit code 0, and `apply_plan` was called
  - `lifecycle-apply recognises the -y short flag` — same as above for short flag
  - `lifecycle-apply without --yes prompts for confirmation and user declines` — verifies `"Apply cancelled."` message, `exit_code == 0`, and `apply_plan` was NOT called
  - `lifecycle-apply without --yes prompts for confirmation and user accepts` — verifies prompt appears, `exit_code == 0`, and `apply_plan` was called
  - `lifecycle-apply catches unexpected exceptions cleanly` — verifies `"Unexpected error"` output, no traceback leak, non-zero exit code (exercises the `except Exception` catch-all)
- **`features/steps/tdd_plan_apply_yes_flag_steps.py`**: Refactored step definitions:
  - `_make_mock_plan` uses `PlanPhase` and `ProcessingState` enum types instead of raw strings
  - `_make_mock_plan` uses `datetime.now(tz=UTC)` instead of timezone-naive `datetime.now()`
  - Unified prompt suppression step handles both `--yes` and `-y` via parameterised step pattern
  - Added `When` step for unexpected error scenario with `RuntimeError` side_effect
  - Added `Then` step for non-zero exit code assertion
- **Feature/Robot documentation**: Updated stale descriptions that said "implementation does not accept --yes" to reflect the flag is now implemented.

### Documentation
- **`docs/reference/plan_cli.md`**: Updated `lifecycle-apply` section with:
  - `### Synopsis` heading with code block
  - `### Options` table listing `--yes/-y` and `--format/-f` flags
  - `### Arguments` table listing `PLAN_ID`
  - Matches the style used by other command sections in the same file

## Review Fixes (Cycle 3 — Luis's review)

| ID | Severity | Issue | Resolution |
|----|----------|-------|------------|
| M1 | Medium | `typer.Abort()` on user decline produces exit code 1 and redundant "Aborted." | Changed to `raise typer.Exit(0)` — consistent with `correct_decision` and legacy `apply` |
| M2 | Medium | Missing exit code assertion on decline scenario | Added `And the lifecycle-apply exit code should be 0` to the decline scenario |
| M3 | Medium | Spec compliance: "summary of pending changes" not implemented | Deferred — spec example shows summary *after* confirmation, not before; implementation matches spec. Ticket-vs-spec ambiguity noted. |
| L1 | Low | Missing `except Exception` catch-all handler | Added catch-all matching `lifecycle_execute_plan` pattern; re-raises `typer.Abort`/`typer.Exit` |
| L2 | Low | Documentation description not updated | Expanded description in `plan_cli.md` to explain confirmation prompt and `--yes` |
| L3 | Low | Dead code `is not None` guards | Removed both guards — `get_plan()` raises `NotFoundError`, never returns `None` |
| I1 | Info | Duplicate `PlanPhase` import | Hoisted import to top of `try` block, eliminating duplicate at old line 2087 |
| I2 | Info | `typer.confirm` without explicit `default=False` | Added `default=False` for consistency with sibling commands |
| L4 | Low | No test for `--yes` after positional arg | Not addressed — Typer/Click handles both orderings; low risk |
| L5 | Low | No test for auto-select + interactive prompt | Not addressed — separate concern outside ticket scope |
| I3 | Info | Robot helper only tests flag recognition | By design — noted as informational |

## Review Fixes (Cycle 4 — Self-QA)

| ID | Severity | Issue | Resolution |
|----|----------|-------|------------|
| Major-1 | Major | No test for `except Exception` catch-all handler | Added new scenario `"lifecycle-apply catches unexpected exceptions cleanly"` with `RuntimeError` side_effect; asserts `"Unexpected error"` output, no traceback, non-zero exit |
| Minor-2 | Minor | Missing `ValueError` handler inconsistent with siblings | Added `except ValueError as e:` with `"[red]Execution Error:[/red]"` before catch-all, matching `lifecycle_execute_plan` and `_lifecycle_apply_with_id` |
| Minor-3 | Minor | Flag scenarios don't verify `apply_plan` called | Added `And the lifecycle-apply should have called apply` to both `--yes` and `-y` scenarios |
| Minor-4 | Minor | Stale docstring in Robot helper references `tdd_expected_fail` inversion | Updated to reflect bug is fixed and tests serve as regression guards |
| Minor-5 | Minor | `plan_cli.md` lacks Options table for `lifecycle-apply` | Added Synopsis, Options, and Arguments sections matching sibling command style |
| Nit-6 | Nit | Duplicated step defs for `--yes` vs `-y` prompt suppression | Unified into single parameterised step `"the lifecycle-apply {flag} output should not contain the confirmation prompt"` |
| Nit-7 | Nit | `datetime.now()` timezone-naive | Changed to `datetime.now(tz=UTC)` |
| Nit-8 | Nit | `_make_mock_plan` params use `str` instead of enum types | Changed to `PlanPhase` and `ProcessingState` enum types |

## Review Fixes (Cycle 5 — Jeff's approval note)

| ID | Severity | Issue | Resolution |
|----|----------|-------|------------|
| Import-1 | Minor | `PlanPhase`/`ProcessingState` imports inside function body instead of module level | Moved to module-level import per CONTRIBUTING.md §Import Guidelines |

## Known Limitations / Deferred Items

- **M3: Ticket AC mentions "summary of pending changes"** but the spec example only shows `"Apply changes for plan <ID>? [y/N]: y"` without a change summary. The implementation shows plan ID only (matching the spec), not a change summary. This is a ticket-vs-spec ambiguity; recommend discussing with ticket author.
- **Legacy `apply` command** accepts `--yes` but does not pass it to `_lifecycle_apply_with_id()`. This is a pre-existing issue outside the scope of this ticket.
- **`pre_plan is None` branch** has no explicit test. Pre-existing architectural issue; no action taken.

## Quality Gates

| Gate | Result |
|------|--------|
| `nox -s lint` |  passed |
| `nox -s typecheck` |  passed (0 errors) |
| `nox -s unit_tests` |  passed (471 features, 12,424 scenarios, 0 failures) |
| `nox -s integration_tests` |  passed (1,727 tests, 0 failures) |
| `nox -s e2e_tests` |  passed (41 tests, 0 failures) |
| `nox -s coverage_report` |  passed (≥97% coverage) |

Reviewed-on: cleveragents/cleveragents-core#1127
Reviewed-by: Jeffrey Phillips Freeman <jeffrey.freeman@cleverthis.com>
Co-authored-by: Rui Hu <rui.hu@cleverthis.com>
Co-committed-by: Rui Hu <rui.hu@cleverthis.com>
2026-03-26 07:50:09 +00:00
aditya d8d08facde feat(acms): budget enforcement with max_file_size and max_total_size constraints
Implements byte-size budget enforcement for the ACMS context assembly
pipeline.  enforce_size_budget() filters context fragments against
max_file_size (per-fragment) and max_total_size (cumulative) limits
defined in a ContextView.  New domain models BudgetViolation and
BudgetEnforcementResult provide structured violation reporting.

Pipeline integration in ACMSPipeline.assemble() applies enforcement
as a pre-filter when a context_view is provided.

Review feedback addressed:
- Fixed PR milestone to v3.4.0 (matching ticket #847)
- Rebased branch onto latest master
- Added CHANGELOG.md entry
- Extracted duplicated enforcement logic into _apply_budget_enforcement()
  shared method on ACMSPipeline, called by both parent and subclass
- Fixed _make_fragment return type from object to ContextFragment
- Moved all imports to top of files (acms_pipeline.py, steps file)
- Added errors="replace" to encode("utf-8") for surrogate safety
- Documented thread-safety caveat on last_enforcement_result property
- Added short-circuit early return when both limits are None
- Added multi-byte Unicode content test scenario
- Added total_size assertions to key scenarios
- Added violation type verification to mixed limits scenario
- Merged duplicate singular/plural step definitions
- Added edge case scenarios (empty list, all exceed, exact boundary)
- Fixed misleading docstring in ContextAssemblyPipeline.assemble()
- Re-exported VALID_PHASES through core __init__.py public API

ISSUES CLOSED: #847
2026-03-26 07:14:43 +00:00
aditya 42a32c2709 fix(acms): align budget allocation formula and protocol signatures with spec
Align ACMS pipeline protocol signatures and allocation formula with the
specification (docs/specification.md §42630-42937):

- BudgetAllocator.allocate() now accepts optional request: ContextRequest
  parameter per BudgetAllocatorProtocol spec (§44754-44766), enabling
  future request-aware allocation strategies.

- Allocation formula changed from proportional to confidence alone to
  proportional to confidence * quality_score per spec §45003. Both
  DefaultBudgetAllocator and ProportionalBudgetAllocator use the new
  weighted formula. With default quality_score=1.0, behavior is backward-
  compatible.

- DetailDepthResolver.resolve() now accepts budget: int parameter per
  DetailDepthResolverProtocol spec (§44803-44814), enabling future
  budget-aware depth resolution decisions.

- Default packer changed from no-op DefaultBudgetPacker to production
  GreedyKnapsackPacker per spec §45013. ACMSPipeline uses lazy import
  to avoid circular dependency; ContextAssemblyPipeline imports directly.

- Added quality_score field to service-layer StrategyCapabilities
  (already present in domain model).

- Updated 5 feature scenarios and 1 Robot helper where GreedyKnapsackPacker
  relevance-based ordering changed fragment output order vs. the old no-op
  packer. Fixed by adjusting test fragment relevance scores to preserve
  expected ordering.

ISSUES CLOSED: #924
2026-03-26 06:19:14 +00:00
Luis Mendes 34c2acc354 fix(acms): implement context tier runtime promotion/demotion/eviction
Implement the missing runtime logic for the ACMS context tier service.
Previously only data models and manual promote()/demote()/evict_lru()
methods existed. This commit adds:

- Auto-promotion on access: get() now promotes fragments one tier up
  when access_count reaches the configurable promotion_threshold
  (default: 5 accesses).  The access counter resets after each
  successful promotion so fragments must accumulate fresh accesses
  before the next tier transition.
- Staleness enforcement: new enforce_staleness() method demotes hot
  fragments older than hot_ttl (default: 24h) to warm, and warm
  fragments older than warm_ttl to cold. A snapshot of existing
  warm-tier IDs prevents double-demotion in a single pass.
- Budget enforcement on store and promote: store() and promote()
  now enforce TierBudget.max_tokens_hot by evicting LRU hot-tier
  fragments until the token budget is met.  The eviction loop uses
  incremental token tracking to avoid recomputing the sum.
- Event emission: Added TIER_PROMOTED, TIER_DEMOTED, TIER_EVICTED
  event types to EventType enum. All tier transitions emit
  DomainEvent instances through the optional EventBus.
- Configuration: Added context_tier_promotion_threshold,
  context_tier_hot_ttl_hours, context_tier_warm_ttl_hours settings.
  The warm TTL setting also accepts the spec-defined env var
  CLEVERAGENTS_CTX_WARM_HOURS as an alias.
- DI wiring: container.py now injects event_bus into
  context_tier_service.

Review fixes applied (code review on PR #1150):
- C1: Reset access_count to 0 after each auto-promotion to prevent
  chain promotion that bypassed the warm tier.
- C2: Call _enforce_hot_budget() inside promote() warm-to-hot path
  so auto-promoted fragments respect the token budget.
- H1: Corrected _enforce_hot_budget() docstring: actual complexity
  is O(n + n*k) not O(n), since min() scans remaining entries on
  each eviction.
- M1: Added CLEVERAGENTS_CTX_WARM_HOURS as an additional env var
  alias for context_tier_warm_ttl_hours per specification line 30555.

Review fixes applied (second code review on PR #1150):
- B-CRIT-1: Fixed data loss in promote() warm-to-hot: emit
  TIER_PROMOTED before _enforce_hot_budget(), and if the promoted
  fragment is evicted by budget, restore it to the warm tier instead
  of silently losing it.
- B-HIGH-1: Fixed self-eviction on store(): fragments whose
  token_count exceeds the entire hot-tier budget are now redirected
  to the warm tier with a warning log.
- B-MED-1: Wrapped _emit_tier_event() in try/except so a failing
  event bus does not break tier operations (best-effort emission).
- B-MED-2: Fixed event ordering so TIER_PROMOTED fires before any
  budget-triggered TIER_EVICTED events.
- D-LOW-1: Fixed type hint in Robot helper (dict[str, Callable]).
- D-LOW-2: Added __all__ export to context_tiers.py.
- S-LOW-1: Added thread-safety docstring note to ContextTierService.

Review fixes applied (third code review on PR #1150):
- B-MED-1: Added TIER_DEMOTED event emission for oversized fragment
  redirect in store(), closing the observability gap where the only
  tier transition without event emission was the hot-to-warm redirect
  for fragments exceeding the entire hot-tier budget.
- S-LOW-1: Added CLEVERAGENTS_CTX_HOT_HOURS as an additional env var
  alias for context_tier_hot_ttl_hours, for consistency with the
  warm-tier alias CLEVERAGENTS_CTX_WARM_HOURS.
- S-LOW-2: Added docstring note to enforce_staleness() reconciling
  the hot-tier TTL with the specification statement that hot-tier
  retention is "Until resource removed" (TTL controls tier placement,
  not data retention).

Review fixes applied (fourth code review on PR #1150):
- B-HIGH-1: Reset access_count to 0 on demotion so that demoted
  fragments must accumulate fresh accesses before re-promotion.
  Without this reset, a previously popular fragment whose
  access_count already exceeded the promotion threshold would be
  re-promoted on the very next get() call, making staleness
  enforcement ineffective.

Review fixes applied (freemo APPROVED review on PR #1150):
- #1: Removed all # type: ignore annotations from test files.
  Fixed _EventCollector, _FailingBus, and _NullBus subscribe()
  signatures to use Callable[[DomainEvent], None] matching the
  EventBus protocol.  Replaced dict-spread TieredFragment construction
  with explicit keyword arguments and post-construction assignment.
- #2: Extracted runtime policy logic (enforce_staleness,
  _maybe_auto_promote, _re_fetch_after_promotion, _enforce_hot_budget,
  _emit_tier_event) into TierRuntimeMixin in tier_runtime.py to
  reduce context_tiers.py toward the 500-line guideline.
- #3: Added fragment_id non-empty validation guard to promote() and
  demote() per CONTRIBUTING.md argument validation policy.
- #8: Renamed _resolve to _re_fetch_after_promotion for clarity.

Removed @tdd_expected_fail from TDD tests (Behave + Robot) as the
bug is now fixed. All 3 TDD scenarios pass normally.

Tests: 27 Behave scenarios (24 feature + 3 TDD), 4 Robot integration
tests, 4 ASV benchmark suites.

ISSUES CLOSED: #821
2026-03-25 17:49:06 +00:00
freemo 06130212ed test(e2e): workflow example 5 — database schema migration with safety nets (review profile) (#816)
## Summary

E2E test for Workflow Example 5 — database schema migration with safety nets using the **review** automation profile. Exercises the full spec-aligned workflow:

- **Custom resource type registration** via `resource type add --config` (postgres-db type with `transaction_rollback` sandbox strategy, `--host`, `--port`, `--database`, `--schema` CLI args with flat `type`/`default` fields per `ResourceTypeArgument` schema)
- **Custom resource instantiation** — attempts `resource add` with the custom type to exercise mixed resource types, followed by `project link-resource` to link DB resource to the project
- **Custom skill creation** with spec-aligned database tools: `local/query_db` (read-only), `local/execute_migration` (writes, checkpointable), `local/backfill_column` (writes, checkpointable) — registered via `skill add --config`, with namespaced tool reference names per `SkillToolRefSchema` validation
- **Action creation** with `automation_profile: review`, `reusable: true`, `state: available`, spec invariants, and typed `arguments` section (`table_name`, `column_name`, `column_type`, `backfill_source` — all required per spec, using `arguments` field per `ActionConfigSchema`)
- **Plan use** with `--arg` flags exercising parameterized action invocation including `backfill_source=audit_log`, plus **explicit `--automation-profile review`** flag (action-to-plan profile propagation is not yet wired in `PlanLifecycleService.use_action`)
- **Phased child plan verification** via `plan tree --format json` with `decision_count >= 2` hard assertion on framework decisions plus WARN tiers for LLM decomposition quality (`< 3`, `< 5`)
- **Plan phase assertion** — hard assertion that phase is populated after execute
- **Checkpoint-based rollback** with hard assertions: `rc=0` on rollback success, `rc!=0` on fake checkpoint, None guard for JSON null checkpoint IDs, re-execute with Traceback/INTERNAL checks on success and explanatory comment on failure path
- **Plan diff** with hard `rc=0` assertion and content-signal verification
- **Migration content verification** — baseline SHA saved before apply, diff against baseline (not `HEAD~1`), WARN-level check on migration keywords (`last_login`, `schema`, `migration`, `column`, `alter`) — flexible per LLM non-determinism
- **Commit count** assertion `>= 2` (fixture baseline: Create Temp Git Repo + DB fixture commit), WARN if no additional commits from lifecycle-apply
- **Backfill evidence** WARN-level check in plan tree/execution output (`backfill`, `batch`, `populate`, `last_login`) with explanatory comment noting tree covers decomposition plan
- **Combined AC #6 gate** — if *both* migration content *and* backfill evidence are absent, explicit WARN visibility for CI debugging
- **Terminal state assertion** after `lifecycle-apply` — `plan status` call verifies phase/processing_state reflects terminal or apply-progress outcome
- **Automation profile fallback verification** — if `plan use` output omits `automation_profile`, falls back to `plan status` for secondary verification (hard assertion always runs)
- **Traceback and INTERNAL checks** on all CLI commands (resource add, project create, resource type add, skill add, action create, plan use, strategize, execute, plan tree, plan status, plan diff, plan rollback, re-execute after rollback, lifecycle-apply) including custom resource error paths
- **Dynamic actor selection** — detects available API keys (Anthropic/OpenAI) at suite setup
- **Skip If No LLM Keys** guard for graceful CI degradation
- **Test-level teardown** with diagnostic logging for both plan status and plan tree on failure
- **30-minute timeout** covering worst-case rollback+re-execute path
- **Force Tags** for consistency with `m6_acceptance.robot`
- **Timeout parameters** (`timeout=60s on_timeout=kill`) on all local `Run Process` git commands
- **Sequential section numbering** (1 through 15) for readability

Closes #751

ISSUES CLOSED: #751

## Approach

Follows the patterns established by `m6_acceptance.robot` and `m2_acceptance.robot`:
- `WF05 Suite Setup` initialises the workspace, generates a unique run suffix, and detects available LLM API keys
- `Safe Parse Json Field` from `common_e2e.resource` for JSON field extraction with None guards for JSON null values
- All CLI commands use `--format json` for predictable, parseable output
- `expected_rc=None` with explicit `Should Be Equal As Integers` for detailed failure messages
- Hard assertions on infrastructure/framework behavior (CLI commands, phase transitions, tool registration)
- WARN-level assertions on LLM-dependent output (decision decomposition, migration content, backfill evidence, commit count) — per ticket requirement "output validation is flexible"
- Traceback and INTERNAL checks on all CLI commands following `m2_acceptance.robot` pattern
- Baseline SHA approach for post-apply diff verification eliminates false positives from fixture commits

## Bug Fix: LifecyclePlanRepository.update() UNIQUE Constraint Violation

**Root cause**: `LifecyclePlanRepository.update()` called `clear()` on child relationship collections (project_links, arguments, invariants) followed by `append()` with new items, but only flushed at the end. SQLAlchemy's default operation ordering can emit INSERTs before DELETEs within the same flush, causing `UNIQUE constraint failed: plan_arguments.plan_id, plan_arguments.name` when plans have arguments.

**Fix**: Group all three `clear()` calls together and flush them before appending new rows. This ensures the DELETEs are committed before any INSERTs, preventing the UNIQUE constraint violation.

**Impact**: This was a latent bug affecting ALL plans with arguments when `update()` is called. Previously undetected because existing E2E tests (M1, M2, M5, M6) create plans without `--arg` flags.

## Review Fixes (addressing medium findings from @CoreRasurae review)

| # | Finding | Fix |
|---|---------|-----|
| **BUG-1** | No regression test for UNIQUE constraint fix | Added targeted BDD scenario in `repositories_coverage_boost.feature` — creates plan with argument `x=v1`, updates to `x=v2`, asserts no `IntegrityError` |
| **TEST-1** | AC #4 weakened — fragile string counting | Replaced raw `count('"decision_id"')` with proper JSON parsing via `json.loads()`, recursive tree walking for decision counting, structural `children_key_count` and `child_link_count` verification |
| **TEST-2** | AC #5 conditionally tested | Added explicit WARN log when no checkpoint_id is present ("AC #5 visibility"); fake checkpoint test now runs unconditionally (moved outside IF/ELSE) with Traceback/INTERNAL checks |
| **TEST-3** | No terminal state assertion after lifecycle-apply | Added `plan status` call after apply with phase/processing_state extraction; hard assertion on terminal state or apply-phase progress |
| **TEST-4** | AC #6 migration/backfill WARN-only | Added combined gate (`has_ac6_evidence`): if *both* migration and backfill evidence are absent, explicit WARN for CI visibility. WARN-only is intentional per ticket AC "output validation is flexible" |
| **TEST-5** | Automation profile silently skipped | Added fallback to `plan status --format json` when `plan use` output omits `automation_profile`; hard assertion (`Should Be Equal As Strings review`) now always executes |
| **TEST-8** | Missing Traceback/INTERNAL on custom resource error paths | Added Traceback/INTERNAL checks inside both `resource add` and `project link-resource` ELSE branches with `NoSuchOption` guard |

## Quality Gates

- `nox -e lint` 
- `nox -e typecheck`  (0 errors)
- `nox -e unit_tests`  (471 features, 12,422 scenarios, 0 failures)
- `nox -e integration_tests`  (1,727 tests, 0 failures)
- `nox -e e2e_tests`  (42 tests, 42 passed, 0 failed)
- `nox -e coverage_report`  (98%, meets threshold)

## Manual Verification

### Prerequisites
- `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` environment variable set

### Commands

```bash
nox -e e2e_tests
# Or run just this suite:
python -m robot --outputdir build/reports/robot --include E2E robot/e2e/wf05_db_migration.robot
```

Reviewed-on: cleveragents/cleveragents-core#816
Reviewed-by: Luis Mendes <luis.mendes@cleverthis.com>
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
2026-03-25 13:05:04 +00:00
aditya c082c8f022 fix(cli): bypass migration prompt entirely when --yes flag is passed
Make require_confirmation configurable on UnitOfWork so that when
the --yes flag is passed to agents init, the migration runner skips
the confirmation prompt entirely rather than calling a prompt that
always returns True.  The previous approach (injecting a prompt
callback that auto-approves) was a workaround that left the
require_confirmation=True hardcoded in _ensure_database_initialized,
meaning the prompt code path was still exercised unnecessarily.

Changes:
- Add require_confirmation parameter to UnitOfWork constructor
  (default True for backward compatibility)
- Pass self._require_confirmation to MigrationRunner.init_or_upgrade
  instead of hardcoded True
- Update init_command to pass require_confirmation=False when --yes
  is set, with the prompt callback retained as belt-and-suspenders

The TDD bug-capture tests from #842 (features/tdd_init_yes_no_input.feature,
robot/tdd_init_yes_no_input.robot) now run as normal regression tests
with @tdd_expected_fail already removed.

All nox quality gates pass:
- lint, typecheck: clean
- unit_tests: 12230 scenarios passed
- integration_tests: all passed
- coverage_report: 98.38% (threshold >97%)

ISSUES CLOSED: #783
2026-03-25 11:28:06 +00:00
Luis Mendes 3cf3f1f69e feat(validation): implement Fix-then-Revalidate orchestration loop for required validations
Implemented FixThenRevalidateOrchestrator with full diagnosis → self-fix →
re-validation → retry limit → strategy revision → user escalation → terminal
failure flow. Added retry counting per validation per plan, configurable retry
limit (default 3), auto_strategy_revision flag support, and validation_fix_history
recording in plan execution metadata.

Review fixes applied (round 1):
- Added try/except around fix_callback and revalidate_callback (spec: validation
  errors treated as required failure regardless of mode)
- Wired optional EventBus for VALIDATION_FIX_ATTEMPTED/SUCCEEDED/EXHAUSTED events
- Fixed total_attempts derivation from fix_history length instead of cumulative
  retry counts
- Updated module docstring to reflect implemented vs caller-responsible steps
- Added exhausted-retry logging on re-invocation
- Replaced bare assert in Robot helper with explicit _check() function
- Added 10 new Behave scenarios covering exception paths, multi-failure, reset,
  field validation, boundary values, event_bus property, and exhausted re-invocation

Review fixes applied (round 2 — PR #711):
- B1+B7: Fixed TOCTOU race in _fix_single_validation; atomic claim-per-iteration
  under RLock; eliminated defaultdict auto-vivification with .get() reads
- B2: Added validation_name mismatch check on revalidate_callback return
- B3: Added fix_description truncation to 2000 chars before FixAttemptRecord
- T1: Switched threading.Lock to threading.RLock for defensive reentrancy
- A1: Added model_validator preventing escalated+terminal_failure both True
- R1: Added max_length=255 to FixAttemptRecord.validation_name
- S7: Fixed misleading docstring about automation_profile integration
- D4: Fixed timestamp field description to specify UTC
- D1: Renamed misleading benchmark time_fix_after_two_retries
- Added 5 new Behave scenarios for truncation, model constraints, name mismatch

ISSUES CLOSED: #583
2026-03-24 22:01:48 +00:00