fix(cli): revert plan start and plan show aliases that contradict v3 spec
CI / lint (pull_request) Successful in 1m2s
CI / build (pull_request) Successful in 55s
CI / typecheck (pull_request) Failing after 1m28s
CI / helm (pull_request) Successful in 30s
CI / quality (pull_request) Successful in 1m43s
CI / security (pull_request) Successful in 1m48s
CI / coverage (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 26s
CI / e2e_tests (pull_request) Failing after 4m30s
CI / unit_tests (pull_request) Failing after 4m52s
CI / docker (pull_request) Has been skipped
CI / integration_tests (pull_request) Failing after 5m10s
CI / status-check (pull_request) Failing after 3s
CI / lint (pull_request) Successful in 1m2s
CI / build (pull_request) Successful in 55s
CI / typecheck (pull_request) Failing after 1m28s
CI / helm (pull_request) Successful in 30s
CI / quality (pull_request) Successful in 1m43s
CI / security (pull_request) Successful in 1m48s
CI / coverage (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 26s
CI / e2e_tests (pull_request) Failing after 4m30s
CI / unit_tests (pull_request) Failing after 4m52s
CI / docker (pull_request) Has been skipped
CI / integration_tests (pull_request) Failing after 5m10s
CI / status-check (pull_request) Failing after 3s
Revert the 'agents plan start' and 'agents plan show' command aliases because the v3 specification mandates 'agents plan use' and 'agents plan status'. The spec explicitly states that the 'use' verb is the command that transitions a plan from the Action phase into Strategize, and there are 86 occurrences of 'plan use' in docs/specification.md with zero for 'plan start'. The aliases diverged from the authoritative specification rather than aligning with it, creating competing API surfaces. ISSUES CLOSED: #8628
This commit is contained in:
+244
-172
@@ -5,13 +5,179 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- **CI coverage job now waits for unit_tests** (#10714): Added `unit_tests` to the
|
||||
`needs` list of the `coverage` job in `ci.yml`. Previously the coverage job ran
|
||||
in parallel with unit tests, which could produce misleading pass results when
|
||||
tests were still in-flight or had already failed. Coverage now only starts after
|
||||
unit tests succeed, eliminating redundant parallel test execution and ensuring
|
||||
coverage results are always meaningful.
|
||||
|
||||
- **Diagnostics spec examples expanded to all 9 providers** (#5320): Updated the
|
||||
`agents diagnostics` command examples in the specification to show all 9 supported
|
||||
providers (OpenAI, Anthropic, Google, Gemini, Azure, OpenRouter, Cohere, Groq,
|
||||
Together), matching the implementation from PR #3469. Rich, plain, JSON, and YAML
|
||||
example outputs now reflect comprehensive provider coverage with accurate warning
|
||||
counts and per-provider recommendations.
|
||||
|
||||
### Added
|
||||
|
||||
- **Plan CLI Spec Alignment** (#8628): Added `agents plan start` as an alias for
|
||||
`agents plan use` and `agents plan show` as an alias for `agents plan status`
|
||||
to match the v3 specification. Both commands delegate to their canonical
|
||||
counterparts while maintaining full feature parity. Updated module docstring
|
||||
to document the new aliases.
|
||||
- **TDD: MCPToolAdapter.infer_resource_slots() TypeError with null properties** (#10470):
|
||||
Added a TDD issue-capture Behave scenario that reproduces the bug where
|
||||
`MCPToolAdapter.infer_resource_slots()` raises `TypeError` when the input schema
|
||||
contains `{"properties": None}`. The test is tagged `@tdd_expected_fail` and will
|
||||
pass (by inversion) until the underlying bug is fixed.
|
||||
|
||||
- **Architecture Pool Supervisor Milestone Assignment** (#7521): Added a "PR Workflow
|
||||
for Major Changes" section to the `architecture-pool-supervisor` agent definition
|
||||
documenting the milestone assignment step for spec PRs. The agent now has
|
||||
`forgejo_update_pull_request` permission to assign PRs to the current active
|
||||
milestone after creation, improving traceability of specification changes within
|
||||
project milestone planning. Includes BDD test coverage for the new workflow
|
||||
documentation and permission configuration.
|
||||
|
||||
- **Git Worktree TOCTOU Race Condition** (#7507): Fixed a Time-Of-Check-To-Time-Of-Use
|
||||
(TOCTOU) race condition in `git_worktree.py` that could cause `git worktree add`
|
||||
operations to fail under concurrent execution. The fix replaces the unsafe
|
||||
`mkdtemp()` + `rmdir()` pattern with a parent-directory approach that maintains
|
||||
the OS-level uniqueness guarantee throughout the entire operation. The parent
|
||||
temporary directory is now persisted and properly cleaned up on both success and
|
||||
failure paths. Comprehensive BDD test coverage validates the fix under concurrent
|
||||
execution and confirms proper cleanup behavior.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Built-in actors v3 YAML format** (#10883): Fixed `agents actor run` failing for
|
||||
built-in actors (e.g., `openai/gpt-4`, `anthropic/claude-3-opus`) due to missing
|
||||
v3 `type` field in stored configuration. `ActorRegistry.ensure_built_in_actors()`
|
||||
now generates and persists v3 YAML text with `type: llm` and `description` fields,
|
||||
ensuring built-in actors work identically to custom actors. The
|
||||
`_generate_builtin_actor_yaml()` helper creates spec-compliant YAML that passes
|
||||
`ReactiveConfigParser._is_v3_format()` validation. Includes BDD scenarios and unit
|
||||
tests covering YAML generation, schema validation, and multiple provider handling.
|
||||
|
||||
- **Atomic `server_connect` config writes** (#993): Fixed `server_connect` in
|
||||
`cli/commands/server.py` to write all three config values (`server.url`,
|
||||
`server.namespace`, `server.tls-verify`) atomically. A snapshot of the config
|
||||
file is taken before any writes; if any `set_value()` call fails, the snapshot is
|
||||
restored and compensating `CONFIG_CHANGED` events are emitted for already-applied
|
||||
keys so the audit trail reflects the rollback. Added `emit_config_changed()` helper
|
||||
to `ConfigService` for decoupled event emission in rollback flows. Added
|
||||
`close()` method to `ReactiveEventBus` for proper resource cleanup in tests.
|
||||
Resolved merge conflict in `config_service.py` integrating the PR's
|
||||
`emit_config_changed()` helper with master's scoped config infrastructure.
|
||||
Removed `# type: ignore[assignment]` by introducing a typed `_AutoDiscover`
|
||||
sentinel class. BDD regression coverage in
|
||||
`features/tdd_server_connect_atomic_writes.feature`.
|
||||
|
||||
- **Atomic `load_from_metadata` for Autonomy Guardrails** (#7504): Fixed
|
||||
`AutonomyGuardrailService.load_from_metadata()` to validate both
|
||||
`AutonomyGuardrails` and `GuardrailAuditTrail` models before writing either
|
||||
to state, ensuring atomic updates. Previously, a validation failure on the
|
||||
audit trail after guardrails were already written would leave the system in
|
||||
an inconsistent state with partial updates. The method now uses a two-phase
|
||||
validate-then-write approach: all model validation occurs in Phase 1, and
|
||||
state mutations only happen in Phase 2 after all validations succeed.
|
||||
|
||||
- **ReactiveConfigParser route synthesis for v3 actors** (#10807): Fixed
|
||||
`agents actor run` silently returning empty output for v3 `type:llm` actors.
|
||||
`_build_from_v3()` and `_build()` now synthesise a default single-node
|
||||
graph route when agents are created without explicit routes, ensuring
|
||||
`run_single_shot()` can invoke the LLM via `GraphExecutor`. The nested
|
||||
`actors:` map format also translates the v3 `actor: "provider/model"` key
|
||||
into separate `provider` and `model` keys so the correct LLM provider is
|
||||
instantiated.
|
||||
|
||||
- **ActorRegistry.add() spec-compliant YAML support** (#4466): The registry now
|
||||
accepts actor YAML using the spec's `actors:` map format with nested `config:`
|
||||
blocks, in addition to the legacy top-level `provider`/`model` format. The
|
||||
`unsafe` flag and graph descriptor from nested config are now correctly
|
||||
preserved during registration. Multi-actor YAML (>1 entry in `actors:`/`agents:`
|
||||
map) is now rejected by `add()` with a `ValidationError`. Nested
|
||||
`config.options` are now correctly preserved. The `unsafe` coercion now uses
|
||||
strict `is True or == 1` instead of `bool()` to prevent truthy non-boolean
|
||||
YAML values (e.g. `unsafe: "no"`) from being treated as unsafe.
|
||||
|
||||
- **UKO Runtime Layer 2 (Paradigm) Indexing** (#9351): Added missing `rdf:type
|
||||
uko-oo:Class` triple emission in `PythonAnalyzer._extract_class()` so that
|
||||
Python class definitions are now correctly classified at layer 2 (paradigm/OO)
|
||||
in addition to layer 3 (technology). Added the corresponding Behave scenario
|
||||
`Indexing a Python file populates layer 2 (paradigm)` to
|
||||
`features/uko_runtime.feature`, completing four-layer guarantee verification
|
||||
for the UKO runtime.
|
||||
|
||||
- **Actor CLI v3 YAML Schema Support** (#6283): Fixed three components to add
|
||||
full v3 `ActorConfigSchema` support to the actor CLI registration and
|
||||
execution paths. `ActorConfiguration.from_blob()` now detects v3 format
|
||||
(top-level `type` key of `llm`/`graph`/`tool`) and correctly extracts
|
||||
provider, model, and graph descriptors — including `type: tool` actors
|
||||
without a `model` field. `ActorRegistry.add()` validates against the full
|
||||
Pydantic v2 schema, persists `skills`/`lsp`/`description` in the config
|
||||
blob, and compiles graph actors with proper metadata.
|
||||
`ReactiveConfigParser._build_from_v3()` now uses correct `source`/`target`
|
||||
edge keys (fixing `KeyError` in `to_graph_config()`), handles `config: null`
|
||||
nodes without crashing, propagates `context_view`/`memory`/`context`/
|
||||
`env_vars`/`response_format`/`lsp_capabilities`/`lsp_context_enrichment`
|
||||
into agent configs, and validates `entry_node` against the nodes map.
|
||||
Exception handling narrowed from broad `except Exception` to specific
|
||||
`NotFoundError` and `ActorCompilationError`. v3 registration logic
|
||||
extracted to `v3_registry.py` to keep `registry.py` under the 500-line
|
||||
limit. 19 BDD scenarios cover all v3 paths including tool actors,
|
||||
update mode, LSP dict bindings, and field propagation.
|
||||
|
||||
- **TDD Non-AssertionError Guard Visibility** (#8294): `apply_tdd_inversion` in
|
||||
`features/environment.py` now emits its non-assertion exception guard warning to
|
||||
both the structured logger and `stderr` via a new `_warning_with_stderr` helper.
|
||||
This makes the guard firing visible in standard Behave console output and CI log
|
||||
snippets where the structured logging sink may not be displayed. BDD infrastructure
|
||||
coverage added: a new scenario in `tdd_expected_fail_infrastructure.feature`
|
||||
asserts that the warning is emitted to stderr when a non-AssertionError exception
|
||||
is encountered in an `@tdd_expected_fail` scenario, and a second scenario asserts
|
||||
the warning is NOT emitted when the exception is an `AssertionError`. The
|
||||
`CONTRIBUTING.md` now documents that `@tdd_expected_fail` step definitions must
|
||||
signal expected failures via `AssertionError`.
|
||||
|
||||
- **Parallel Behave Runner Log Noise Reduction** (#8351): The parallel behave
|
||||
runner now suppresses captured stdout/stderr for passing worker chunks and
|
||||
only replays diagnostics for failed, errored, or crashed chunks. This makes
|
||||
failure output significantly easier to spot in CI and local runs. A worker
|
||||
crash (unhandled exception) is detected via an all-zero summary and the
|
||||
captured traceback is always surfaced.
|
||||
|
||||
- **Bug Hunt Pool Supervisor Non-Blocking Tracking**: Updated `bug-hunt-pool-supervisor` to make the automation tracking step non-blocking. The `automation-tracking-manager` call in step 5 is now best-effort — if it does not complete within a reasonable time or fails, the supervisor skips it and continues to the next cycle. Added explicit rule 9 clarifying that tracking must never block the main loop. Core functionality (module scanning and worker dispatch) takes priority over status reporting.
|
||||
|
||||
- **Name Validator Server-Qualified Format** (#9074): Updated actor, skill, and tool name
|
||||
validators to accept the spec-required `[[server:]namespace/]name` format. Previously,
|
||||
server-qualified names like `dev:freemo/custom-analysis` were incorrectly rejected.
|
||||
Added BDD scenarios for server-qualified name acceptance and rejection. All three
|
||||
validators (`ActorConfigSchema.validate_name`, `NAMESPACED_NAME_RE`,
|
||||
`_TOOL_NAME_PATTERN`) now correctly support optional server prefixes while maintaining
|
||||
backward compatibility with existing `namespace/name` names.
|
||||
|
||||
- **Automation Profile Silent Fallback** (#8232): `_resolve_profile_for_plan` in
|
||||
`PlanLifecycleService` now raises a clear `ValidationError` when a plan's
|
||||
automation profile name is not a known built-in profile, instead of silently
|
||||
falling back to `"manual"`. Users who configured custom automation profiles
|
||||
(e.g. `"semi-auto"`, `"acme/strict"`) will now receive an actionable error
|
||||
message listing available built-in profiles. The resolved profile name is also
|
||||
logged at debug level for observability.
|
||||
|
||||
### Added
|
||||
- Wired `StrategyActor` into the real plan execution path: `_get_plan_executor`
|
||||
in `plan.py` now resolves the strategy actor via `resolve_strategy_actor()`
|
||||
(reading the `actor.default.strategy` config key) instead of always
|
||||
constructing `LLMStrategizeActor`. `run_strategize` in `PlanExecutor` now
|
||||
passes `resources` (derived from `plan.project_links`) and `project_context`
|
||||
to the actor so the LLM prompt receives full project context. Strategy
|
||||
decisions are serialised as JSON in `plan.error_details["strategy_decisions_json"]`
|
||||
so `_build_decisions` can reconstruct the full hierarchy (dependency ordering,
|
||||
parent/child structure) during Execute instead of rebuilding from
|
||||
`definition_of_done`. `StrategizeStubActor.execute` accepts `**kwargs` for
|
||||
forward-compatibility. Added BDD coverage for the stored-JSON path,
|
||||
corrupt-JSON fallback, resource-passing, and stub extra-kwargs scenarios.
|
||||
(#828)
|
||||
|
||||
|
||||
- **TDD Issue-Capture Test Activation** (#7025): Replaced 234 bare `@skip` tags
|
||||
across 82 Behave feature files with the correct `@tdd_expected_fail @tdd_issue
|
||||
@@ -52,10 +218,10 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
`new-issue-creator`, and `issue-state-updater` now delegate all label operations to this
|
||||
subagent.
|
||||
|
||||
- **PR–Issue Label Synchronization**: PRs now inherit `Priority/`, `MoSCoW/`, `Points/`,
|
||||
- **PR-Issue Label Synchronization**: PRs now inherit `Priority/`, `MoSCoW/`, `Points/`,
|
||||
and `State/` labels from their associated issues at creation time
|
||||
(`pr-api-creator`). The `backlog-groomer` adds a continuous Pass 19 for ongoing
|
||||
PR–issue label synchronization. The `issue-state-updater` syncs PR state labels whenever
|
||||
PR-issue label synchronization. The `issue-state-updater` syncs PR state labels whenever
|
||||
issue states change.
|
||||
|
||||
- **Automation Tracking Announcements**: Extended `automation-tracking-manager` with
|
||||
@@ -69,14 +235,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
the `*-pool-supervisor` naming pattern. New agents added: `pr-editor` (safe PR editing
|
||||
with description preservation), `pr-manager` (unified PR interface), and
|
||||
`pr-merge-pool-supervisor` (automated PR merging supervisor). Renamed:
|
||||
`pr-api-creator` → `pr-creator`, `pr-checker` → `pr-ci-test-fixer`,
|
||||
`pr-status-checker` → `pr-status-analyzer`, `pr-self-reviewer` → `pr-reviewer`,
|
||||
`pr-fix-orchestrator` → `pr-fix-pool-supervisor`.
|
||||
`pr-api-creator` to `pr-creator`, `pr-checker` to `pr-ci-test-fixer`,
|
||||
`pr-status-checker` to `pr-status-analyzer`, `pr-self-reviewer` to `pr-reviewer`,
|
||||
`pr-fix-orchestrator` to `pr-fix-pool-supervisor`.
|
||||
|
||||
- **Automated PR Merging** (`pr-merge-pool-supervisor`): New supervisor continuously
|
||||
monitors for merge-ready PRs and merges them automatically when all criteria are met
|
||||
(approvals, CI passing, no conflicts). Supports both formal reviews and comment-based
|
||||
approvals (LGTM, ✅, "ready to merge", etc.).
|
||||
approvals (LGTM, ready to merge, etc.).
|
||||
|
||||
- **Implementation Worker Workflow Completion**: `implementation-worker` now implements
|
||||
work claiming protocols with conflict detection, comprehensive review feedback handling
|
||||
@@ -106,8 +272,21 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
`docs/development/automation-tracking.md` and the new
|
||||
`docs/development/docs-writer.md` reference.
|
||||
|
||||
- **ACMS / UKO API Documentation** (`docs/api/acms.md`): Added comprehensive API
|
||||
reference for the `cleveragents.acms` package covering the four-layer UKO ontology
|
||||
hierarchy, `VocabularyRegistry`, `ProvenanceInfo`, `UKOClass`, `UKOProperty`,
|
||||
`UKOVocabulary`, `Layer2Dependency`, `ParadigmVocabulary`, `DetailLevelMapBuilder`,
|
||||
and all Layer 3 language vocabulary types (Python, TypeScript, Rust, Java).
|
||||
The new page is linked from the API Reference index and the MkDocs navigation.
|
||||
|
||||
### Changed
|
||||
|
||||
- **`product-builder` Worker Allocation Tier Comments** (#8169): Clarified the
|
||||
`N_FULL` tier comment to explicitly document that PR fixing is handled by
|
||||
`implementation-pool-supervisor` via its PR-First Priority rule. Updated the
|
||||
`N_QUARTER` comment to enumerate the pools it covers (UAT, bug hunting, test
|
||||
infra). Prevents confusion about which supervisor handles PR fix work.
|
||||
|
||||
- **Decision Tree Full ULID Display** (#5825): The `agents plan tree` command now
|
||||
displays full 26-character ULIDs for all decisions instead of truncating them to
|
||||
8 characters. This enables users to copy decision IDs directly from tree output
|
||||
@@ -121,7 +300,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
- **PR Review Policy**: Reduced PR review requirement from 2 approvals to 1. Self-approval
|
||||
is now permitted including for automated bot PRs. Approval can be a formal review OR an
|
||||
approval comment (LGTM, Approved, ✅, "ready to merge").
|
||||
approval comment (LGTM, Approved, ready to merge).
|
||||
|
||||
- **Label Delegation Enforcement**: `automation-tracking-manager` now enforces delegation
|
||||
to `forgejo-label-manager` for all label operations, preventing "invalid label ID" errors
|
||||
@@ -139,6 +318,36 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Plan Concurrency Race Condition** (#7989): Fixed critical race condition in `execute_plan()` and
|
||||
`apply_plan()` where concurrent CLI/worker sessions could simultaneously modify the same plan,
|
||||
corrupting plan state. `LockService` is now wired into the plan lifecycle with plan-level advisory
|
||||
locking. Each invocation generates a unique caller identity (UUID) to prevent re-entrant lock
|
||||
acquisition by concurrent sessions on the same plan. Concurrent attempts now raise `LockConflictError`
|
||||
instead of silently racing. Lock is acquired before phase transition and released in a `finally`
|
||||
block to ensure cleanup even on error.
|
||||
|
||||
- **`--format color` ANSI Output** (#7910): Fixed `format_output` routing the `color` format
|
||||
option to `_format_plain`, which produced plain uncoloured text instead of ANSI escape
|
||||
sequences. The `color` format is now routed to `format_output_session` which uses the
|
||||
`ColorMaterializer` to emit proper ANSI-coloured output. `--format plain` and all other
|
||||
formats remain unaffected.
|
||||
|
||||
- **ContextTierService Thread Safety** (#7547): Added `threading.RLock` to
|
||||
`ContextTierService` to prevent `RuntimeError: dictionary changed size during
|
||||
iteration` and data corruption under concurrent plan execution. All public
|
||||
methods (`store`, `get`, `promote`, `demote`, `evict_lru`, `enforce_staleness`,
|
||||
`get_metrics`, `get_all_fragments`, `get_hot_fragments`, `get_for_actor`,
|
||||
`get_scoped_view`, `get_scoped_by_resource`, `get_scoped_metrics`) now acquire
|
||||
the reentrant lock before accessing the hot/warm/cold tier dicts. The service
|
||||
was previously documented as single-threaded but registered as a DI Singleton,
|
||||
causing potential data corruption when parallel subplans shared the same
|
||||
instance. The `TierRuntimeMixin.enforce_staleness()` and
|
||||
`ScopedTierMixin.get_scoped_by_resource()` / `get_scoped_metrics()` methods
|
||||
are also protected. The DI container registration as `providers.Singleton`
|
||||
is now correct and safe.
|
||||
|
||||
- **TOCTOU Race Condition in Git Worktree Sandbox** (#7507): Fixed Time-Of-Check-To-Time-Of-Use race condition in `GitWorktreeSandbox.create()` by replacing unsafe mkdtemp+rmdir pattern with persistent parent directory approach. Parent directory is now held throughout operation lifetime and properly cleaned up in all error paths (timeout, CalledProcessError, OSError) and in the cleanup() method, eliminating race window where another process could claim the worktree path. Comprehensive BDD coverage added for all error-path cleanup branches.
|
||||
|
||||
- **Validation Gate Empty-Run Guard** (#7508): Fixed `ApplyValidationSummary.all_required_passed`
|
||||
returning `True` when zero validations were run, silently bypassing the apply gate. The property
|
||||
now returns `False` when the validation result set is empty (`is_empty` is `True`), ensuring
|
||||
@@ -159,6 +368,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
`sandbox_root=.cleveragents/sandbox/`, so LLM file output (`FILE:` blocks)
|
||||
is written to disk during the execute phase. (#4222)
|
||||
|
||||
- **SubplanExecutionService fail_fast cancellation** (#7582): Fixed a race condition where
|
||||
already-running parallel subplans were not cancelled when `fail_fast` fired. Previously,
|
||||
`Future.cancel()` only prevented queued futures from starting but had no effect on
|
||||
in-flight futures that completed after `stop_flag` was set -- their `COMPLETE` results
|
||||
were incorrectly included in the merge output. The fix adds a post-completion guard that
|
||||
overrides any non-`ERRORED`/non-`CANCELLED` result to `CANCELLED` when `stop_flag` is
|
||||
active, and clears the associated output to prevent it from entering the merge. Also
|
||||
replaces the O(n) linear `status` lookup in the `as_completed()` loop with an O(1)
|
||||
`status_map` dict pre-computed before the executor block.
|
||||
|
||||
- **Robot Framework TDD Listener Guards** (#5436): Added three guard conditions to the
|
||||
`tdd_expected_fail_listener` `end_test()` function to prevent blindly inverting ALL test
|
||||
failures to passes, which was masking infrastructure errors and causing flaky CI behavior.
|
||||
@@ -167,6 +386,11 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
e2e test files and removed `tdd_expected_fail` from 4 context assembly e2e tests where
|
||||
bugs were already fixed.
|
||||
|
||||
- **PluginLoader entry point prefix validation** (#7476): Parse entry point targets before
|
||||
import, enforce the module allowlist ahead of loading, and add Behave plus Robot Framework
|
||||
regression coverage to ensure disallowed prefixes never execute untrusted module-level code
|
||||
in either unit or integration flows.
|
||||
|
||||
- **`issue-state-updater` Bash Script Errors**: Removed problematic bash script examples
|
||||
that tried to invoke `task forgejo-label-manager` as a bash command (the Task tool cannot
|
||||
be invoked from bash). Replaced with clear step-by-step operational instructions and
|
||||
@@ -181,9 +405,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
`pr-merge-pool-supervisor` to the product-builder's supervisor launch list (18 total
|
||||
supervisors). Updated all numeric references, pre-flight checklists, and validation logic.
|
||||
|
||||
- `ActionRepository.update()` now uses explicit bulk `sa_delete()` + `session.flush()`
|
||||
before re-inserting child rows for `action_arguments` and `action_invariants`, fixing
|
||||
a `sqlite3.IntegrityError: UNIQUE constraint failed` crash when `agents plan use` was
|
||||
called on an action that already had arguments registered via `action create`. (#4197)
|
||||
|
||||
---
|
||||
|
||||
## [3.8.0] — 2026-04-05
|
||||
## [3.8.0] -- 2026-04-05
|
||||
|
||||
### Added
|
||||
|
||||
@@ -194,171 +423,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
`INVARIANT_VIOLATED` events. Post-correction reconciliation runs
|
||||
via `CORRECTION_APPLIED` event subscription (best-effort). Added
|
||||
`InvariantService` Singleton provider in the DI container.
|
||||
- **TUI — Shell danger detection**: The TUI shell mode (`!` prefix) now detects
|
||||
- **TUI -- Shell danger detection**: The TUI shell mode (`!` prefix) now detects
|
||||
dangerous command patterns before execution. A configurable pattern registry
|
||||
classifies commands by danger level (warning, critical) and surfaces a user
|
||||
warning overlay before proceeding. Patterns cover destructive filesystem
|
||||
operations, privilege escalation, network exfiltration, and more. (#1003)
|
||||
|
||||
- **TUI — Permission Question Widget**: A new inline `PermissionQuestionWidget`
|
||||
- **TUI -- Permission Question Widget**: A new inline `PermissionQuestionWidget`
|
||||
renders permission requests directly in the conversation stream for single-file
|
||||
operations. Users can allow/reject with single-key shortcuts (`a`/`A`/`r`/`R`),
|
||||
navigate with arrow keys, confirm with `Enter`, or press `v` to open the full
|
||||
permissions screen. (#1004)
|
||||
|
||||
- **TUI — Actor thought blocks**: Expandable reasoning trace widgets rendered
|
||||
inline in the conversation stream with muted styling. Collapsed by default;
|
||||
expand with `Space` or click. (#1005)
|
||||
|
||||
- **UKO provenance tracking**: Every typed triple now carries `sourceResource`,
|
||||
`validFrom`, and `isCurrent` metadata. A revision chain enables temporal
|
||||
queries and point-in-time ontology state reconstruction.
|
||||
|
||||
- **JSON-RPC 2.0 A2A wire format**: `A2aRequest`/`A2aResponse` fields renamed to
|
||||
standard JSON-RPC 2.0 names (`method`, `id`, `result`, `error`). The
|
||||
`A2aVersionNegotiator` handles backward compatibility.
|
||||
|
||||
- **Database resource handler**: Full CRUD and checkpoint/rollback support for
|
||||
SQLite, PostgreSQL, MySQL, and DuckDB resources via the resource DAG.
|
||||
|
||||
- **Estimation lifecycle hook**: `actor.default.estimation` config key wires an
|
||||
estimation actor into the Strategize-to-Estimate lifecycle hook.
|
||||
|
||||
- **Persona system**: YAML-backed personas bind actors, argument presets, and scope
|
||||
references to named identities; persisted in `~/.config/cleveragents/personas/`.
|
||||
|
||||
- **Session management**: Create, list, export, and import conversation sessions;
|
||||
full JSON export/import for portability; Markdown transcript export
|
||||
(`--format md`) for human-readable sharing.
|
||||
|
||||
- **First-run experience**: `ActorSelectionOverlay` guides new users to pick an
|
||||
actor on first TUI launch; creates a `"default"` persona automatically.
|
||||
|
||||
- **Server mode**: `agents server connect` configures a remote CleverAgents server;
|
||||
Kubernetes Helm chart in `k8s/` for production deployment.
|
||||
|
||||
- **A2A integration**: Agent-to-Agent protocol facade wires CLI and TUI to live
|
||||
application services (session, plan, registry, event).
|
||||
|
||||
- **Permissions screen**: TUI overlay for reviewing tool permission requests with
|
||||
unified, side-by-side, and context diff views; session-scoped allow/reject decisions.
|
||||
|
||||
- **Inline permission questions**: `PermissionQuestionWidget` renders single-file
|
||||
permission requests directly in the conversation stream with single-key shortcuts.
|
||||
|
||||
- **Invariant reconciliation**: `InvariantReconciliationActor` runs automatically at
|
||||
every plan phase transition; failures block the transition and emit `INVARIANT_VIOLATED`.
|
||||
|
||||
- **UKO runtime**: Universal Knowledge Ontology query interface, inference engine, and
|
||||
graph persistence for ACMS context strategies.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `LangChainChatProvider.name` and `model_id` are now mutable properties with setters,
|
||||
fixing an `AttributeError` when `PlanService` attempted to resolve provider names after
|
||||
instantiation. (#1553)
|
||||
|
||||
---
|
||||
|
||||
## [3.7.0] — 2026-03-15
|
||||
|
||||
### Added
|
||||
|
||||
- **Interactive TUI** (`agents tui`) — full-screen Textual app with multi-session tabs,
|
||||
persona switching, slash commands (67 commands across 14 groups), reference picker
|
||||
(`@`), shell mode (`!`), context-sensitive F1 help, and `Ctrl+T` argument preset cycling.
|
||||
- **Slash command system** — 67 commands across 14 groups accessible via `/` overlay.
|
||||
- **Reference picker** — `@` key opens a file/resource reference picker that inserts
|
||||
references into the input field.
|
||||
- **TUI persona system** — YAML-backed personas bind actors, argument presets, and scope
|
||||
references; persisted in `~/.config/cleveragents/personas/`.
|
||||
- **TUI session export/import** — full JSON round-trip and Markdown transcript export
|
||||
(`--format md`).
|
||||
|
||||
---
|
||||
|
||||
## [3.6.0] — 2026-02-28
|
||||
|
||||
### Added
|
||||
|
||||
- Advanced Context Management System (ACMS) with three-tier context strategy.
|
||||
- UKO Runtime (Universal Knowledge Ontology) with graph persistence and inference engine.
|
||||
- Implicit inference engine producing `uko:implicitSiblingOf`, `uko:implicitContains`,
|
||||
and `uko:implicitDependsOn` triples with confidence 0.7.
|
||||
|
||||
---
|
||||
|
||||
## [3.5.0] — 2026-02-14
|
||||
|
||||
### Added
|
||||
|
||||
- Autonomy hardening: advisory locking, validation pipeline, definition-of-done gating.
|
||||
- Resource DAG with dependency tracking and type hierarchy with multiple inheritance.
|
||||
- Container resource types (`container.docker`, `container.podman`).
|
||||
- LSP resource types (`lsp.*`).
|
||||
|
||||
---
|
||||
|
||||
## [3.4.0] — 2026-01-31
|
||||
|
||||
### Added
|
||||
|
||||
- ACMS v1 with context scaling strategies.
|
||||
- Resource type inheritance system (ADR-042).
|
||||
- Safety profile extraction (ADR-041).
|
||||
|
||||
---
|
||||
|
||||
## [3.3.0] — 2026-01-17
|
||||
|
||||
### Added
|
||||
|
||||
- Corrections and subplans support in plan lifecycle.
|
||||
- Checkpoint and rollback for all resource writes.
|
||||
- Decision tree versioning and history (ADR-034).
|
||||
- Decision tree rollback and replay (ADR-035).
|
||||
|
||||
---
|
||||
|
||||
## [3.2.0] — 2026-01-03
|
||||
|
||||
### Added
|
||||
|
||||
- Decisions, validations, and invariants in plan lifecycle.
|
||||
- Validation abstraction layer (ADR-013).
|
||||
- Invariant system (ADR-016).
|
||||
- Automation profiles (ADR-017).
|
||||
- Semantic error prevention (ADR-018).
|
||||
|
||||
---
|
||||
|
||||
## [3.1.0] — 2025-12-20
|
||||
|
||||
### Added
|
||||
|
||||
- MCP (Model Context Protocol) adapter and client (ADR-029).
|
||||
- LSP (Language Server Protocol) client integration (ADR-027).
|
||||
- Agent Skills Standard (AgentSkills.io) support (ADR-028).
|
||||
- Skill abstraction definition (ADR-030).
|
||||
|
||||
---
|
||||
|
||||
## [3.0.0] — 2025-12-06
|
||||
|
||||
### Added
|
||||
|
||||
- Initial public release of CleverAgents Core.
|
||||
- Unified `agents` / `cleveragents` CLI entry points.
|
||||
- Layered architecture: Entry Points → Application → Domain → Infrastructure → Integration → Core.
|
||||
- Actor system with YAML-defined LangGraph node graphs.
|
||||
- Tool system with four-stage lifecycle (activate → validate → execute → deactivate).
|
||||
- Skill system with three-tier progressive disclosure.
|
||||
- Resource system with DAG and type hierarchy.
|
||||
- A2A (Agent-to-Agent) protocol facade.
|
||||
- DI container (`cleveragents.application.container`).
|
||||
- LangChain/LangGraph integration (ADR-022).
|
||||
- Provider registry with fallback chain (OpenAI → Anthropic → Google → Azure → OpenRouter → Groq → Together → Cohere).
|
||||
- Observability: structured logging, metrics, audit trail, token/cost tracking.
|
||||
- BDD test suite (Behave + Robot Framework).
|
||||
- Nox automation for lint, typecheck, tests, docs, benchmarks.
|
||||
- MkDocs-powered documentation with CleverAgents branding.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
Feature: Plan CLI start and show command aliases
|
||||
As a developer following the v3 spec
|
||||
I want to use `agents plan start` and `agents plan show` commands
|
||||
So that the CLI matches the specification documentation
|
||||
|
||||
Background:
|
||||
Given a plan start show CLI runner
|
||||
And a plan start show mocked lifecycle service
|
||||
|
||||
# ---- agents plan start: alias for agents plan use ----
|
||||
Scenario: Plan start creates a plan (alias for plan use)
|
||||
Given a plan start show action exists
|
||||
When I run plan start with action "local/test-action" and project "proj-1"
|
||||
Then the plan start should succeed
|
||||
And the plan start should create a plan in Strategize phase
|
||||
|
||||
Scenario: Plan start with multiple projects
|
||||
Given a plan start show action exists
|
||||
When I run plan start with action "local/test-action" and projects "proj-1" and "proj-2"
|
||||
Then the plan start should succeed
|
||||
And the plan start should link projects "proj-1" and "proj-2"
|
||||
|
||||
Scenario: Plan start with --arg option
|
||||
Given a plan start show action exists
|
||||
When I run plan start with action "local/test-action" project "proj-1" and arg "target_coverage=80"
|
||||
Then the plan start should succeed
|
||||
And the plan start should pass argument "target_coverage" with value 80
|
||||
|
||||
Scenario: Plan start with --automation-profile
|
||||
Given a plan start show action exists
|
||||
When I run plan start with action "local/test-action" project "proj-1" and automation profile "trusted"
|
||||
Then the plan start should succeed
|
||||
And the plan start output should contain "Automation Profile"
|
||||
|
||||
Scenario: Plan start with --invariant
|
||||
Given a plan start show action exists
|
||||
When I run plan start with action "local/test-action" project "proj-1" and invariant "No warnings"
|
||||
Then the plan start should succeed
|
||||
And the plan start should pass invariant "No warnings"
|
||||
|
||||
# ---- agents plan show: alias for agents plan status ----
|
||||
Scenario: Plan show displays plan status (alias for plan status)
|
||||
Given a plan start show plan exists for show
|
||||
When I run plan show for the plan
|
||||
Then the plan show should succeed
|
||||
And the plan show output should contain "Phase"
|
||||
And the plan show output should contain "Processing State"
|
||||
|
||||
Scenario: Plan show with no arguments lists all plans
|
||||
Given plan start show plans exist
|
||||
When I run plan show with no arguments
|
||||
Then the plan show should succeed
|
||||
And the plan show output should contain "Active Plans"
|
||||
|
||||
Scenario: Plan show displays plan details
|
||||
Given a plan start show plan exists for show
|
||||
When I run plan show for the plan
|
||||
Then the plan show should succeed
|
||||
And the plan show output should contain "Action"
|
||||
And the plan show output should contain "Projects"
|
||||
And the plan show output should contain "Arguments"
|
||||
|
||||
# ---- Help text verification ----
|
||||
Scenario: Plan start command appears in help
|
||||
When I run plan help
|
||||
Then the help output should contain "start"
|
||||
|
||||
Scenario: Plan show command appears in help
|
||||
When I run plan help
|
||||
Then the help output should contain "show"
|
||||
@@ -1,327 +0,0 @@
|
||||
"""Step definitions for plan CLI start and show command aliases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from behave import given, then, when
|
||||
from behave.runner import Context
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from cleveragents.cli.commands.plan import app as plan_app
|
||||
from cleveragents.domain.models.core.action import Action, ActionState
|
||||
from cleveragents.domain.models.core.plan import (
|
||||
AutomationProfileProvenance,
|
||||
AutomationProfileRef,
|
||||
NamespacedName,
|
||||
Plan,
|
||||
PlanIdentity,
|
||||
PlanInvariant,
|
||||
PlanPhase,
|
||||
PlanTimestamps,
|
||||
ProcessingState,
|
||||
ProjectLink,
|
||||
)
|
||||
|
||||
_PLAN_ULID = "01KHDE6WWS2171PWW3GJEBXZ8S"
|
||||
|
||||
|
||||
def _make_plan(
|
||||
*,
|
||||
name: str = "local/test-plan",
|
||||
action_name: str = "local/test-action",
|
||||
phase: PlanPhase = PlanPhase.STRATEGIZE,
|
||||
state: ProcessingState = ProcessingState.QUEUED,
|
||||
project_links: list[ProjectLink] | None = None,
|
||||
arguments: dict[str, object] | None = None,
|
||||
arguments_order: list[str] | None = None,
|
||||
automation_profile: AutomationProfileRef | None = None,
|
||||
invariants: list[PlanInvariant] | None = None,
|
||||
strategy_actor: str | None = "openai/gpt-4",
|
||||
execution_actor: str | None = "openai/gpt-4",
|
||||
estimation_actor: str | None = None,
|
||||
invariant_actor: str | None = None,
|
||||
) -> Plan:
|
||||
"""Create a Plan instance for start/show tests."""
|
||||
now = datetime.now()
|
||||
return Plan(
|
||||
identity=PlanIdentity(plan_id=_PLAN_ULID),
|
||||
namespaced_name=NamespacedName.parse(name),
|
||||
description="Test plan description",
|
||||
definition_of_done="All tests pass",
|
||||
action_name=action_name,
|
||||
phase=phase,
|
||||
processing_state=state,
|
||||
project_links=project_links or [],
|
||||
arguments=dict(arguments) if arguments else {},
|
||||
arguments_order=arguments_order or [],
|
||||
automation_profile=automation_profile,
|
||||
invariants=invariants or [],
|
||||
strategy_actor=strategy_actor,
|
||||
execution_actor=execution_actor,
|
||||
estimation_actor=estimation_actor,
|
||||
invariant_actor=invariant_actor,
|
||||
reusable=True,
|
||||
read_only=False,
|
||||
created_by=None,
|
||||
timestamps=PlanTimestamps(created_at=now, updated_at=now),
|
||||
)
|
||||
|
||||
|
||||
def _make_action(name: str = "local/test-action") -> Action:
|
||||
"""Create an Action for plan start tests."""
|
||||
return Action(
|
||||
namespaced_name=NamespacedName.parse(name),
|
||||
description="Test action",
|
||||
long_description=None,
|
||||
definition_of_done="All tests pass",
|
||||
strategy_actor="openai/gpt-4",
|
||||
execution_actor="openai/gpt-4",
|
||||
reusable=True,
|
||||
read_only=False,
|
||||
state=ActionState.AVAILABLE,
|
||||
created_by=None,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now(),
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Background
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("a plan start show CLI runner")
|
||||
def step_plan_start_show_runner(context: Context) -> None:
|
||||
"""Set up the CLI runner."""
|
||||
context.runner = CliRunner()
|
||||
|
||||
|
||||
@given("a plan start show mocked lifecycle service")
|
||||
def step_plan_start_show_mocked_service(context: Context) -> None:
|
||||
"""Mock the lifecycle service."""
|
||||
context.mock_service = MagicMock()
|
||||
context.mock_service.use_action.return_value = _make_plan()
|
||||
context.mock_service.get_plan.return_value = _make_plan()
|
||||
context.mock_service.list_plans.return_value = [_make_plan()]
|
||||
context.mock_service.get_action_by_name.return_value = _make_action()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plan start (alias for plan use)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("a plan start show action exists")
|
||||
def step_plan_start_show_action_exists(context: Context) -> None:
|
||||
"""Ensure an action exists for testing."""
|
||||
context.action = _make_action()
|
||||
|
||||
|
||||
@when('I run plan start with action "{action}" and project "{project}"')
|
||||
def step_run_plan_start_single_project(
|
||||
context: Context, action: str, project: str
|
||||
) -> None:
|
||||
"""Run plan start with a single project."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(plan_app, ["start", action, project])
|
||||
|
||||
|
||||
@when('I run plan start with action "{action}" and projects "{proj1}" and "{proj2}"')
|
||||
def step_run_plan_start_multiple_projects(
|
||||
context: Context, action: str, proj1: str, proj2: str
|
||||
) -> None:
|
||||
"""Run plan start with multiple projects."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(
|
||||
plan_app, ["start", action, proj1, proj2]
|
||||
)
|
||||
|
||||
|
||||
@when('I run plan start with action "{action}" project "{project}" and arg "{arg}"')
|
||||
def step_run_plan_start_with_arg(
|
||||
context: Context, action: str, project: str, arg: str
|
||||
) -> None:
|
||||
"""Run plan start with an argument."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(
|
||||
plan_app, ["start", action, project, "--arg", arg]
|
||||
)
|
||||
|
||||
|
||||
@when(
|
||||
'I run plan start with action "{action}" project "{project}" and automation profile "{profile}"'
|
||||
)
|
||||
def step_run_plan_start_with_profile(
|
||||
context: Context, action: str, project: str, profile: str
|
||||
) -> None:
|
||||
"""Run plan start with automation profile."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(
|
||||
plan_app,
|
||||
["start", action, project, "--automation-profile", profile],
|
||||
)
|
||||
|
||||
|
||||
@when(
|
||||
'I run plan start with action "{action}" project "{project}" and invariant "{invariant}"'
|
||||
)
|
||||
def step_run_plan_start_with_invariant(
|
||||
context: Context, action: str, project: str, invariant: str
|
||||
) -> None:
|
||||
"""Run plan start with an invariant."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(
|
||||
plan_app, ["start", action, project, "--invariant", invariant]
|
||||
)
|
||||
|
||||
|
||||
@then("the plan start should succeed")
|
||||
def step_plan_start_should_succeed(context: Context) -> None:
|
||||
"""Verify plan start succeeded."""
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Expected exit code 0, got {context.result.exit_code}. "
|
||||
f"Output: {context.result.stdout}"
|
||||
)
|
||||
|
||||
|
||||
@then("the plan start should create a plan in Strategize phase")
|
||||
def step_plan_start_creates_plan(context: Context) -> None:
|
||||
"""Verify plan was created in Strategize phase."""
|
||||
context.mock_service.use_action.assert_called_once()
|
||||
|
||||
|
||||
@then('the plan start should link projects "{proj1}" and "{proj2}"')
|
||||
def step_plan_start_links_projects(context: Context, proj1: str, proj2: str) -> None:
|
||||
"""Verify projects were linked."""
|
||||
context.mock_service.use_action.assert_called_once()
|
||||
call_args = context.mock_service.use_action.call_args
|
||||
project_links = call_args.kwargs.get("project_links", [])
|
||||
project_names = [p.project_name for p in project_links]
|
||||
assert proj1 in project_names, f"Project {proj1} not found in {project_names}"
|
||||
assert proj2 in project_names, f"Project {proj2} not found in {project_names}"
|
||||
|
||||
|
||||
@then('the plan start should pass argument "{arg_name}" with value {arg_value}')
|
||||
def step_plan_start_passes_argument(
|
||||
context: Context, arg_name: str, arg_value: str
|
||||
) -> None:
|
||||
"""Verify argument was passed."""
|
||||
context.mock_service.use_action.assert_called_once()
|
||||
call_args = context.mock_service.use_action.call_args
|
||||
arguments = call_args.kwargs.get("arguments", {})
|
||||
assert arg_name in arguments, f"Argument {arg_name} not found in {arguments}"
|
||||
# Convert arg_value to int if it looks like a number
|
||||
try:
|
||||
expected_value = int(arg_value)
|
||||
except ValueError:
|
||||
expected_value = arg_value
|
||||
assert arguments[arg_name] == expected_value
|
||||
|
||||
|
||||
@then('the plan start should pass invariant "{invariant}"')
|
||||
def step_plan_start_passes_invariant(context: Context, invariant: str) -> None:
|
||||
"""Verify invariant was passed."""
|
||||
context.mock_service.use_action.assert_called_once()
|
||||
call_args = context.mock_service.use_action.call_args
|
||||
invariants = call_args.kwargs.get("invariants", [])
|
||||
invariant_texts = [inv.text for inv in invariants]
|
||||
assert invariant in invariant_texts, (
|
||||
f"Invariant {invariant} not found in {invariant_texts}"
|
||||
)
|
||||
|
||||
|
||||
@then('the plan start output should contain "{text}"')
|
||||
def step_plan_start_output_contains(context: Context, text: str) -> None:
|
||||
"""Verify output contains text."""
|
||||
assert text in context.result.stdout, (
|
||||
f"Expected '{text}' in output, got: {context.result.stdout}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plan show (alias for plan status)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("a plan start show plan exists for show")
|
||||
def step_plan_start_show_plan_exists(context: Context) -> None:
|
||||
"""Ensure a plan exists for show testing."""
|
||||
context.plan = _make_plan()
|
||||
|
||||
|
||||
@given("plan start show plans exist")
|
||||
def step_plan_start_show_plans_exist(context: Context) -> None:
|
||||
"""Ensure multiple plans exist."""
|
||||
context.plans = [
|
||||
_make_plan(name="local/plan-1"),
|
||||
_make_plan(name="local/plan-2"),
|
||||
]
|
||||
context.mock_service.list_plans.return_value = context.plans
|
||||
|
||||
|
||||
@when("I run plan show for the plan")
|
||||
def step_run_plan_show_for_plan(context: Context) -> None:
|
||||
"""Run plan show for a specific plan."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(plan_app, ["show", _PLAN_ULID])
|
||||
|
||||
|
||||
@when("I run plan show with no arguments")
|
||||
def step_run_plan_show_no_args(context: Context) -> None:
|
||||
"""Run plan show with no arguments (list all plans)."""
|
||||
with patch(
|
||||
"cleveragents.cli.commands.plan._get_lifecycle_service",
|
||||
return_value=context.mock_service,
|
||||
):
|
||||
context.result = context.runner.invoke(plan_app, ["show"])
|
||||
|
||||
|
||||
@when("I run plan help")
|
||||
def step_run_plan_help(context: Context) -> None:
|
||||
"""Run plan help to see available commands."""
|
||||
context.result = context.runner.invoke(plan_app, ["--help"])
|
||||
|
||||
|
||||
@then("the plan show should succeed")
|
||||
def step_plan_show_should_succeed(context: Context) -> None:
|
||||
"""Verify plan show succeeded."""
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Expected exit code 0, got {context.result.exit_code}. "
|
||||
f"Output: {context.result.stdout}"
|
||||
)
|
||||
|
||||
|
||||
@then('the plan show output should contain "{text}"')
|
||||
def step_plan_show_output_contains(context: Context, text: str) -> None:
|
||||
"""Verify output contains text."""
|
||||
assert text in context.result.stdout, (
|
||||
f"Expected '{text}' in output, got: {context.result.stdout}"
|
||||
)
|
||||
|
||||
|
||||
@then('the help output should contain "{text}"')
|
||||
def step_help_output_contains(context: Context, text: str) -> None:
|
||||
"""Verify help output contains text."""
|
||||
assert text in context.result.stdout, (
|
||||
f"Expected '{text}' in help output, got: {context.result.stdout}"
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user