forked from cleveragents/cleveragents-core
feature/m7-postgresql-backend
612 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ac476fd6ff | fix: rename cls to klass in _validate_protocol staticmethod for pyright compliance | ||
|
|
721ece458c |
fix(server): address PostgreSQL backend review findings
- Move psycopg2-binary to optional [server] extra with lazy import - Replace silent fallback PostgreSQL URL with ValueError - Add TODO for wiring resolve_database_url into engine paths - Add pool-parameter source comment in UnitOfWork - Add read-only properties for pool params in UnitOfWork - Add UUID/INTERVAL/ARRAY/JSONB to portable type allowlist - Add development-only warning to Docker Compose credentials - Update BDD scenario for new ValueError behavior |
||
|
|
301050fa5c |
feat(server): implement PostgreSQL storage backend for server mode
Add PostgreSQL support as the server-mode storage backend alongside existing SQLite for local mode. Verify all ORM models are dialect- agnostic, configure connection pooling for multi-user access, add Docker Compose for local PG development, and wire database URL selection based on deployment mode. Changes: - Add psycopg2-binary dependency to pyproject.toml - Add server_mode, db_pool_size, db_max_overflow, db_pool_recycle settings to Settings with environment variable support - Add resolve_database_url() and is_postgresql() to Settings for mode-aware database URL resolution - Configure UnitOfWork engine creation with pool_size, max_overflow, pool_recycle, and pool_pre_ping for PostgreSQL connections - Update MigrationRunner to handle both SQLite and PostgreSQL backends - Add compare_type=True to Alembic env.py for dialect-aware migrations - Add docker-compose.yml with PostgreSQL 16-alpine for local development - Add Behave BDD feature (14 scenarios) covering settings, pool config, engine creation, ORM dialect compatibility, and migration runner - Add Robot Framework integration tests (12 test cases) for the abstraction layer with requires_postgresql tag for live PG tests ISSUES CLOSED: #878 |
||
|
|
93da31e80f |
fix(skill): resolve skill add persistence regression after PR #640
Root cause: _build_skill_service in container.py created a SkillRepository pointing at the database but did not ensure the skills/skill_items tables existed. When the tables were missing, SkillRepository.list_all() and create() failed silently (caught by SkillService._load_from_db and _persist_skill exception handlers), causing the service to operate in in-memory-only mode. Skills added in one CLI process were lost when a new process created a fresh SkillService. Additionally, SkillRepository lacked auto_commit support. Each call to session_factory() returned a new session, so the flush in create/update/ delete operated on a different session than the commit in SkillService._commit(), meaning data was never actually persisted even when the tables existed. Fix: 1. Add targeted table creation in _build_skill_service (following the pattern in _build_session_service) — checks for missing skills and skill_items tables and creates them via Base.metadata.create_all. 2. Add auto_commit parameter to SkillRepository (following the pattern in SessionRepository) so each mutating method commits and closes its own session. 3. Pass auto_commit=True from the container builder. 4. Remove @tdd_expected_fail from TDD test (leaving @tdd_bug and @tdd_bug_980 as permanent regression guards). ISSUES CLOSED: #980 |
||
|
|
a6e2bc7842 |
test: add TDD bug-capture test for #980 — skill add cross-process persistence
Write cross-process Behave and Robot tests capturing skill add persistence regression. Tests use subprocess invocations to verify skills persist across CLI process boundaries. The existing persistence tests (skill_add_persist.feature) verify round-trip within the same Python process by creating two SkillService instances sharing the same in-memory database. This approach cannot detect the cross-process regression where _build_skill_service falls back to in-memory storage because the skills table does not exist in the database created by agents init. Behave: features/tdd_skill_add_regression.feature Robot: robot/tdd_skill_add_regression.robot Tags: @tdd_bug, @tdd_bug_980, @tdd_expected_fail ISSUES CLOSED: #981 |
||
|
|
90e5bbb99c |
feat(resource): implement ResourceHandler CRUD and discovery methods
Extend the ResourceHandler protocol with six content operations (read, write, delete, list_children, diff, discover_children) and four frozen dataclass result types (Content, WriteResult, DeleteResult, DiffResult). Handler implementations: - GitCheckoutHandler: read via git show (binary-safe), write/delete via filesystem ops, list via git ls-tree, diff via git diff --no-index with locale-safe shortstat parsing, discover via git ls-tree -d - FsDirectoryHandler: full CRUD via pathlib/os/difflib/shutil - DevcontainerHandler: read/write/discover via devcontainer exec - CloudResourceHandler: NotImplementedError stubs for protocol compliance - DatabaseResourceHandler: inherits base NotImplementedError stubs Security: - Path traversal guard (_safe_resolve) on all read/write/delete ops using os.sep-suffixed startswith check to prevent prefix collisions - Empty-path deletion rejected with PermissionError Tests: - 22 Behave scenarios (115 steps): CRUD for FsDirectory and GitCheckout, path traversal rejection (3 scenarios), NotImplementedError defaults - 2 Robot integration tests: read -> write -> diff cycle on real temp directories and git repos ISSUES CLOSED: #827 |
||
|
|
65a2e4db76 |
feat(resource): add LSP resource types
Add 4 LSP-related built-in resource types to the resource registry: - executable: system binary/interpreter/LSP server binary with auto-discovery from container-exec-env and fs-directory (lazy) - lsp-server: LSP server definition with command, language-ids, transport, args, port, initialization-options config; children: lsp-workspace - lsp-workspace: workspace root tracked by LSP server, auto-discovered from lsp-server; children: lsp-document; not user-addable - lsp-document: text document tracked by LSP server, auto-discovered from lsp-workspace; read+write capabilities; not user-addable Type definitions extracted to _resource_registry_lsp.py for consistency with existing type modules. Parent/child hierarchy: lsp-server -> lsp-workspace -> lsp-document. YAML configs with ADR references (ADR-039, ADR-040). All 7 lsp-server CLI args per ADR-040. Behave tests (21 scenarios): YAML loading, user-addable flags, capabilities, parent/child hierarchy, auto-discovery for all 3 discoverable types, BUILTIN_NAMES, DB bootstrap roundtrip, negative tests for manual registration rejection. Robot tests (6 tests): import, BUILTIN_NAMES, DB roundtrip, hierarchy, auto-discovery, user-addable guard. ISSUES CLOSED: #832 |
||
|
|
4ff075e0da |
feat(lsp): implement functional LSP runtime
Replace local-mode stubs with real LSP protocol support: - StdioTransport: subprocess management with JSON-RPC framing - LspClient: LSP protocol (initialize/shutdown/diagnostics/completions) - LspLifecycleManager: reference-counted instances, health checks, crash restart - LspRuntime: registry-based server lookup, auto-restart on crash - LspToolAdapter: runtime-delegating handlers with local-mode fallback - LanguageDiscovery: 4-layer detection (extension, shebang, UKO, project) - activate_bindings/deactivate_bindings: actor compiler LSP binding wiring Tests: 27 Behave scenarios, 6 Robot integration tests, 250 existing pass. ISSUES CLOSED: #826 |
||
|
|
36c36bc0ee |
test(cli): add regression-guard tests for Container.resolve() crash
Add Behave and Robot Framework regression tests for bug #647, where plan tree, plan explain, and plan correct CLI commands crashed with AttributeError when resolving DecisionService from the DI container. Tests use a real DI container with seeded decisions (not MagicMock) to catch the exact class of bug that existing M3 tests missed. Assertions verify successful execution and command-specific output content. Includes Settings.reset() classmethod for robust singleton cleanup in test teardown. Review feedback addressed (hurui200320 Round 5): - Fixed Behave step engine leak by capturing UoW in cleanup closure - Removed dead @then decorator; renamed to private _assert_command_succeeded - Strengthened plan correct assertions with revert/dry-run content checks - Updated misleading get_container() comment to reflect singleton warming - Added test-only warning to Settings.reset() docstring - Added type annotations to 4 settings step functions - Fixed CONTRIBUTORS.md alphabetical ordering and removed duplicate entry - Replaced glob.glob with pathlib suffix iteration in Robot helper - Fixed feature description line break for readability - Removed redundant TYPE_CHECKING import for Decision ISSUES CLOSED: #648 |
||
|
|
9e316b1a3e |
fix(domain): align plan lifecycle model validation with specification
Aligned the plan lifecycle model with the specification: 1. ERRORED is now treated as terminal in is_terminal property, matching the spec table where errored is marked "Terminal? Yes" for all processing phases. 2. Added per-phase state validation via model_validator: APPLIED and CONSTRAINED are only valid in APPLY phase; COMPLETE is only valid in STRATEGIZE or EXECUTE phases. Invalid combinations now raise ValueError at construction time. 3. Updated ProcessingState.COMPLETE docstring to clarify phase-level terminality semantics. 4. Fixed assignment ordering in execute_plan() to set processing_state before phase, consistent with the state-first pattern used in apply_plan() and _perform_reversion(). 5. Added defensive coercion in LifecyclePlanModel.to_domain() to handle legacy DB rows with invalid phase/state combinations (e.g. APPLY/COMPLETE -> APPLY/APPLIED) with warning-level logging for observability. 6. Updated module docstrings: ERRORED description now reflects terminal semantics, terminal outcomes location clarified for all phases, can_revert_to docstring notes ERRORED/CONSTRAINED are terminal but revertable, is_terminal docstring explains the distinction between terminal and permanently irrecoverable and documents why COMPLETE is not plan-terminal despite the spec marking it "Terminal? Yes" (phase-level vs plan-level). 7. Updated PlanResumeService.validate_eligibility() docstring to reflect that ERRORED is now terminal but still eligible for resume. 8. Added CHANGELOG entry. ISSUES CLOSED: #918 |
||
|
|
bd18491b8d |
fix(test): replace shell=True with shell=False in cli_coverage_steps.py subprocess call
Replaced shell=True with shell=False and shlex.split() for command tokenization in cli_coverage_steps.py, consistent with the pattern already used in cli_plan_context_commands_steps.py. Audited all step files for additional shell=True usages. ISSUES CLOSED: #734 |
||
|
|
1ffb0fddbe |
feat(plan): decision correction recomputes only affected subtree
Enhanced the CorrectionService to properly isolate correction scope:
1. analyze_impact() now populates excluded_decisions by computing
the set difference between all plan decisions and the affected
subtree, ensuring root and sibling decisions are tracked.
2. Added rollback_tier computation that counts parent hops from
the target decision to the root, enabling depth-aware rollback
strategies (tier 0 = root targeted, tier N = N levels deep).
3. Enhanced dry-run report with excluded decisions list, rollback
tier, and tier-0 warning when entire tree is affected.
4. Added subtree isolation validation confirming root exclusion
and sibling non-contamination for non-root corrections.
5. Fixed status state-machine regression in execute_revert() where
analyze_impact() overwrote status back to ANALYZING from EXECUTING.
Guard now only transitions to ANALYZING when status is PENDING.
execute_revert() now transitions through ANALYZING before EXECUTING
for correct lifecycle ordering.
6. Fixed validate_subtree_isolation() to use structural-only BFS for
the sibling invariant check, so that influence-DAG-caused sibling
reachability is not misreported as an isolation violation (per spec
§ Affected Subtree Computation).
7. Fixed false-positive cycle-detection warnings from convergent
(diamond) topologies by replacing per-node seen_this_round with a
global enqueued set in BFS, preventing duplicate queue insertions
from different parents.
8. Added dry_run enforcement guard in _assert_executable() to prevent
execution of dry-run-only corrections per spec (§ plan correct
--dry-run: "Show impact without executing").
9. Fixed generate_dry_run_report() to preserve request status so that
generating a preview does not advance the correction lifecycle
(dry-run is non-mutating per spec). Status restoration now uses
try/finally to guarantee recovery even when analyze_impact() raises
after transitioning the status.
10. Improved cycle-detection log message accuracy to cover both
structural tree and influence DAG sources.
11. Extracted cost/time estimation constants (_COST_PER_DECISION,
_RECOMPUTE_SECONDS_PER_DECISION) from magic numbers.
12. Added terminal-state guard in analyze_impact() to reject
re-analysis after execution (APPLIED/FAILED/CANCELLED/REJECTED),
preventing audit-data corruption. Promoted terminal-status set
to a module-level _TERMINAL_STATUSES frozenset constant.
13. Added mode validation in execute_revert()/execute_append() to
prevent mode-mismatched execution (e.g. calling execute_revert
on an APPEND correction).
14. Fixed _collect_all_decisions() to always include the target
decision in the universe, preventing broken partition invariant
for isolated single-node plans.
15. Fixed tier-0 dry-run warning to only trigger when the target
is genuinely in the structural tree (avoiding false warnings for
nodes not present in the tree).
16. Removed duplicate as_cli_dict regression scenarios in
resource_type_deferred_physical.feature.
ISSUES CLOSED: #845
|
||
|
|
a2113deace |
fix(a2a): suppress stdout/stderr in facade bootstrap to prevent test pollution
The _notify_facade and _facade_dispatch functions call get_container() during lazy facade construction. This can trigger structlog output that corrupts CLI stdout captured by CliRunner in tests. Wrap facade construction in redirect_stdout/redirect_stderr to suppress any side-effect output. Also reset the facade singleton in after_scenario for test isolation. |
||
|
|
4f2aa4189c |
fix(test): guard result.stderr access against ValueError
Click/Typer CliRunner.Result.stderr is a property that raises ValueError when stderr was not separately captured (mix_stderr=True is the default). Wrap all result.stderr accesses in try/except to handle this gracefully. |
||
|
|
0c301ac581 |
fix(test): update facade operation count from 11 to 42
The A2A facade now exposes 42 operations (31 extension + 11 legacy) after the spec-aligned _cleveragents/ extension methods were added. Update the BDD assertion and docs to match the actual count. |
||
|
|
24aad463a1 |
feat(a2a): A2A facade session and plan lifecycle operations functional via CLI
Wire CLI session and plan lifecycle commands through the A2A local facade, establishing the A2A protocol data flow: CLI -> A2aLocalFacade.dispatch() -> Service -> Domain. Key changes: - Added cli_bootstrap.py module providing get_facade() which lazily constructs a process-wide A2aLocalFacade instance wired to the DI container (plan_lifecycle_service, session_service, resource_registry_service, tool_registry). Service wiring is best-effort via contextlib.suppress. - Session CLI create command now notifies the A2A facade after session creation for protocol bookkeeping and telemetry. - Plan CLI commands (use, execute, lifecycle-apply) now notify the A2A facade via _notify_facade() helper after operations complete. The notification is best-effort (exceptions are suppressed) to avoid breaking CLI functionality if the facade is not available. - Added Behave feature (a2a_cli_facade_integration.feature) with 8 scenarios covering: facade bootstrap wiring, all 11 operations supported, session/plan dispatch through facade, and best-effort error suppression. The facade notification pattern preserves backward compatibility: CLI commands still perform the primary work via direct service calls, then notify the facade for A2A protocol compliance. This allows incremental migration toward full facade-first routing. ISSUES CLOSED: #852 |
||
|
|
f138bab5ff |
feat(a2a): implement SSE streaming for task updates and artifacts
Add Server-Sent Events (SSE) streaming infrastructure to the A2A event system, enabling real-time delivery of task status updates and artifact notifications. Key changes: - Defined SSE event type constants: TASK_STATUS_UPDATE (TaskStatusUpdateEvent) and TASK_ARTIFACT_UPDATE (TaskArtifactUpdateEvent) per the A2A protocol specification. - Added SseEventFormatter class that converts A2aEvent instances to text/event-stream format with event, id, and data fields. Includes keepalive formatting for long-lived connections. - Added EventBusBridge class that subscribes to the internal EventBus (ReactiveEventBus) and translates DomainEvent instances into A2aEvent instances published to the A2aEventQueue. Maps plan lifecycle events (PLAN_CREATED, PLAN_PHASE_CHANGED, etc.) to TaskStatusUpdateEvent and checkpoint events to TaskArtifactUpdateEvent. - Bridge handles closed queue gracefully via contextlib.suppress. - Added 8 Behave scenarios covering SSE formatting, event type constants, EventBusBridge translation for both status and artifact events, closed queue handling, and JSON payload validation. ISSUES CLOSED: #875 |
||
|
|
c2a2c5c4bf |
fix(cli): make plan correct accept plan_id as primary identifier (#1055)
## Summary - **`plan correct` now accepts a plan_id** as its positional argument (in addition to decision_id). When a plan_id is given, the root decision is automatically selected as the correction target. - The positional parameter is renamed from `decision_id` to `identifier` with updated help text reflecting dual use. - Backward compatibility is fully preserved: decision_id inputs continue to work exactly as before. ## How it works 1. Try `container.plan_lifecycle_service().get_plan(identifier)` to check if the identifier is a plan_id 2. If it resolves to a real `Plan` object, use it as `resolved_plan_id` and auto-select the root decision (`parent_decision_id is None`) 3. If lookup fails (`ResourceNotFoundError`) or the result is not a `Plan` instance, fall back to treating the identifier as a decision_id (original behavior) ## Verification - `nox -s lint` — All checks passed - `nox -s typecheck` — 0 errors, 1 pre-existing warning - `nox -s unit_tests` (correction features) — 150 scenarios passed, 683 steps passed, 0 failures ISSUES CLOSED: #969 Reviewed-on: cleveragents/cleveragents-core#1055 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
34c6972a05 |
feat(tool): implement BuiltinAdapter class and MCP automatic resource slot creation (#964)
## Summary Implement `BuiltinAdapter` class and MCP automatic resource slot creation. Two main changes: ### 1. BuiltinAdapter Class (`tool/builtins/adapter.py`) Formal adapter implementing the tool adapter lifecycle pattern for built-in tools: - `discover()` — returns all built-in tool descriptors (file, git, subplan tools) - `register(registry)` — registers all tools in a ToolRegistry, returns registered names - `activate()`/`deactivate()` — no-ops (built-in tools are always available) - Wraps existing `ALL_FILE_TOOLS`, `ALL_GIT_TOOLS`, `ALL_SUBPLAN_TOOLS` lists - Backward compatible — existing registration functions still work ### 2. MCP Resource Slot Inference (`mcp/adapter.py`) New `infer_resource_slots()` static method on `MCPToolAdapter`: - Scans MCP tool parameter schemas for file/directory/repo patterns - Creates `ResourceSlot` objects with appropriate type, access mode, binding mode - Mapping: `file_path`→file(rw), `directory`→directory(ro), `repo_path`→git-checkout(rw) - Slots stored in `source_metadata["resource_slots"]` on registered `ToolSpec` objects ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,817 scenarios) | | `nox -s integration_tests` | PASS (6 new tests) | | `nox -s coverage_report` | 97% (>= 97%) | Closes #882 Reviewed-on: cleveragents/cleveragents-core#964 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
b88bc0ec1b |
feat(perf): large project scaling tests (#984)
## Summary Add large project scaling benchmarks and tests at production scale (10K–100K files). ### New ASV Benchmarks **IndexingScalingSuite** (`large_project_scaling_bench.py`): - `time_walk_and_index` at 1K/10K/50K/100K files - `time_incremental_refresh` (1% modified files) - `track_indexed_file_count`, `track_tokens_per_second` **ContextAssemblyScalingSuite** (`context_assembly_scaling_bench.py`): - `time_full_pipeline` at 100/1K/5K/10K fragments - `time_tiered_strategy`, `time_recency_strategy` - `track_assembled_tokens`, `track_fragments_per_second` **ExecutionThroughputSuite** (`execution_throughput_bench.py`): - `time_sequential_plans` at 10/50/100 plans - `time_executor_construction`, `time_decision_tree_scaling` ### Scale Fixture Updates - Added `xlarge` (50K files) and `xxlarge` (100K files) profiles to `scale_metadata.json` - Added 50K/100K thresholds to `baseline_thresholds.json` - Added `context_assembly` and `execution_throughput` threshold sections ### Tests & Documentation - 15 Behave scenarios validating profiles, thresholds, monotonicity, memory budgets - 6 Robot integration tests including live 1K-file indexing throughput check - `docs/reference/scaling_baselines.md` documenting all baseline metrics ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,910 scenarios) | | `nox -s integration_tests` | PASS (1,526 tests) | | `nox -s coverage_report` | 97% (>= 97%) | Closes #859 Reviewed-on: cleveragents/cleveragents-core#984 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
8a87262f86 |
feat(sandbox): implement overlay filesystem sandbox strategy (#994)
## Summary Implement the overlay filesystem sandbox strategy with OverlayFS support and userspace fallback. ### Implementation **`OverlaySandbox`** (`infrastructure/sandbox/overlay.py`, 497 lines): - Detects OverlayFS availability at runtime via `/proc/filesystems` + `os.geteuid() == 0` check - **Real OverlayFS mode** (requires root): creates upper/work/merged dirs, mounts overlay filesystem, captures writes in upper layer - **Userspace fallback** (default in CI/containers): `shutil.copytree` the original into merged dir, tracks changes via `filecmp` diff on commit - `create()`: sets up directory structure, mounts if available - `commit()`: copies changed/added files from overlay to original, removes deleted files - `rollback()`: unmounts (or removes) merged, recreates from scratch - `cleanup()`: unmounts, removes all temp dirs, idempotent ### Domain Model Updates - Added `OVERLAY = "overlay"` to `SandboxStrategy` enum in both `resource_type.py` and `resource.py` - Added `STRATEGY_OVERLAY` to `SandboxFactory`, registered for `fs-mount`, `fs-directory`, `fs-file` resources ### Tests - **22 Behave scenarios**: full lifecycle (create/commit/rollback/cleanup), status transitions, path traversal guard, fallback detection, error handling - **6 Robot integration tests**: end-to-end overlay sandbox operations ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,917 scenarios) | | `nox -s coverage_report` | 97% (>= 97%) | Closes #880 Reviewed-on: cleveragents/cleveragents-core#994 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
6531440431 |
feat(actor): implement estimation actor type (#962)
## Summary Implement the estimation actor as a functional actor type. The estimation actor provides cost, time, and resource estimates for plan operations, running after Strategize completes (before Execute). Estimation is informational only and optional — plans work without an estimation actor configured. ### Changes **New files:** - `src/cleveragents/domain/models/core/estimation.py` — `EstimationResult` frozen Pydantic model with fields for cost (USD), tokens, steps, child plans, time, risk level/factors, summary - `features/estimation_actor.feature` — 12 Behave test scenarios (model validation, serialization, stub actor, plan integration, optional behavior) - `features/steps/estimation_actor_steps.py` — Step definitions - `robot/estimation_actor.robot` — 6 Robot integration tests - `robot/helper_estimation_actor.py` — Robot test helper **Modified files:** - `domain/models/acms/tiers.py` — Added `ESTIMATOR` to `ActorRole` enum - `domain/models/core/plan.py` — Added `estimation_result: EstimationResult | None` field, estimation data in `as_cli_dict()` - `application/services/plan_executor.py` — Added `EstimationStubActor` class - `application/services/plan_lifecycle_service.py` — Added `_run_estimation()` method, invoked in `execute_plan()` - `cli/commands/plan.py` — Display estimation results in plan status - `vulture_whitelist.py` — Added 12 new public symbols ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,818 scenarios) | | `nox -s integration_tests` | PASS (1,512 tests) | | `nox -s coverage_report` | 98% (>= 97%) | Closes #890 Reviewed-on: cleveragents/cleveragents-core#962 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
95d3e09925 |
feat(cli): final CLI polish and UX consistency pass (#1018)
## Summary Final CLI polish and UX consistency pass: shared constants, centralized error formatting, shell completion, and standardized help text. ### New Modules - **`cli/constants.py`** (70 lines): Exit codes (`EXIT_SUCCESS`=0 through `EXIT_CONFLICT`=4), format defaults (`FORMAT_TEXT`, `FORMAT_JSON`, `FORMAT_TABLE`) - **`cli/errors.py`** (105 lines): `cli_error()` with hint support, `cli_warning()`, `cli_not_found()` with resource-type-aware hint ### CLI Changes - Shell `completion` command generating scripts for bash/zsh/fish/powershell - Standardized help text across command modules - Error functions exported from `cli/__init__.py` ### Tests - **17 Behave scenarios**: Exit codes, error formatting, cli_not_found, format constants, help text, completion - **15 Robot integration tests**: All subcommands respond to --help, invalid commands return non-zero, completion generation works ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,912 scenarios) | | `nox -s coverage_report` | 97% (>= 97%) | Closes #861 Reviewed-on: cleveragents/cleveragents-core#1018 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
00897be24a |
feat(ci): CI/CD pipeline definitions (#983)
## Summary Complete CI/CD pipeline definitions: release workflow, caching, status-check consolidation, and documentation. ### Changes **New: Release pipeline** (`.forgejo/workflows/release.yml`): - Triggered on `v*` tags - 3 jobs: `build-wheel` → `build-docker` → `create-release` - Builds wheel via `nox -s build`, Docker image via multi-stage Dockerfile - Creates Forgejo release via API with wheel artifact attached - Configurable registry push via `REGISTRY_*` secrets **Updated: CI pipeline** (`.forgejo/workflows/ci.yml`): - Added `actions/cache@v3` for `~/.cache/uv` on all 8 primary jobs (keyed on `pyproject.toml` hash) - Added `status-check` consolidation job depending on all required checks — single gate for branch protection **Updated: CONTRIBUTING.md**: - New CI/CD section: pipeline overview, job table, required merge checks, release process, secrets documentation **Tests**: - 13 new Behave scenarios validating workflow YAML structure, tag triggers, job definitions, dependencies - 9 new Robot tests validating file existence, content, nox session references ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,819 scenarios) | | `nox -s coverage_report` | 97.9% (>= 97%) | Closes #858 Reviewed-on: cleveragents/cleveragents-core#983 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
fa3f2d6365 |
test: add TDD bug-capture test for #932 — plan apply missing --yes flag (#958)
## Summary TDD expected-fail tests proving bug #932 exists: the `plan apply` (`lifecycle-apply`) command does not accept the `--yes`/`-y` flag required by the specification. The flag should skip the confirmation prompt before applying plan changes. ### Tests Added **Behave scenarios** (`features/tdd_plan_apply_yes_flag.feature`): - `lifecycle-apply --yes` should be accepted (tests `--yes` long flag) - `lifecycle-apply -y` should be accepted (tests `-y` short flag) Tags: `@tdd_expected_fail @tdd_bug @tdd_bug_932` **Robot Framework tests** (`robot/tdd_plan_apply_yes_flag.robot`): - `check-yes-long` — invokes CLI with `--yes`, asserts no "No such option" error - `check-yes-short` — invokes CLI with `-y`, asserts no "No such option" error ### How the Bug Is Proven The `lifecycle_apply_plan` function (`cleveragents.cli.commands.plan`) defines only `plan_id` and `--format` parameters — no `--yes`/`-y`. When tests invoke `lifecycle-apply --yes`, Typer/Click rejects it with `"No such option: --yes"` and exit code 2. The assertion that this error is absent **fails**, confirming bug #932. The `@tdd_expected_fail` tag inverts this to a pass. ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,808 scenarios) | | `nox -s integration_tests` | PASS (1,508 tests) | | `nox -s coverage_report` | 98% (>= 97%) | Closes #950 Reviewed-on: cleveragents/cleveragents-core#958 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
8d108cb5d1 |
feat(tool): add tool-level execution environment preferences (#970)
## Summary Add tool-level execution environment preferences with four modes: `required`, `preferred`, `specific`, and `none`. ### Changes **New model** (`domain/models/core/execution_environment_preference.py`): - `EnvironmentPreferenceMode` enum: REQUIRED, PREFERRED, SPECIFIC, NONE - `ExecutionEnvironmentPreference` frozen Pydantic model with `mode` and `target_resource` fields - Model validator ensures `target_resource` required iff mode is SPECIFIC **ToolSpec & Tool model updates:** - Added `execution_environment: ExecutionEnvironmentPreference` field to both `ToolSpec` (runtime) and `Tool` (domain) - `Tool.from_config()` parses `execution_environment` from YAML config dicts **ToolRunner integration** (`tool/runner.py`): - Before calling `_env_resolver.resolve()`, checks `spec.execution_environment.mode`: - REQUIRED: raises `ContainerUnavailableError` if resolved env is not CONTAINER - PREFERRED: tries container, gracefully falls back to host - SPECIFIC: overrides `tool_env` with `target_resource` - NONE: current behavior (caller-supplied tool_env) ### Tests - **27 Behave scenarios** covering model validation, serialization, preference routing, YAML parsing, error cases - **12 Robot integration tests** covering CLI tool preference display and end-to-end routing ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,833 scenarios) | | `nox -s integration_tests` | PASS (12 new tests) | Closes #879 Reviewed-on: cleveragents/cleveragents-core#970 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
4f950a1600 |
feat(cli): repo indexing CLI functional (#982)
## Summary Add `agents repo index` and `agents repo status` CLI commands, wiring the existing `RepoIndexingService` backend to the CLI layer. ### Commands - **`agents repo index <resource_name>`** — Triggers full or incremental indexing of a repository resource. Options: `--full` (force full re-index), `--format text|json` - **`agents repo status <resource_name>`** — Displays indexing metadata: status, file count, token estimate, primary language, last indexed timestamp. Options: `--format text|json` ### Implementation - New CLI module `src/cleveragents/cli/commands/repo.py` (238 lines) - Registered as `repo` subcommand group in `cli/main.py` - Resolves resource by name via `ResourceRegistryService` - Calls `RepoIndexingService.index_resource()` / `refresh_index()` / `get_index_status()` - Both text (Rich panel) and JSON output formats ### Quality Gates | Session | Result | |---|---| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` | PASS (10,819 scenarios) | | `nox -s coverage_report` | 98% (>= 97%) | | Integration tests | 5/5 PASS | Closes #856 Reviewed-on: cleveragents/cleveragents-core#982 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
051ee7c290 |
test(coverage): add Behave BDD tests to improve coverage across 52 source files
Added 52 new .feature files and corresponding _steps.py files targeting previously uncovered code paths in the following areas: - TUI layer: app, commands, persona (state/schema/registry), widgets, input (shell_exec, reference_parser) - Application services: plan lifecycle/service/executor, session, project, repo indexing, correction, checkpoint, actor, llm_actors, strategy coordinator, resource file watcher, service retry wiring - CLI commands: session, resource, repl, plan, db, automation_profile - Domain models: retry_policy, resource_type, cost_budget, docker_compose_analyzer, detail_level, _sql_string_aware, _postgresql_helpers - Core: circuit_breaker, retry_service_patterns - Infrastructure: repositories, transaction_sandbox, strategy_registry, plugins/loader, container - Config: settings - Agents: plan_generation, context_analysis, auto_debug - A2A: facade All new tests follow the Behave/Gherkin BDD standard. Resolved step definition collisions with unique prefixes. Fixed Alembic fileConfig logger disabling issue (disable_existing_loggers=False). ISSUES CLOSED: #1068 |
||
|
|
79b0a2c52e |
chore(cli): complete renderer migration for remaining command modules (#1059)
## Summary Migrates 8 CLI command modules from module-level `Console()` objects to the shared `_get_console()` from `renderers.py`. This eliminates redundant Console instances and ensures consistent output handling across all CLI commands. ### Modules Migrated | Module | Change | |--------|--------| | `auto_debug.py` | `Console()` → `_get_console()` | | `context.py` | `Console()` → `_get_console()` | | `automation_profile.py` | `Console()` → `_get_console()` | | `tool.py` | `Console()` → `_get_console()` | | `project.py` | Both stdout + stderr Console → shared instances | | `config.py` | `Console()` → `_get_console()` | | `resource.py` | `Console()` → `_get_console()` | | `skill.py` | `Console()` → `_get_console()` | ### Approach Each module-level `console = Console()` was replaced with `console = _get_console()`, keeping the module attribute name `console` intact for backward compatibility with existing test patches. The `from rich.console import Console` import was removed from each module. Modules NOT migrated in this PR (documented for follow-up): - `plan.py` — uses streaming/Live display, too large for this scope - `session.py`, `repl.py`, `lsp.py`, `server.py` — specialized usage patterns ### Quality Gates | Session | Result | |---------|--------| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | Unit tests (targeted) | All affected scenarios pass | Partial implementation of #813 (step 2 of 4: Console consolidation). Steps 1, 3, 4 remain open on issue #813. Reviewed-on: cleveragents/cleveragents-core#1059 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com> |
||
|
|
714d799ae9 |
feat(examples/actors/code_review.yaml): add a new code review tool (#458)
## Summary Add a `code_review.yaml` actor example that uses Claude Opus 4 with file and git tools to perform automated code reviews against the project's review playbook. ### Changes - **New file**: `examples/actors/code_review.yaml` — LLM actor configured with `files/read_file`, `files/list_directory`, and `builtin/git-*` tools, with a system prompt that reads `docs/development/review-playbook.md` and diffs against `master` - **Updated**: `features/actor_examples.feature` — bumped example count from 7 to 8 and added `code_review.yaml` to the file listing assertion ### Verification - `nox -e lint` — passed - `nox -e typecheck` — 0 errors - `nox -e format -- --check` — all files formatted - `nox -s unit_tests -- features/actor_examples.feature` — 25 scenarios passed ### Rebase Notes - Rebased onto current master (`4d3499dc`) - Squashed 2 commits into 1 (eliminated 3 merge commits) - Resolved conflict in `actor_examples.feature`: master added `strategy_with_subplan.yaml` (count 7), our branch adds `code_review.yaml` (count now 8) Reviewed-on: cleveragents/cleveragents-core#458 Reviewed-by: Jeffrey Phillips Freeman <jeffrey.freeman@cleverthis.com> Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
202c9bfe75 |
test(acms): TDD failing tests for context tier runtime logic (bug #821) (#1058)
## Summary TDD expected-fail tests proving bug #821 exists: `ContextTierService` has data models for hot/warm/cold tiers but **no runtime logic** for automatic promotion, demotion, or eviction. - **Promotion on access**: Accessing a cold-tier fragment repeatedly via `get()` does NOT auto-promote it — `get()` updates `access_count`/`last_accessed` but never calls `promote()` - **Demotion on staleness**: No staleness enforcement method exists — tried `enforce_staleness()`, `apply_tier_policy()`, `tick()`, etc. — none are implemented - **Eviction on budget overflow**: `store()` does NOT enforce `TierBudget.max_tokens_hot` — the hot tier grows without bound ### Files Added | File | Purpose | |------|---------| | `features/tdd_context_tier_runtime.feature` | 3 Behave scenarios tagged `@tdd_expected_fail @tdd_bug @tdd_bug_821 @mock_only` | | `features/steps/tdd_context_tier_runtime_steps.py` | Type-annotated step definitions exercising real `ContextTierService` | | `robot/tdd_context_tier_runtime.robot` | 3 Robot Framework integration tests tagged `tdd_expected_fail` | | `robot/helper_tdd_context_tier_runtime.py` | Helper script for Robot tests with 3 subcommands | ### Verification - `nox -s lint` — passed - `nox -s typecheck` — passed (0 errors) - `nox -s unit_tests -- features/tdd_context_tier_runtime.feature` — **3 scenarios passed** (all assertions fail as expected, `@tdd_expected_fail` inverts to CI pass) ISSUES CLOSED: #840 Reviewed-on: cleveragents/cleveragents-core#1058 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
60af2cae0c |
fix(cli): make plan explain accept plan_id for plan-level explanation (#1057)
## Summary Fixes `plan explain` to accept both decision IDs and plan IDs as the positional argument, matching the M3 acceptance test usage pattern `plan explain <plan_id>`. ### Problem `explain_decision_cmd` only accepted a decision ULID. When the M3 acceptance test passed a plan ID, `svc.get_decision(plan_id)` returned `None`, causing "Decision not found" error with exit code 1. ### Fix 1. Renamed parameter `decision_id` → `identifier` 2. Tries `svc.get_decision(identifier)` first (backward compat) 3. Falls back to `svc.list_decisions(identifier)` treating it as a plan_id, explaining the root decision 4. Clear error if neither resolves ### Quality Gates | Session | Result | |---------|--------| | `nox -s lint` | PASS | | `nox -s typecheck` | PASS (0 errors) | | `nox -s unit_tests` (explain features) | 46/46 PASS | Closes #968 Reviewed-on: cleveragents/cleveragents-core#1057 Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com> |
||
|
|
35e1807f95 |
fix(cli): restore phase-aware execution in plan execute command
Consolidate the execute_plan CLI handler to eliminate a redundant service.get_plan() call (the separate read-only pre-check now reuses the same current_plan reference used for phase detection), update the command-table description from the stale 'Transition to Execute phase' to 'Run phase-aware plan execution', and replace the static post-execution hint with a state-aware message that distinguishes execute/complete (ready for apply) from other states (continue executing). Update corresponding Behave test mocks to match the reduced get_plan call sequence and the updated output panel title. ISSUES CLOSED: #967 |
||
|
|
e2f90ffcd5 |
feat(resource): add deferred physical resource types
Add 11 deferred physical resource types covering the git object taxonomy (git, git-remote, git-branch, git-tag, git-commit, git-tree, git-tree-entry, git-stash, git-submodule) and filesystem link types (fs-symlink, fs-hardlink). - YAML configs under examples/resource-types/ - Bootstrap registration via _resource_registry_physical.py module - Auto-discovery rules with bounded scan_depth (1-2) for git object graph - fs-directory scan_depth=1 (immediate children only) - git-branch scan_depth=1 (single HEAD), git-tree scan_depth=2 (capped) - Updated fs-directory child_types/parent_types/auto_discovery - Updated git-checkout child_types to include git - Fixed git-tag child_types to include git-commit (DAG consistency) - Behave tests, Robot tests, ASV benchmarks - Documentation in docs/reference/resource_types_builtin.md ISSUES CLOSED: #330 |
||
|
|
554d6889cc |
fix(cli): add --skill flag to actor run command (#971)
## Summary Add the missing `--skill` repeatable flag to `actor run` and `actor-run` CLI commands, aligning the implementation with the specification (CLI Synopsis line 277). The flag enables ad-hoc skill injection at runtime without modifying YAML configuration. Closes #887 ## Changes ### DI Container - **`container.py`**: Added `_build_skill_service()` factory and `skill_service` Singleton provider, following the established `_build_*` pattern. Falls back to in-memory `SkillService()` when the database is unavailable. Exception handling narrowed to `(ImportError, OperationalError, DatabaseError, OSError)` with `exc_info=True` for traceability. ### CLI Layer - **`actor.py`**: Added `--skill` Typer option (`list[str] | None`, repeatable, `metavar="NAME"`). Help text notes that skills only augment tool-bearing agents. Wrapped constructor in the existing `try/except` block so `CleverAgentsException` from skill resolution is properly caught. - **`actor_run.py`**: Same `--skill` option with `metavar="NAME"`. Exception handler catches `CleverAgentsException` (matching master — not broadened to `CleverAgentsError`). - **`skill.py`**: Removed module-level `_service` cache. `_get_skill_service()` now always delegates to `get_container().skill_service()` so that `reset_container()` correctly invalidates the cached instance. `_reset_skill_service()` now overrides the container's provider via `providers.Object()`. Removed dead `validate_skill_names()` function. ### Runtime Layer - **`application.py`** (438 lines, down from 625): `ReactiveCleverAgentsApp` gains `skill_names` parameter with automatic deduplication via `dict.fromkeys`. `_resolve_skills()` obtains `SkillService` from the DI container (no CLI layer import). Separate `except KeyError` and `except ValueError` produce distinct error messages (`"not found in registry"` vs `"resolution failed: {exc}"`). Skill tools are only injected into agents that already have tools (`if self._resolved_skill_tools and tools:`), preventing LLM agents from being converted to pass-through `SimpleToolAgent` instances. When skill tools are skipped for tool-less agents, `logger.debug` emits a diagnostic message. `_sanitize_skill_name()` validates skill name format with tightened regex: `^[\w.-]{1,127}/[\w.-]{1,127}$` with `re.ASCII` flag. Zero-tool skill warning now uses `logger.warning` (not `print(stderr)`), ensuring structured log output and proper log-level filtering. - **`graph_executor.py`** (334 lines): Extracted graph execution logic. Type annotations improved. ### Tests - 24+ Behave scenarios across feature files covering: single/multiple/unknown skill flags, skill+context combined, duplicate deduplication, skill resolution, ValueError path, zero-tool resolution, error handling, tool merging, default behavior, overrides, LLM agent guard, `_sanitize_skill_name` edge cases (empty string, too-long name, ANSI escape codes, disallowed characters), `_build_skill_service` happy+fallback paths, `_get_skill_service` container delegation. - CLI "unknown skill" tests for **both** `actor.py` and `actor_run.py` exercise the real error chain (mock only `get_container()`, not the entire `ReactiveCleverAgentsApp`), testing `_resolve_skills()` → `CleverAgentsException` → `except CleverAgentsException` → exit code 2 end-to-end. - Combined skill+context tests assert `ContextManager` was instantiated and `exists()` was called in dedicated **Then** steps. - `@coverage` tags added to all new scenarios. - **Robot Framework smoke tests** added (`robot/skill_actor_run.robot` + `robot/helper_skill_actor_run.py`): unknown-skill error path and valid-skill acceptance path. ### Changelog - Added entry under `## Unreleased` in `CHANGELOG.md`. ## Review Fixes Applied (Brent Edwards, Rounds 1 & 2) | # | Finding | Resolution | |---|---------|------------| | **P1-1** | `print(stderr)` for zero-tool skill warning | **Fixed** — replaced with `logger.warning("Skill '%s' resolved to zero tools", name)`, removed unused `import sys` | | **P2-2** | Skill tools silently skipped for tool-less agents | **Fixed** — added `logger.debug` when skipped; updated `--skill` help text to note "only augments tool-bearing agents" | | **P2-3** | `container.py` at 739 lines | **Acknowledged** — pre-existing growth (+59 lines for `_build_skill_service`); extracting factories is a separate refactoring task | | **P2-4↑** | `CleverAgentsException` → `CleverAgentsError` broadens catch scope | **Fixed** — reverted `actor_run.py` to `except CleverAgentsException` matching master | | **P3-5** | No Robot Framework smoke test for `--skill` | **Fixed** — added `skill_actor_run.robot` with 2 test cases (unknown-skill error, valid-skill acceptance) | | **P3-6** | `GraphExecutor._follow_chained_edges` static-calling-static | **Acknowledged** — cosmetic pattern that doesn't affect correctness; can address in a follow-up | ## Known Limitations / Deferred Items | Item | Reason | |------|--------| | `actor.py` at 679 lines (500-line guideline) | Pre-existing (670 on master), +9 lines for `--skill`. Refactoring the shared `_execute()` closure is a separate task. | | `container.py` at 739 lines (500-line guideline) | Was 680 lines on master, +59 lines for `_build_skill_service()` and `skill_service` provider. Refactoring into sub-modules is a separate task. | | Code duplication between `actor.py` and `actor_run.py` `run()` | ~47 lines identical code. Coupled with the line-count issue above — both require extracting shared execution logic into a helper module. | | `SimpleToolAgent` only executes `tools[0]` | Deferred to #974. Pre-existing architectural limitation, not introduced by this PR. | | `GraphExecutor._follow_chained_edges` static-calling-static pattern | Cosmetic, doesn't affect behavior. | ## Quality Gates - `nox -s lint`: ✅ PASS - `nox -s typecheck`: ✅ PASS (0 errors) - `nox -s unit_tests`: ✅ PASS (11,130 scenarios, 0 failures) - `nox -s integration_tests`: ✅ PASS (1,559 tests, 0 failures) - `nox -s coverage_report`: ✅ 97% (meets threshold) - Branch rebased onto latest `master` (`ab1fd19b`) Reviewed-on: cleveragents/cleveragents-core#971 Co-authored-by: Rui Hu <rui.hu@cleverthis.com> Co-committed-by: Rui Hu <rui.hu@cleverthis.com> |
||
|
|
2434253c1a |
feat(cli): implement full output rendering framework (#812)
## Summary Implement the 6 missing element handle types (Tree, Text, Code, Diff, Separator, ActionHint) and ensure all 6 materialization strategies (rich, color, table, plain, json, yaml) support all 10 element types. This completes the output rendering framework per the specification (§25417-27276). Closes #550 ## Changes ### Source (restructured into smaller modules) #### `handles/` package (was `handles.py` — 1062 lines → 5 files, all ≤500 lines) - **`_models.py`**: All Pydantic data models, constants (MAX_TREE_DEPTH, MAX_ELEMENTS_PER_SESSION, MAX_TABLE_ROWS), exceptions (ElementClosedError), event classes, ElementSnapshot union. P2-1: `DiffLine.type` renamed to `line_type` with backward-compatible alias. - **`_base.py`**: Generic `ElementHandle[E]` base class with thread-safe `element_copy()` (lock-protected). - **`_panel_table.py`**: PanelHandle, TableHandle, StatusHandle. - **`_concrete.py`**: ProgressHandle (P1-1: validates `total` non-negative), TreeHandle, TextHandle (P3-2: close() delegates to super()), CodeHandle, DiffHandle, SeparatorHandle, ActionHintHandle. P3-8: `increment(delta=0)` now rejected. - **`__init__.py`**: Re-exports all public symbols. #### `_renderers.py` — plain renderers, sanitization, and shared helpers - Plain render functions for all 10 element types. - Terminal escape sanitization (`strip_terminal_escapes`). - P2-3: `_sort_table_rows` now consults `ColumnDef.col_type` for numeric sorting. - P2-6: `compute_column_widths()` shared helper extracted (DRY fix). #### `_color_renderers.py` — ANSI colour renderers - Colour-coded render functions for all 10 element types. - P2-2: TextBlock now gets color treatment (`_render_text_color`) instead of falling through to plain. #### `_boxdraw.py` — box-drawing renderers - P2-5: Upgraded from ASCII `+-|` to Unicode box-drawing `╭─╮│╰╯` with rounded corners per spec §26821. #### `_ids.py` — ID generation helpers - P3-1: Session and handle IDs now use separate counters, making IDs monotonic within their namespace. #### `materializers.py` — strategy protocol and 6 concrete strategies - P1-2: `_snapshot_to_dict` now includes `timing` field in JSON/YAML output per spec §27022. - P2-4: `_column_def_to_dict` always includes all fields unconditionally for stable JSON schemas. #### `selection.py` — materializer selection with fallback - P1-4: `NO_COLOR` environment variable now respected (https://no-color.org/). When set, all visual formats fall back to plain. Precedence: explicit flag > NO_COLOR > terminal capability fallback. #### `session.py` - P1-1: `session.progress()` factory validates `total >= 0`. - P1-2: `snapshot()` includes `timing` when available. #### `__init__.py` — package docstring - P1-3: SD-29 corrected to reflect actual Table → Color → Plain fallback chain. - SD-14 marked as implemented (NO_COLOR support added). ### Spec Deviations (Documented) 28 deliberate deviations documented in `__init__.py` module docstring (SD-1 through SD-29, with SD-14 now implemented). SD-29 corrected. ### Tests (updated) - **+23 new BDD scenarios** covering: P1-1 total validation, P1-4 NO_COLOR, P2-1 DiffLine.line_type alias, P2-2 text color, P2-3 numeric sorting, P2-4 ColumnDef serialization, P2-5 Unicode box-drawing, P3-3 add_rows limit, P3-5 10-thread stress test, P3-6 summary truncation, P3-7 explicit format for color/table, P3-8 zero delta. - **Robot tests**: Updated box-drawing assertion for Unicode chars. ## Verification | Check | Result | |-------|--------| | Pyright | 0 errors, 1 pre-existing warning | | Ruff lint | All passed | | Unit tests | 393 features, 11,344 scenarios, 0 failures | | Integration tests | All passed | | E2E tests | All passed | | Coverage | 97% overall (threshold: 97%) | ## Review Fixes Applied (Luis Review #2412) | ID | Severity | Fix | |----|----------|-----| | P1-1 | High | `set_progress()` and `session.progress()` validate `total >= 0` | | P1-2 | High | `_snapshot_to_dict` includes `timing` field | | P1-3 | High | SD-29 documentation corrected | | P1-4 | High | `NO_COLOR` env var respected | | P2-1 | Medium | `DiffLine.type` → `line_type` with alias | | P2-2 | Medium | TextBlock gets color treatment | | P2-3 | Medium | Numeric column sorting | | P2-4 | Medium | ColumnDef always serializes all fields | | P2-5 | Medium | Unicode box-drawing characters | | P2-6 | Medium | Shared `compute_column_widths()` helper | | P3-1 | Low | Separate ID counters | | P3-2 | Low | TextHandle.close() delegates to super() | | P3-3 | Low | add_rows batch limit test | | P3-5 | Low | 10-thread stress test | | P3-6 | Low | Summary truncation test | | P3-7 | Low | Explicit format tests for color/table | | P3-8 | Low | Zero delta rejected | ### Deferred Items | ID | Reason | |----|--------| | P3-9 | snapshot() lock scope — acceptable correctness trade-off | | P3-10 | CLEVERAGENTS_FORMAT env var — documented as SD-15, requires CLI framework changes | Reviewed-on: cleveragents/cleveragents-core#812 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> |
||
|
|
3837327564 |
feat(plan): enforce decision type phase-gating at recording time (#973)
## Summary Adds phase-gating validation to `DecisionService.record_decision()` that enforces the specification's constraint: certain decision types are only valid during specific plan phases. This prevents invalid decisions (e.g., `tool_invocation` during Strategize, `strategy_choice` during Execute) from being persisted. ### Changes - **Exception** (`cleveragents.core.exceptions`): Added `DecisionPhaseViolationError(BusinessRuleViolation)` with `decision_type`, `plan_phase`, and `allowed_types` attributes. - **Phase constants** (`cleveragents.domain.models.core.decision`): - `resource_selection` added to `EXECUTE_TYPES` — now phase-agnostic (Strategize or Execute) per ADR-007 L72 and ADR-033 L74. - `subplan_spawn` / `subplan_parallel_spawn` in both sets; code comment documents divergence from ADRs per M4 subplan model (ticket #931). - `USER_INTERVENTION` remains phase-agnostic (both sets). - Module-level docstring table updated to match actual assignments. - `is_any_phase_type` property updated to check membership in both sets dynamically (was hardcoded to `USER_INTERVENTION` only). - **Phase-gating module** (`cleveragents.application.services.phase_gating`): - Extracted from `DecisionService` to reduce `decision_service.py` line count (1010 → 913) and isolate the phase-gating concern. - `PHASE_ALLOWED_TYPES` typed as `Mapping[PlanPhase, frozenset[DecisionType]]`. - `resolve_plan_phase()` helper: supports explicit parameter, DB lookup, and graceful skip. - `validate_phase_gating()` enforcement raises `DecisionPhaseViolationError`. - Exception narrowing: DB lookup catches `(DatabaseError, OperationalError, OSError)` instead of bare `except Exception` — only absorbs infrastructure failures, not programming errors. - `# TODO(pg-migration):` marker on TOCTOU race documentation for future PostgreSQL migration. - **Decision service** (`cleveragents.application.services.decision_service`): - Added `plan_phase` parameter to `record_decision()`. - Invalid `plan_phase` string now raises `ValidationError` (was uncaught `ValueError`). - Imports and delegates to `phase_gating` module for all phase-gating logic. - `PHASE_ALLOWED_TYPES` re-exported in `__all__` for backward compatibility. - **CHANGELOG**: Added behavioral change entry for `resource_selection` reclassification. - **Backward compatibility**: Phase-gating is opt-in — when neither `plan_phase` is provided nor a UnitOfWork is wired, validation is skipped, preserving all existing callers. - **Unrelated drive-by reverted**: Removed `ULID_PATTERN` from `decision.py` `__all__` (was an unrelated export addition). - **Tests**: - 36 Behave scenarios covering valid/invalid types per phase, phase-agnostic acceptance, DB-based resolution (Strategize and Execute plans), unknown plan in DB, PlanPhase enum pass-through, error attributes, and ungated phases. - 11 new Behave scenarios for `is_any_phase_type`: 4 dual-phase types (true) + 7 single-phase types (false), including `prompt_definition` root test. - 6 Robot Framework integration tests with stderr assertions. - Updated `consolidated_decision.feature` for new `EXECUTE_TYPES` member count (8 members). - Test cleanup now calls `uow.engine.dispose()` before file deletion. - `tempfile.mktemp()` replaced with `tempfile.mkstemp()`. - Inline imports moved to module top-level per CONTRIBUTING.md. - Flaky concurrency test timing increased in `subplan_execution_steps.py`. ### Review Round 1 + 2 Fixes | # | Finding | Resolution | |---|---------|------------| | P1-1 | `except Exception` too broad in `_resolve_plan_phase` | Narrowed to `(DatabaseError, OperationalError, OSError)` — matches codebase pattern | | P2-2 | `decision_service.py` at 1010 lines | Extracted to `phase_gating.py` module (1010 → 913 lines) | | P2-3 | TOCTOU race — no programmatic guard | Added `# TODO(pg-migration):` marker with actionable guidance | | P2-4 | `resource_selection` reclassification needs CHANGELOG | Added CHANGELOG entry documenting behavioral change | | P2-7 | `is_any_phase_type` BDD gap for dual-phase types | Added 11 parametrized scenarios covering all 4 dual-phase + 7 single-phase types | | P3-5 | `ULID_PATTERN` export is unrelated drive-by | Reverted — removed from `decision.py` `__all__` | | P3-6 | `decision.py` at 514 lines (now 513) | No action — reviewer accepted as marginally over | ### Quality Gates | Session | Result | |---------|--------| | lint | PASS | | typecheck | PASS (0 errors) | | unit_tests | PASS (11,153 scenarios, 0 failures) | | integration_tests | PASS (1,563 tests, 0 failures) | | e2e_tests | PASS (16 tests, 0 failures) | | coverage_report | 97% (threshold: 97%) | Closes #931 Reviewed-on: cleveragents/cleveragents-core#973 Co-authored-by: Rui Hu <rui.hu@cleverthis.com> Co-committed-by: Rui Hu <rui.hu@cleverthis.com> |
||
|
|
0b415a6e5e |
test: add TDD bug-capture test for #969 — plan correct plan_id handling (#1051)
## Summary Add TDD bug-capture tests for bug #969 (`plan correct` expects `decision_id` but M3 acceptance test passes `plan_id`). These tests prove the bug exists and will serve as regression guards once the fix in #969 is merged. ### Changes - **Behave test** (`features/tdd_plan_correct_plan_id.feature`): Two scenarios tagged `@tdd_expected_fail @tdd_bug @tdd_bug_969` — one for `--mode revert` and one for `--mode append` — that invoke `plan correct <plan_id>` (without `--plan` flag) and assert the command resolves the plan_id to its root decision as `target_decision_id`. Both modes are tested because the bug affects `target_decision_id` resolution **before** mode-specific branching. - **Step definitions** (`features/steps/tdd_plan_correct_plan_id_steps.py`): Mock setup for DI container (DecisionService), CorrectionService, and `_resolve_active_plan_id`. Uses `tpcpid` step prefix per project conventions. - **Shared fixtures** (`features/mocks/tdd_plan_correct_plan_id_fixtures.py`): Centralised constants, patch targets, mock builders (`make_decision_ns`, `make_mock_container`, `make_correction_svc`, `make_default_decisions`, `make_default_container`), and `build_cli_args` helper. Both the Behave steps and Robot helper import from this shared module, eliminating code duplication and drift risk. - **Robot test** (`robot/tdd_plan_correct_plan_id.robot`): Two integration-level tests (revert + append) with `tdd_expected_fail tdd_bug tdd_bug_969` tags, exercising the same code paths via the helper script. - **Robot helper** (`robot/helper_tdd_plan_correct_plan_id.py`): Standalone helper that exits 0 with sentinel when the bug is fixed, exits 1 when the bug is present. Imports shared fixtures from `features/mocks/`. - **Changelog** (`CHANGELOG.md`): Added entry under Unreleased for #979. ### Bug Description The `correct_decision` function in `cleveragents.cli.commands.plan` declares `decision_id` as its first positional argument. When the M3 acceptance test calls `plan correct <plan_id> --mode revert --guidance "..."`, the plan_id is captured as `decision_id` and used directly as `target_decision_id` in `svc.request_correction()`. Since the plan_id is not a valid decision_id, the correction service cannot find the targeted decision. The same bug path is exercised by `--mode append`. ### How TDD Expected-Fail Works - The `@tdd_expected_fail` tag causes the test framework to invert the result: the test passes CI when the underlying assertion fails (proving the bug exists) and fails CI if the assertion passes (bug was fixed without removing the tag). - When bug #969 is fixed, the developer removes the `@tdd_expected_fail` tag, and the test runs normally as a regression guard. ### Quality Gates All nox sessions pass: - `nox -s lint` ✅ - `nox -s typecheck` ✅ (0 errors) - `nox -s unit_tests` ✅ (387 features, 11121 scenarios, 0 failures) - `nox -s integration_tests` ✅ (1561 tests, 0 failures) - `nox -s e2e_tests` ✅ (16 tests, 0 failures) - `nox -s coverage_report` ✅ (≥97%) Closes #979 Reviewed-on: cleveragents/cleveragents-core#1051 Co-authored-by: Rui Hu <rui.hu@cleverthis.com> Co-committed-by: Rui Hu <rui.hu@cleverthis.com> |
||
|
|
c741329f70 |
test: add TDD bug-capture test for #968 — plan explain plan_id handling (#1052)
## Summary Add TDD bug-capture tests for bug #968: the `plan explain` CLI command fails with rc=1 when given a plan_id because `explain_decision_cmd` treats its argument as a decision_id and `svc.get_decision(plan_id)` raises `DecisionNotFoundError`. These tests capture the exact failure condition described in #968 and are tagged with `@tdd_expected_fail`, `@tdd_bug`, and `@tdd_bug_968`. The `@tdd_expected_fail` tag inverts the test result so that CI passes while the bug is unfixed. Once bug #968 is fixed, the `@tdd_expected_fail` tag will be removed and the tests will serve as permanent regression tests. Closes #978 ## Changes ### Behave Tests (`features/tdd_plan_explain_plan_id.feature`) - **Scenario 1: Plan explain succeeds when given a plan_id with decisions** — Mocks `DecisionService` so `get_decision(plan_id)` raises `DecisionNotFoundError` (the bug) and `list_decisions(plan_id)` returns decisions. Invokes `plan explain <plan_id>` via CliRunner and asserts rc=0 with decision details visible. Verifies `list_decisions` was called with the correct `plan_id`. - **Scenario 2: Plan explain with plan_id shows root decision question** — Same setup, asserts the root decision question appears in the output. Both scenarios currently fail (rc=1, proving the bug exists), but the `@tdd_expected_fail` tag makes CI pass. ### Robot Tests (`robot/tdd_plan_explain_plan_id.robot`) - **TDD Plan Explain Succeeds With Plan ID** — Integration test using a helper script that records decisions via `DecisionService`, invokes `plan explain <plan_id>` via subprocess, and asserts rc=0. - **TDD Plan Explain With Plan ID Shows Root Question** — Same setup, asserts the root decision question appears in the output. Both tests carry `tdd_expected_fail`, `tdd_bug`, and `tdd_bug_968` tags. ### Step Definitions (`features/steps/tdd_plan_explain_plan_id_steps.py`) Step implementations for the Behave feature, following the established patterns from `plan_explain_cli_coverage_steps.py`. Includes `list_decisions.assert_called_once_with(plan_id)` verification to ensure the fix exercises the fallback lookup path. ### Helper Script (`robot/helper_tdd_plan_explain_plan_id.py`) Python helper for Robot tests, following the established pattern from `helper_tdd_checkpoint_real_rollback.py`. Uses a shared `_run_plan_explain()` helper to avoid subprocess invocation duplication, sets `NO_COLOR=1` in subprocess environment to prevent ANSI escape codes. ### CHANGELOG Added entry under `## Unreleased` describing the TDD bug-capture tests for bug #968. ## Review Fixes Applied (Iteration 1) 1. **Imports moved to module level** (Major) — Three imports in `_setup_plan_with_decisions()` moved to module level with `# noqa: E402`, consistent with all other `robot/helper_tdd_*.py` files. 2. **Mock accurately models real behavior** (Minor) — `svc.get_decision.return_value = None` changed to `svc.get_decision.side_effect = DecisionNotFoundError(...)` to match the real `DecisionService.get_decision()` which raises `DecisionNotFoundError`, not returns `None`. 3. **Strengthened assertion** (Minor) — `assert "decision_id" in output or "question" in output` changed to AND for a more precise assertion that verifies both fields are present. 4. **Removed `# type: ignore[arg-type]`** (Minor) — Changed `kwargs: dict[str, object]` to `kwargs: dict` and removed the `# type: ignore[arg-type]` directive, per CONTRIBUTING.md type safety rules. 5. **Handled `subprocess.TimeoutExpired`** (Minor) — Both `subprocess.run()` calls wrapped in `try/except subprocess.TimeoutExpired` with descriptive `_fail()` messages. 6. **Resolved timeout race condition** (Minor) — Inner `subprocess.run` timeout reduced from 60s to 45s so the helper catches timeout before Robot's 60s outer timeout kills it. 7. **Defensive setup check** (Nit, addressed) — Added `list_decisions()` verification after `record_decision()` to distinguish setup failures from the actual bug. ## Review Fixes Applied (Iteration 2) 1. **Added CHANGELOG.md entry** (Major) — Added entry under `## Unreleased` describing the TDD bug-capture tests for bug #968, per CONTRIBUTING.md §"Pull Request Process". 2. **Fixed Gherkin step text and feature comments** (Minor) — Renamed step from "returns None" to "raises DecisionNotFoundError" in feature file and step decorator. Updated feature comment from "returns None" to "raises DecisionNotFoundError". Step text now accurately describes the mock behavior. 3. **Fixed Robot helper output assertion logic** (Minor) — Changed `if "decision" not in combined.lower() and "question" not in combined.lower()` to `or` so both keywords must be present, matching the Behave test's AND-based validation. 4. **Added `NO_COLOR=1` to subprocess environment** (Minor) — Added `_make_subprocess_env()` helper that copies `os.environ` and sets `NO_COLOR=1`, following the established pattern from `robot/helper_e2e_common.py`. 5. **Extracted shared subprocess helper** (Nit, addressed) — Deduplicated subprocess invocation blocks into `_run_plan_explain(plan_id)` function, reducing code duplication across the two test subcommands. 6. **Added `list_decisions` mock verification** (Nit, addressed) — Added `svc.list_decisions.assert_called_once_with(plan_id)` in the Behave step that checks decision details, making the test contract clearer. 7. **Rebased on current master** (Nit, addressed) — Branch rebased on `origin/master` per CONTRIBUTING.md §"Branch Hygiene". ## Quality Gates - ✅ `nox -e lint` — All checks passed - ✅ `nox -e typecheck` — 0 errors - ✅ `nox -e unit_tests` — 391 features, 11175 scenarios, 0 failed - ✅ `nox -e integration_tests` — All tests passed (including Tdd Plan Explain Plan Id) - ✅ `nox -e e2e_tests` — 16 tests, 0 failed - ✅ `nox -e coverage_report` — 97% coverage (≥97% threshold met) Reviewed-on: cleveragents/cleveragents-core#1052 Co-authored-by: Rui Hu <rui.hu@cleverthis.com> Co-committed-by: Rui Hu <rui.hu@cleverthis.com> |
||
|
|
cbf8bcc993 |
test(e2e): E2E acceptance criteria for M5 (v3.4.0) — ACMS v1 and context scaling (#811)
## Summary Add `robot/e2e/m5_acceptance.robot` with **21 zero-mock E2E test cases** (in addition to existing M5 test suite) covering all M5 (v3.4.0) acceptance criteria: 1. **Context Assembly** — add/list/show/clear files in the context pipeline 2. **Context Scaling** — 10,000+ file project setup with simulate plumbing *(structural)* 3. **Context Policy Configuration** — per-view include/exclude paths, file-size limits 4. **Budget Enforcement** — max_file_size / max_total_size constraint storage *(structural)* 5. **Context Analysis** — ACMS pipeline inspect (tier schema) and simulate (JSON schema) *(structural)* 6. **Plan Execution** — real LLM calls via `openai/gpt-4o-mini` (`plan use` + `plan resume`) ### Structural vs. Behavioural Scope Tests in sections 1b–4 that use `project context simulate` or `inspect` are **structural / plumbing validations** — they verify CLI execution, JSON serialization, and stored configuration but do **not** exercise actual ACMS indexing or budget enforcement because the `ContextTierService` is an in-memory singleton that starts empty per CLI process. Each affected test has a `[Documentation]` note explaining this limitation. Behavioural ACMS validation is deferred until the full indexing pipeline is wired. ### Production Bug Fixes | Fix | File | Description | |-----|------|-------------| | `session.flush()` → `session.commit()` | `project_context.py` | Policy changes silently lost on `session.close()` | | `contextlib.suppress` rollback wrapper | `project_context.py` | Prevents rollback failure from masking original commit exception | | Add `session_factory` DI provider | `container.py` | `project context` commands hit `AttributeError` | | `providers.Factory` → `providers.Singleton` | `container.py` | Avoid creating duplicate engines per call | | Add Gemini API key pattern | `redaction.py` | `AIzaSy...` keys now redacted in logs | ### Review Feedback Addressed (Tenth Pass — @CoreRasurae Review #2410) | # | Severity | Finding | Fix | |---|----------|---------|-----| | P3-1 | Medium | "Clear Context" test tautological — never asserts files were present before clearing | Added `Should Contain ${list_before.stdout} config.py` precondition check after `context-load` and before `clear` | | P3-2 | Medium | Policy/budget verification uses substring matching (`Should Contain 262144`) | Replaced with `Extract JSON From Stdout` + `$rv.get('max_file_size') == 262144` parsed JSON assertions using `resolved_view` dict access | | P3-3 | Medium | Plan resume doesn't verify `phase` value, only existence | Added `Should Not Be Equal As Strings ${phase} queued` assertion to verify plan transitioned from queued | | P3-4 | Medium | Plan JSON extraction inconsistency (`rindex` vs `Extract JSON From Stdout`) | Replaced fragile `rindex`-based extraction with `Extract JSON From Stdout` keyword for consistency | | P3-6 | Medium | Context show summary weak content assertions | Added `Should Not Contain` guards against traceback/error output to reject false positives | | P3-8 | Medium | `_SafeSession` singleton may accumulate dirty state after rollback | Changed `_SafeSession.close()` from pure no-op to `real.rollback()` to reset session state between calls | | P3-14 | Medium | No test for `_save_policy_json` rollback path | Added BDD scenario "Save policy rollback re-raises after commit failure" with monkey-patched commit | | P3-15 | Medium | No test for `_save_policy_json` on nonexistent project | Added BDD scenario "Save policy on nonexistent project row updates zero rows" verifying silent 0-row behavior | | P4-1 | Low | Plan resume TRY/EXCEPT swallows assertion details | Moved field assertions outside TRY block; TRY only guards JSON extraction | | P4-2 | Low | `Safe Parse Json Field` logs stale error context | Fixed to track and report both Strategy 1 and Strategy 2 error contexts separately | | P4-4 | Low | SQLite WAL/SHM files not cleaned in regression test | Added cleanup loop for `-wal` and `-shm` suffixes alongside `.db` file | ### Deferred Items (Out of Scope) | ID | Severity | Reason | |----|----------|--------| | P2-1 | High | `execution_environment` silently dropped on subsequent `context set` — pre-existing production code bug in `_write_policy()`, not introduced by this PR | | P2-2 | High | Unhandled `ValidationError` on corrupt policy blob — pre-existing `_read_policy()` code, not changed by this PR | | P2-3 | High | Silent no-op UPDATE when `ns_projects` row missing — pre-existing `_save_policy_json` logic; this PR only changed error handling | | P3-5 | Medium | Structural tests cannot detect regressions — already honestly documented in every affected test's `[Documentation]` block | | P3-7 | Medium | View inheritance/override behavior not tested — nice-to-have, not in ticket acceptance criteria | | P3-9 | Medium | `context_set` double-writes when `execution_environment` set — pre-existing production logic | | P3-10 | Medium | `budget_tokens=0` silently replaced by default (falsy `or`) — pre-existing production code bug | | P3-11 | Medium | `context set` replaces entire view instead of merging — pre-existing design choice | | P3-12 | Medium | GEMINI_API_KEY propagated but potentially unused — security-first: propagating for redaction testing | | P3-13 | Medium | `reset_container()` doesn't dispose Singleton resources — pre-existing container lifecycle issue | | M5 | Medium | `_build_session_factory` engine never disposed — production code architecture, out of scope for testing ticket | | M6 | Medium | Missing `check_same_thread`/`isolation_level` — production code architecture, out of scope for testing ticket | | L1 | Low | `plan resume` not in spec CLI synopsis — informational | | L2 | Low | Context summary assertions depend on exact CLI wording — acceptable stability risk | | L3 | Low | Gemini regex minimum length slightly loose — acceptable security-first trade-off | | L4 | Low | Missing Google OAuth2 credential patterns — out of scope for this PR | | P4-3 | Low | `Run CLI` keyword duplicated — different purpose (uses `${WS}` as default cwd), not a true duplicate | | P4-5–P4-9 | Low | Various additional E2E coverage gaps — nice-to-have, not in ticket acceptance criteria | ### Quality Gates | Gate | Result | |------|--------| | lint | PASS | | typecheck | PASS (0 errors) | | unit_tests | **393/393** features, 11,210 scenarios | | integration_tests | **1,576/1,576** | | e2e_tests | **37/37** (21 M5 + 12 M6 + 2 smoke + 2 M1) | | coverage_report | **97%** (threshold: 97%) | ### Files Changed | File | Change | |------|--------| | `robot/e2e/m5_acceptance.robot` | **NEW** — 21 E2E test cases with honest structural documentation, parsed JSON assertions, prerequisite skip guards on all sections, safe assertion messages | | `robot/e2e/common_e2e.resource` | `on_timeout=kill` + return code checks + safe key evaluation via `os.environ.get` + fixed stale error logging in `Safe Parse Json Field` | | `robot/e2e/m1_acceptance.robot` | `on_timeout=kill` on git log | | `robot/e2e/m2_acceptance.robot` | `on_timeout=kill` + return code checks + safe assertion messages (no stderr embedding) | | `src/cleveragents/application/container.py` | Add `_build_session_factory` + `session_factory` Singleton | | `src/cleveragents/cli/commands/project_context.py` | `flush()` → `commit()` + `contextlib.suppress` rollback | | `src/cleveragents/shared/redaction.py` | Add Gemini API key pattern | | `noxfile.py` | Propagate `GEMINI_API_KEY` in e2e_tests | | `CHANGELOG.md` | 4 entries for #745 | | `features/application_container_coverage_boost.feature` | Updated title + 3 scenarios | | `features/steps/application_container_coverage_boost_steps.py` | Step defs for `_build_session_factory` | | `features/consolidated_security.feature` | 2 Gemini API key redaction scenarios | | `features/project_context_cli_coverage_boost.feature` | `flush()→commit()` regression test + rollback path + nonexistent project tests | | `features/steps/project_context_cli_coverage_boost_steps.py` | Separate engines for regression test + `try/finally` cleanup + `_SafeSession.close()` state reset + rollback/nonexistent test steps + WAL/SHM cleanup | Closes #745 ISSUES CLOSED: #745 Reviewed-on: cleveragents/cleveragents-core#811 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> |
||
|
|
774dfedc6b |
test: add TDD bug-capture test for #967 — plan execute phase processing (#1050)
## Summary This PR adds TDD bug-capture tests for bug #967 — `plan execute` only transitions state without running strategize or execute phase processing. ### Motivation Bug #967 describes that the `plan execute` CLI command originally only called `service.execute_plan(plan_id)`, which is a state transition only (Strategize/COMPLETE → Execute/QUEUED). When a plan was in Strategize/QUEUED state (immediately after `plan use`), the command failed because `execute_plan()` requires Strategize/COMPLETE. The CLI should detect the plan's current phase and run `PlanExecutor.run_strategize()` before transitioning. Per the project's TDD Bug Fix Workflow (`CONTRIBUTING.md`), the first step in fixing any bug is to write a test that captures the buggy behavior. Since the fix for #967 is already present in the codebase (the CLI handler already orchestrates properly), the `@tdd_expected_fail` tags have been removed and these tests serve as **permanent regression guards** ensuring the fix is never reverted. ### Design Approach **All `@tdd_expected_fail` scenarios were rewritten to exercise the CLI orchestration layer** — the actual code path affected by bug #967. This satisfies AC4: "The test is specific enough that it will pass normally (without the tag) only when the bug is genuinely fixed." - **Scenarios 1, 2, 4** (previously `@tdd_expected_fail`): Use Typer's `CliRunner` with mocked services to invoke the `plan execute` CLI command handler directly. This tests the orchestration logic in `plan.py` — the exact code that was buggy. - **Scenario 3** (positive control): Uses real `PlanLifecycleService` (in-memory) and `PlanExecutor` (stub actors) to demonstrate that proper service-level orchestration works. - **Robot tests**: Replicate the CLI orchestration logic using real services to verify at the integration level. ### Changes #### Behave Unit Tests - `features/tdd_plan_execute_phase_processing.feature` — 4 scenarios - `features/steps/tdd_plan_execute_phase_processing_steps.py` — Step definitions using CliRunner and mocked services **Scenarios:** 1. **CLI execute command handles plan in Strategize/QUEUED state** — Invokes `plan execute` via CliRunner on a QUEUED plan. Verifies the CLI succeeds and the plan reaches Execute phase. 2. **CLI execute command orchestrates full lifecycle for QUEUED plan** — Verifies `run_strategize()` and `run_execute()` are both called by the CLI handler. 3. **Positive control — proper orchestration transitions QUEUED plan to Execute** — Demonstrates that `run_strategize()` → `execute_plan()` works correctly at the service level. Always passes. 4. **CLI auto-discovery finds plans in Strategize/QUEUED state** — Invokes `plan execute` with no plan_id. Verifies the auto-discovery filter includes QUEUED plans. #### Robot Integration Tests - `robot/tdd_plan_execute_phase_processing.robot` — 4 test cases matching the Behave scenarios - `robot/helper_tdd_plan_execute_phase_processing.py` — Helper script replicating CLI orchestration logic #### CHANGELOG - `CHANGELOG.md` — Added entry under "## Unreleased" describing the new tests. ### Quality Gates - `nox -e lint`: ✅ passed - `nox -e typecheck`: ✅ passed (0 errors) - `nox -e unit_tests`: ✅ passed (391 features, 11177 scenarios, 0 failures) - `nox -e integration_tests`: ✅ passed (1572 tests, 0 failures) - `nox -e e2e_tests`: ✅ passed (16 tests, 0 failures) - `nox -e coverage_report`: ✅ 97% coverage ### Review Cycle 2 Fixes - **Critical #1**: Rewrote `@tdd_expected_fail` scenarios to exercise the CLI orchestration layer via CliRunner instead of testing service/executor APIs that are correct by design. Removed `@tdd_expected_fail` tags since the bug fix is already in the codebase. - **Major #2**: Added CHANGELOG entry. - **Major #3**: Rebased onto current `master`. - **Minor #4**: Added `ProcessingState.QUEUED` assertion to positive control scenario. - **Minor #5**: Narrowed exception handler from bare `except Exception` to `except (PlanError, PlanNotReadyError)`. - **Minor #7**: Changed Robot suite to use `Setup Test Environment With Database Isolation`. - **Minor #8**: Added `on_timeout=kill` to all Robot `Run Process` calls. - **Minor #12**: Added docstring to `_fail()` helper. - **Minor #13**: Removed redundant `Settings()` instantiation. ### Known Limitations - The `@tdd_expected_fail` tags were removed because the bug fix for #967 is already in the codebase. If this PR is merged before the #967 fix PR, the tags would need to be re-added. However, the CHANGELOG and existing CLI code confirm the fix is already on `master`. Closes #977 Reviewed-on: cleveragents/cleveragents-core#1050 Co-authored-by: Rui Hu <rui.hu@cleverthis.com> Co-committed-by: Rui Hu <rui.hu@cleverthis.com> |
||
|
|
747d8d3c9a |
test(cli): TDD failing tests for init --yes non-interactive (bug #783) (#1049)
## Summary Adds TDD bug-capture tests proving that `agents init --yes` fails to bypass the migration approval prompt in a TTY environment (bug #783). These tests follow the mandatory TDD bug-fix workflow defined in CONTRIBUTING.md §Bug Fix Workflow. ### Changes - **Behave feature** (`features/tdd_init_yes_no_input.feature`): Two scenarios tagged `@tdd_expected_fail @tdd_bug @tdd_bug_783` that invoke `agents init --yes` in a fresh environment with the migration prompt simulated as declining (TTY with no input). The tests verify the command exits successfully and does not display the "Apply migrations now?" prompt. - **Behave steps** (`features/steps/tdd_init_yes_no_input_steps.py`): Step definitions that create an isolated temp directory, clear auto-apply and database-URL environment variables, patch `sys.stdin` on the real `sys` module (correct mock target per project convention), and replace `MigrationRunner._default_prompt_for_migration` with a function that returns `False` (simulating a TTY prompt where the user declines). Temp directory and env var cleanup is registered via `context.add_cleanup()`. - **Robot Framework test** (`robot/tdd_init_yes_no_input.robot`): Two integration test cases tagged `tdd_bug`, `tdd_bug_783`, `tdd_expected_fail` matching the Behave scenarios. - **Robot helper** (`robot/helper_tdd_init_yes_no_input.py`): Helper script that exercises the same code path using the same mock strategy as the Behave steps. ### Bug Reproduction Mechanism The core challenge is that `CliRunner` replaces `sys.stdin` during `invoke()`, making direct `isatty()` patching ineffective for controlling the prompt path. The mock strategy addresses this with a two-pronged approach: 1. **`patch.object(sys, "stdin", mock_stdin)`** — Patches `sys.stdin` directly on the real `sys` module (correct mock target per the project's established pattern in `features/steps/migration_runner_steps.py`). Documents the intent of simulating a TTY. 2. **`patch.object(MigrationRunner, "_default_prompt_for_migration", ...)`** — Replaces the prompt function with one that returns `False` (simulating a TTY user declining migration). This is the mechanism that actually exercises the bug path, since CliRunner's stdin replacement bypasses the `isatty()` check. 3. **`CLEVERAGENTS_DATABASE_URL` with non-template filename** — Sets the database URL to a path whose filename does not match the `before_scenario` template-DB prefixes, forcing the real Alembic migration path instead of the test fast-path. With bug #783 present, `require_confirmation=True` is hardcoded in `UnitOfWork._ensure_database_initialized()`, so the prompt fires, returns `False`, causing `MigrationNotApprovedError` and a non-zero exit code. After the fix, `--yes` should bypass the prompt entirely. ### Root Cause (for the bug fix developer) `init_command()` in `cleveragents.cli.commands.project` receives the `--yes` flag but only uses it to control output format. It does not forward `yes` to the migration runner. The migration runner's `init_or_upgrade()` is called via `unit_of_work._ensure_database_initialized()` with `require_confirmation=True` hardcoded. ### Scenario 2 Limitation While the bug is present, Scenario 2's first assertion (exit code 0) fails and Behave skips subsequent steps. The "contains Initialized" assertion is only evaluated after the bug is fixed, providing distinct post-fix regression value. ### Quality Gate Results - `nox -s lint` — ✅ passed - `nox -s typecheck` — ✅ passed (0 errors) - `nox -s unit_tests` — ✅ passed (387 features, 11121 scenarios, 0 failed) - `nox -s integration_tests` — ✅ passed (1561 tests, 0 failed) - `nox -s e2e_tests` — ✅ passed (16 tests, 0 failed) - `nox -s coverage_report` — ✅ passed (97% coverage) Closes #842 Reviewed-on: cleveragents/cleveragents-core#1049 Co-authored-by: Rui Hu <rui.hu@cleverthis.com> Co-committed-by: Rui Hu <rui.hu@cleverthis.com> |
||
|
|
ad98d41d61 |
feat(a2a): implement _cleveragents/ extension method routing
Add spec-aligned _cleveragents/ prefixed extension method routing to the A2A local facade per ADR-047. The facade now supports 42 total operations: 31 new extension methods across 6 families plus 11 legacy proprietary names retained for backward compatibility. Extension method families implemented: - _cleveragents/plan/* (13 methods): use, execute, apply, cancel, status, tree, explain, correct, diff, artifacts, prompt, rollback, list. Plan operations delegate to PlanLifecycleService when wired; new operations (cancel, tree, explain, correct, artifacts, prompt, rollback, list) have stub handlers returning safe defaults. - _cleveragents/registry/* (6 methods): tool/list, resource/list, actor/list, skill/list, action/list, project/list. Tool and resource list delegate to existing services; entity lists are stubs. - _cleveragents/context/* (4 methods): show (delegates to existing handler), inspect, simulate, set (stubs). - _cleveragents/health/* (2 methods): check, diagnostics/run. - _cleveragents/sync/* (3 methods): pull, push, status (stubs). - _cleveragents/namespace/* (3 methods): list, show, members (stubs). Legacy proprietary names (session.create, plan.create, etc.) continue to work via the same handler map, marked deprecated. Updated existing test assertions for the expanded operation count (11 -> 42). ISSUES CLOSED: #876 |
||
|
|
399939a6db |
refactor(db): migrate from create_all() to Alembic-managed schema migrations
Complete the Alembic migration infrastructure by adding CLI commands, improving stamp logic, and adding comprehensive lifecycle tests. Key changes: - Added agents db CLI command group (db.py) with 5 subcommands: migrate (autogenerate), upgrade, downgrade, current, history. All delegate to MigrationRunner which wraps Alembic command API. - Registered the db command group in main.py CLI registration. - Fixed legacy database stamp logic in MigrationRunner to stamp at "head" instead of "001_initial_schema" when pre-Alembic tables are detected. This avoids migration failures when create_all-produced tables already exist (migrations would try to CREATE TABLE and fail with "table already exists"). - Added commit() after stamp to ensure alembic_version is persisted before subsequent operations on the same in-memory database. - Added FakeConnection.commit() method to the mock test infrastructure to support the new commit call in the stamp path. - Added Behave feature (db_migration_lifecycle.feature) with 8 scenarios covering: forward migration, rollback, round-trip, CLI upgrade/current/downgrade, legacy stamp logic, and init_database schema validation. - Added vulture whitelist entries for new CLI commands. Note: init_database() still uses Base.metadata.create_all() as the primary schema creation path. Full migration to Alembic-only init is deferred until ORM model constraints are reconciled with migration scripts (action_arguments UniqueConstraint mismatch). ISSUES CLOSED: #941 |
||
|
|
21e9a65c33 |
fix(plan): implement end-to-end subplan execution orchestration
Replace the metadata-only subplan spawn with real child Plan domain object creation. SubplanService.spawn() now creates full Plan instances with proper PlanIdentity (linking parent_plan_id and root_plan_id), sets them to PlanPhase.STRATEGIZE / ProcessingState.QUEUED, and returns them in the SpawnResult.child_plans list for downstream lifecycle orchestration. Key changes: - SubplanService.spawn() now creates Plan domain objects for each spawn entry with complete PlanIdentity linking, inheriting the parent plan's namespace, actors, project links, definition_of_done, and access settings. - SpawnResult dataclass extended with child_plans: list[Plan] field to carry the created child plans alongside the existing metadata and statuses. - Parent plan's subplan_statuses list is updated with SubplanStatus entries tracking each child's lifecycle state. - Fixed Pyright type errors: added missing definition_of_done, reusable, read_only, and server parameters to Plan and NamespacedName constructors. - Removed @tdd_expected_fail tags from TDD test files since the bug is fixed. - Added Behave scenarios and Robot integration tests verifying child plan creation, lifecycle phase/state, and parent tracking. ISSUES CLOSED: #823 |
||
|
|
ec4c39aecf |
fix(plan): implement real checkpoint rollback via git reset
Replace the simulated rollback in CheckpointService.rollback_to_checkpoint() with real git operations. The method now executes git reset --hard <sandbox_ref> followed by git clean -fd inside the sandbox working directory, reverting all tracked file changes and removing untracked files added after the checkpoint. Key changes: - CheckpointService.rollback_to_checkpoint() now calls _git_reset_hard() and _git_clean() via subprocess against the sandbox path, enforcing sandbox boundary by confining all git operations to cwd=sandbox_path. - Added _resolve_sandbox_path() to extract sandbox validation into a dedicated method, supporting both lifecycle-service and in-memory fallback paths. - Added _validate_sandbox() to verify the sandbox path is a directory containing a .git subdirectory before executing git operations. - Added _git_changed_paths() to compute the diff between HEAD and the target ref before reset, providing accurate restored file counts in the result. - Domain event emission (CHECKPOINT_RESTORED) added via the optional event_bus, following the same EventBus protocol pattern used by other services. - Updated existing Robot Framework helper (helper_checkpoint_rollback.py) to use a real temporary git workspace instead of a fake sandbox path, since _validate_sandbox() now enforces that the path is a real git repository. - Removed @tdd_expected_fail tags from TDD test files since the bug is now fixed. - Added new Behave scenarios and Robot integration tests for real file reversion, file removal after rollback, domain event emission, and sandbox boundary enforcement. ISSUES CLOSED: #822 |
||
|
|
b8f9da4cca
|
fix(tui/persona): lock delete operation in registry
Wrap `PersonaRegistry.delete()` with the personas file lock and use race-safe `unlink` handling for concurrent save/delete consistency. |
||
|
|
d9e51d98f8
|
fix(tui,cli,tests): harden persona/input modes and stabilize parallel test execution
- Add shell safety controls for REPL/TUI (`looks_dangerous`, confirmation gate, timeout handling, and env-based shell disable guard). - Secure persona workflows with strict name/path validation, safe import/export resolution, atomic+locked registry writes, and malformed YAML resilience. - Unify persona models and wiring by reusing canonical TUI schema/registry, adding DI providers, and lazy-loading TUI exports to avoid circular imports. - Improve reference discovery with ignored-directory filtering, symlink-safe walking, and TTL caching for CLI/TUI reference catalogs. - Expand Behave/Robot coverage for safety/error paths and parallel-isolation behavior; add shared `features/mocks/fake_repl_input.py` helper. - Fix parallel-run flakiness via deterministic cleanup/reload patterns and watchdog polling fallback when inotify limits are reached. ISSUES CLOSED: #695 |
||
|
|
c02f3842ae
|
feat(tui): implement persona system and reference/command input modes
- Add `agents tui` command wiring and Textual app scaffolding for interactive TUI startup. - Implement TUI persona schema/registry/state with local YAML persistence and per-session persona binding. - Add three input-mode flows: Normal (`@` references), Command (`/` slash commands), and Shell (`!` passthrough). - Introduce TUI widgets/overlays for prompt, persona bar, reference picker, and slash command interactions. - Extend REPL command routing to support persona/session commands and reference/shell dispatch behavior. - Add test coverage: Behave features + step definitions, Robot TUI smoke test, and ASV fuzzy-reference benchmark. ISSUES CLOSED: #695 |
||
|
|
48ecf4c00c |
fix(cli): add --execution-env-priority flag to plan use (#972)
## Summary
Adds the missing `--execution-env-priority` flag to the `agents plan use` command, aligning the CLI with the specification (spec line 12501). The flag accepts `fallback` (default) or `override` and controls execution environment routing precedence per ADR-043:
- **`override`**: The specified execution environment always wins, bypassing devcontainer auto-detection.
- **`fallback`**: The specified environment defers to auto-detected devcontainers or project-level overrides.
### Changes
- **Domain model** (`cleveragents.domain.models.core.plan`):
- Added `ExecutionEnvPriority` StrEnum with `FALLBACK`/`OVERRIDE` values.
- Changed `execution_env_priority` field type to `ExecutionEnvPriority | None` (leverages Pydantic enum validation).
- Added `@model_validator` enforcing that `execution_env_priority` requires `execution_environment` (domain-level fail-fast invariant). Verified `validate_assignment=True` is set on `Plan.model_config`.
- Updated `Plan.as_cli_dict()` to include `execution_environment` and `execution_env_priority`, with fallback default of `"fallback"` for pre-migration data where `execution_env_priority` is `None`.
- **CLI** (`cleveragents.cli.commands.plan`):
- Added `--execution-env-priority` parameter to `use_action`.
- Validation: priority requires `--execution-environment`, enum value validation, case-insensitive input.
- Defaults to `"fallback"` when `--execution-environment` is set without explicit priority.
- Updated `_print_lifecycle_plan` and `_plan_spec_dict` to display the priority, defaulting to `"fallback"` for pre-migration data.
- Updated `_plan_spec_dict` docstring to mention new keys.
- Hoisted `ExecutionEnvPriority` import to function-entry deferred imports in both `_plan_spec_dict` and `_print_lifecycle_plan` (no longer conditional on execution environment being set).
- Guarded `service.save_plan(plan)` with `has_overrides` flag so it is only called when CLI overrides were actually applied.
- **Persistence** (`cleveragents.infrastructure.database`):
- Added `execution_environment` (`String(255)`, nullable) and `execution_env_priority` (`String(20)`, nullable) columns to `LifecyclePlanModel`.
- Updated `from_domain()`/`to_domain()` for round-trip serialization including `ExecutionEnvPriority` enum reconstruction. Uses direct attribute access (`plan.execution_environment`) instead of defensive `getattr` — Plan fields always exist on the Pydantic BaseModel.
- Updated `LifecyclePlanRepository.update()` to persist both fields using direct attribute access.
- Added Alembic migration `m4_003_plan_env_columns` adding both columns to the `v3_plans` table. Uses `String(255)` for `execution_environment` to accommodate namespaced resource names. Descends from `m6_005_profile_guards_json`.
- **Service** (`cleveragents.application.services.plan_lifecycle_service`):
- Added `save_plan()` public convenience method for callers that need to re-persist after post-creation mutations.
- **Tests**:
- 18 Behave scenarios covering:
- CLI acceptance criteria (valid values, defaults, validation errors, output display, case-insensitive input, service invocation with `call_args` verification).
- Domain model validator invariant: construction with priority but no environment raises `ValueError`; construction with both fields succeeds.
- `ExecutionEnvPriority` enum: values verification, `StrEnum` subclass assertion.
- `Plan.as_cli_dict()`: includes both fields when set, omits both when `None`, defaults priority to `"fallback"` for pre-migration data.
- DB round-trip serialization: `from_domain()` → `to_domain()` preserves both fields; preserves `None` values.
- 5 Robot Framework integration tests.
- Updated pre-existing `SimpleNamespace`-based plan test fixtures in `database_models_lifecycle_coverage_steps`, `database_models_new_coverage_steps`, `database_models_coverage_r2_steps`, and `repositories_error_handling_coverage_steps` to include `execution_environment` and `execution_env_priority` attributes.
- Simplified Robot helper `sys.path` pattern to standard approach.
- **Changelog**: Updated per CONTRIBUTING.md requirements.
### Review Fixes (Brent Edwards, Review #2384)
- **P2 #1 — Defensive `getattr`**: Replaced `getattr(plan, "execution_environment", None)` with direct `plan.execution_environment` access in both `LifecyclePlanModel.from_domain()` and `LifecyclePlanRepository.update()`. Plan is a Pydantic BaseModel with `default=None`, so the field always exists. Updated 4 pre-existing `SimpleNamespace`-based test fixtures to include the new attributes.
- **P2 #2 — Late conditional import**: Hoisted `ExecutionEnvPriority` import from inside conditional blocks to function-entry deferred imports in both `_plan_spec_dict` and `_print_lifecycle_plan`.
- **P3 — Migration naming**: Acknowledged as deferred (cosmetic only). Updated `m4_003` to descend from `m6_005_profile_guards_json` (post-rebase chain fix).
- **Rebase**: Branch rebased onto latest `master` with merge conflict in `CHANGELOG.md` resolved.
### Deferred Items
- **Partial failure atomicity** (#8 from review): If `save_plan()` fails after `use_action()` succeeds, the plan exists in the DB without CLI overrides. This requires service-layer restructuring beyond the scope of this ticket.
- **Alembic migration naming** (#11 from review): Migration `m4_003` depends on `m6_005`, creating non-sequential naming. Renaming an existing migration risks breaking the chain for anyone who has already applied it.
### Quality Gates
| Session | Result |
|---------|--------|
| lint | PASS |
| typecheck | PASS (0 errors) |
| unit_tests | PASS (11,125 scenarios, 0 failures) |
| integration_tests | PASS (1,562 tests, 0 failures) |
| coverage_report | 97% (threshold: 97%) |
Closes #886
Reviewed-on: cleveragents/cleveragents-core#972
Reviewed-by: Brent Edwards <brent.edwards@cleverthis.com>
Co-authored-by: Rui Hu <rui.hu@cleverthis.com>
Co-committed-by: Rui Hu <rui.hu@cleverthis.com>
|