Files
cleveragents-core/features/plan_cli_commands_r2.feature
hurui200320 1fda56b778
CI / lint (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 58s
CI / quality (pull_request) Successful in 29s
CI / security (pull_request) Successful in 1m11s
CI / build (pull_request) Successful in 24s
CI / push-validation (pull_request) Successful in 21s
CI / helm (pull_request) Successful in 41s
CI / integration_tests (pull_request) Successful in 4m22s
CI / e2e_tests (pull_request) Successful in 4m39s
CI / unit_tests (pull_request) Successful in 9m51s
CI / coverage (pull_request) Successful in 13m30s
CI / docker (pull_request) Successful in 1m23s
CI / status-check (pull_request) Successful in 2s
fix(cli): implement plan diff --correction to show real correction attempt diff
Implement spec-compliant correction diff output for `agents plan diff
--correction <CORRECTION_ATTEMPT_ID>`. Fixes the following issues from
the cycle-1 PR review:

- C1/M1: Replace direct `unit_of_work.correction_attempts` access with
  the proper `unit_of_work.transaction()` context manager, eliminating
  the AttributeError crash and the resource (session) leak.
- C2: Add `unit_of_work: UnitOfWork | None = None` constructor parameter
  to `PlanApplyService` and wire it in `_get_apply_service()` via
  `container.unit_of_work()`, removing the illegal `get_container()`
  call inside the method body (ADR-003 DI violation).
- C3: Replace metadata serialization stub with a three-section structured
  diff (Correction Diff summary, Comparison table, Patch Preview) as
  specified in §agents plan diff of the specification.
- C4/M2: Add `features/plan_correction_diff.feature` with 6 BDD
  scenarios covering all 4 output formats plus plan-not-found and
  correction-not-found error paths.
- C5: Update the three existing BDD scenarios that tested old stub
  behavior to mock `_get_apply_service()` and assert the new output.
- C6: Rename branch to `bugfix/m4-plan-diff-correction-stub` per
  CONTRIBUTING.md convention.
- C7: Amend commit message with body and ISSUES CLOSED footer.
- C8: Narrow `except Exception` to `except CorrectionAttemptNotFoundError`
  to avoid masking programming errors.
- M3: Add `robot/plan_correction_diff.robot` and
  `robot/helper_plan_correction_diff.py` integration test covering rich,
  plain, and JSON formats and the not-found error path.
- M4: Type `_build_correction_diff_dict` parameter as
  `CorrectionAttemptRecord` instead of `Any`.
- M5: Change `fmt: str` to `fmt: Literal["rich", "plain", "json", "yaml"]`
  on both `diff()` and `correction_diff()`, with a `cast()` call in the
  CLI layer where Typer supplies a plain `str`.
- M6: Add `ValueError` guards for empty `plan_id` and
  `correction_attempt_id` at the top of `correction_diff()`.
- M7: Add blank line between `diff()` and `correction_diff()` method
  definitions.
- M8: Update PR description to reflect actual implementation.
- m1: Remove unused `plan` variable in `correction_diff()`.
- m2: Reduce three blank lines to two between top-level definitions in
  `plan_apply_service.py`.
- n1: Remove trailing whitespace from blank line in `plan.py`.

Quality gates: lint (ruff), typecheck (pyright strict), unit_tests
(Behave 632 features / 0 failures) all pass.

ISSUES CLOSED: #9085
2026-04-17 08:34:33 +00:00

230 lines
10 KiB
Gherkin
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Feature: Plan CLI commands branch coverage (round 2)
As a developer
I want to exercise uncovered branches in CLI command functions
So that branch coverage for use_action, execute, apply, list, revert, status, correct, and diff rises
# ===================================================================
# use_action argument parsing int / float / bool / string
# ===================================================================
Scenario: r2plan use_action parses integer argument
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and arg "count=42"
Then r2plan-the parsed arguments should have "count" as int 42
Scenario: r2plan use_action parses float argument
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and arg "ratio=3.14"
Then r2plan-the parsed arguments should have "ratio" as float 3.14
Scenario: r2plan use_action parses bool true argument
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and arg "verbose=true"
Then r2plan-the parsed arguments should have "verbose" as bool true
Scenario: r2plan use_action parses bool false argument
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and arg "verbose=false"
Then r2plan-the parsed arguments should have "verbose" as bool false
Scenario: r2plan use_action parses string argument
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and arg "name=hello"
Then r2plan-the parsed arguments should have "name" as string "hello"
Scenario: r2plan use_action rejects argument missing equals sign
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and arg "badarg"
Then r2plan-the command should abort
And r2plan-the output should contain "Invalid argument format"
# ===================================================================
# use_action invalid automation profile
# ===================================================================
Scenario: r2plan use_action rejects invalid automation profile
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and automation profile "bogus"
Then r2plan-the command should abort
And r2plan-the output should contain "Invalid automation profile"
# ===================================================================
# use_action actor overrides with invalid names
# ===================================================================
Scenario: r2plan use_action rejects invalid strategy actor
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and strategy actor "INVALID"
Then r2plan-the command should abort
And r2plan-the output should contain "Validation Error"
Scenario: r2plan use_action rejects invalid execution actor
Given r2plan-a mocked lifecycle service
When r2plan-I invoke use with action "local/test" and execution actor "bad-name"
Then r2plan-the command should abort
And r2plan-the output should contain "Validation Error"
# ===================================================================
# execute_plan auto-resolve: no plans / multiple plans
# ===================================================================
Scenario: r2plan execute_plan aborts when no plans ready
Given r2plan-a mocked lifecycle service
And r2plan-the service lists no complete strategize plans
When r2plan-I invoke execute without plan_id
Then r2plan-the command should abort
And r2plan-the output should contain "No plans ready for execution"
Scenario: r2plan execute_plan aborts when multiple plans ready
Given r2plan-a mocked lifecycle service
And r2plan-the service lists multiple complete strategize plans
When r2plan-I invoke execute without plan_id
Then r2plan-the command should abort
And r2plan-the output should contain "Multiple plans ready"
# ===================================================================
# apply_plan auto-resolve: no plans / multiple plans
# ===================================================================
Scenario: r2plan apply aborts when no plans ready
Given r2plan-a mocked lifecycle service
And r2plan-the service lists no complete execute plans
When r2plan-I invoke apply without plan_id
Then r2plan-the command should abort
And r2plan-the output should contain "No plans ready for apply"
Scenario: r2plan apply aborts when multiple plans ready
Given r2plan-a mocked lifecycle service
And r2plan-the service lists multiple complete execute plans
When r2plan-I invoke apply without plan_id
Then r2plan-the command should abort
And r2plan-the output should contain "Multiple plans ready"
# ===================================================================
# list_plans invalid phase / invalid state
# ===================================================================
Scenario: r2plan list rejects invalid phase
Given r2plan-a mocked lifecycle service
When r2plan-I invoke list with phase "nonexistent"
Then r2plan-the command should abort
And r2plan-the output should contain "Invalid phase"
Scenario: r2plan list rejects invalid state
Given r2plan-a mocked lifecycle service
When r2plan-I invoke list with state "nonexistent"
Then r2plan-the command should abort
And r2plan-the output should contain "Invalid state"
# ===================================================================
# list_plans empty result
# ===================================================================
Scenario: r2plan list shows no-plans message when empty
Given r2plan-a mocked lifecycle service
And r2plan-the service lists no plans
When r2plan-I invoke list
Then r2plan-the command should succeed
And r2plan-the output should contain "No plans found"
# ===================================================================
# revert_plan invalid --to-phase
# ===================================================================
Scenario: r2plan revert_plan rejects invalid to-phase
Given r2plan-a mocked lifecycle service
When r2plan-I invoke revert with plan "PLAN123" and invalid phase "badphase"
Then r2plan-the command should abort
And r2plan-the output should contain "Invalid phase"
Scenario: r2plan revert_plan non-rich format
Given r2plan-a mocked lifecycle service
And r2plan-the service can revert a plan
When r2plan-I invoke revert with plan "PLAN123" and format "json"
Then r2plan-the command should succeed
And r2plan-the output should contain "plan_id"
# ===================================================================
# plan_status without plan_id: no plans, plans in non-rich, rich
# ===================================================================
Scenario: r2plan status shows no-plans message
Given r2plan-a mocked lifecycle service
And r2plan-the service lists no plans
When r2plan-I invoke status without plan_id
Then r2plan-the command should succeed
And r2plan-the output should contain "No v3 lifecycle plans found"
Scenario: r2plan status without plan_id lists plans in non-rich
Given r2plan-a mocked lifecycle service
And r2plan-the service lists some plans
When r2plan-I invoke status without plan_id and format "json"
Then r2plan-the command should succeed
And r2plan-the output should contain "plan_id"
Scenario: r2plan status with plan_id in non-rich format
Given r2plan-a mocked lifecycle service
And r2plan-the service can get a plan by id
When r2plan-I invoke status with plan_id and format "json"
Then r2plan-the command should succeed
And r2plan-the output should contain "plan_id"
# ===================================================================
# correct_decision invalid mode / empty guidance
# ===================================================================
Scenario: r2plan correct rejects invalid mode
Given r2plan-a mocked lifecycle service
When r2plan-I invoke correct with mode "badmode"
Then r2plan-the command should abort
And r2plan-the output should contain "Invalid mode"
Scenario: r2plan correct rejects empty guidance
Given r2plan-a mocked lifecycle service
When r2plan-I invoke correct with empty guidance
Then r2plan-the command should abort
And r2plan-the output should contain "guidance"
# ===================================================================
# plan_diff correction flag present
# ===================================================================
Scenario: r2plan diff shows correction diff when correction flag set
Given r2plan-a mocked lifecycle service
And r2plan-the apply service correction_diff returns formatted output for "CORR1"
When r2plan-I invoke diff with plan "P1" and correction "CORR1"
Then r2plan-the command should succeed
And r2plan-the output should contain "Correction Diff"
And r2plan-the output should contain "CORR1"
# ===================================================================
# execute_plan auto-resolve with exactly one plan
# ===================================================================
Scenario: r2plan execute_plan auto-resolves single plan
Given r2plan-a mocked lifecycle service
And r2plan-the service lists exactly one complete strategize plan
When r2plan-I invoke execute without plan_id
Then r2plan-the command should succeed
# ===================================================================
# apply auto-resolve with exactly one plan
# ===================================================================
Scenario: r2plan apply auto-resolves single plan
Given r2plan-a mocked lifecycle service
And r2plan-the service lists exactly one complete execute plan
When r2plan-I invoke apply without plan_id
Then r2plan-the command should succeed
# ===================================================================
# list_plans projects > 2 truncation
# ===================================================================
Scenario: r2plan list truncates projects over 2
Given r2plan-a mocked lifecycle service
And r2plan-the service lists a plan with 4 project links
When r2plan-I invoke list
Then r2plan-the command should succeed
And r2plan-the output should contain "+2 more"