Feature: Plan CLI coverage boost
  As a developer
  I want to exercise uncovered branches in plan.py
  So that code coverage is improved for the plan CLI module

  # ---- _plan_spec_dict helper ----

  Scenario: _plan_spec_dict returns error_message when truthy
    Given a v3 Plan with error_message set to "Strategy failed"
    When I call _plan_spec_dict on the plan
    Then the spec dict should contain key "error_message" with value "Strategy failed"

  Scenario: _plan_spec_dict falls back to legacy format for non-Plan objects
    Given a non-Plan object with string value "legacy plan data"
    When I call _plan_spec_dict on the object
    Then the spec dict should equal {"plan": "legacy plan data"}

  Scenario: _plan_spec_dict omits error_message when it is None
    Given a v3 Plan with error_message set to None
    When I call _plan_spec_dict on the plan
    Then the spec dict should not contain key "error_message"

  Scenario: _plan_spec_dict includes estimation_result when set
    Given a v3 Plan with estimation_result set
    When I call _plan_spec_dict on the plan
    Then the spec dict should contain key "estimation"

  Scenario: _plan_spec_dict includes invariants when populated
    Given a v3 Plan with invariants populated
    When I call _plan_spec_dict on the plan
    Then the spec dict should contain key "invariants"

  Scenario: _plan_spec_dict includes execution_environment when set
    Given a v3 Plan with execution_environment set
    When I call _plan_spec_dict on the plan
    Then the spec dict should contain key "execution_environment"
    And the spec dict should contain key "execution_env_priority"

  Scenario: _plan_spec_dict includes dod_evaluation when validation_summary set
    Given a v3 Plan with validation_summary populated
    When I call _plan_spec_dict on the plan
    Then the spec dict should contain key "dod_evaluation"

  Scenario: _plan_spec_dict includes last_completed_step and last_checkpoint_id
    Given a v3 Plan with last_completed_step and last_checkpoint_id
    When I call _plan_spec_dict on the plan
    Then the spec dict should contain key "last_completed_step"
    And the spec dict should contain key "last_checkpoint_id"

  # ---- _print_lifecycle_plan helper ----

  Scenario: _print_lifecycle_plan prints all optional timestamps
    Given a v3 Plan with all timestamps populated
    When I call _print_lifecycle_plan on the plan
    Then the printed output should contain "Strategize Started"
    And the printed output should contain "Strategize Completed"
    And the printed output should contain "Execute Started"
    And the printed output should contain "Execute Completed"
    And the printed output should contain "Applied At"

  Scenario: _print_lifecycle_plan prints estimation_actor when set
    Given a v3 Plan with estimation_actor set to "local/cost-estimator"
    When I call _print_lifecycle_plan on the plan
    Then the printed output should contain "Estimation Actor"
    And the printed output should contain "local/cost-estimator"

  Scenario: _print_lifecycle_plan prints invariant_actor when set
    Given a v3 Plan with invariant_actor set to "local/invariant-checker"
    When I call _print_lifecycle_plan on the plan
    Then the printed output should contain "Invariant Actor"
    And the printed output should contain "local/invariant-checker"

  Scenario: _print_lifecycle_plan falls back for non-Plan objects
    Given a non-Plan object with string value "legacy-plan-object"
    When I call _print_lifecycle_plan on the object
    Then the printed output should contain "legacy-plan-object"

  # ---- execute_plan non-rich format ----

  Scenario: execute_plan outputs JSON when format is json
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has a complete strategize plan for execute
    When I invoke execute with "--format" "json" and plan id
    Then the plan coverage command should succeed
    And the plan coverage output should contain "plan_id"
    And the plan coverage output should contain "sandbox"
    And the plan coverage output should contain "worker"
    And the plan coverage output should contain "progress"
    And the plan coverage output should contain "strategy_summary"
    And the plan coverage output should contain "command"
    And the plan coverage output should contain "exit_code"

  @tdd_issue @tdd_issue_4251 @tdd_expected_fail
  Scenario: execute_plan JSON output has spec-required envelope structure
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has a complete strategize plan for execute
    When I invoke execute with "--format" "json" and plan id
    Then the plan coverage command should succeed
    And the execute JSON output has the spec-required envelope fields
    And the execute JSON output data has sandbox with strategy field
    And the execute JSON output data has progress list with label and status

  # ---- apply_plan non-rich format ----

  Scenario: apply_plan outputs JSON when format is json
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has a complete execute plan for apply
    When I invoke apply with "--format" "json" and plan id
    Then the plan coverage command should succeed
    And the plan coverage output should contain "plan_id"

  # ---- list_plans regex and state/processing_state filtering ----

  Scenario: list_plans filters by regex pattern
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has multiple plans for lifecycle list
    When I invoke list with regex "alpha"
    Then the plan coverage command should succeed
    And the plan coverage output should contain "alpha"
    And the plan coverage output should not contain "beta"

  Scenario: list_plans filters by state
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has plans in different processing states
    When I invoke list with "--state" "processing"
    Then the plan coverage command should succeed

  Scenario: list_plans filters by processing_state alias
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has plans in different processing states
    When I invoke list with "--processing-state" "complete"
    Then the plan coverage command should succeed

  Scenario: list_plans rejects invalid regex
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has multiple plans for lifecycle list
    When I invoke list with regex "[invalid"
    Then the plan coverage command should abort
    And the plan coverage output should contain "Invalid regex"

  Scenario: list_plans filters by action name
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has multiple plans for lifecycle list
    When I invoke list with "--action" "local/test-action"
    Then the plan coverage command should succeed

  Scenario: list_plans outputs JSON when format is json
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service has multiple plans for lifecycle list
    When I invoke list with "--format" "json"
    Then the plan coverage command should succeed
    And the plan coverage output should contain "plan_id"

  # ---- cancel_plan ----

  Scenario: cancel_plan in non-rich format without reason
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service can cancel a plan
    When I invoke cancel with "--format" "json" and no reason
    Then the plan coverage command should succeed
    And the plan coverage output should contain "plan_id"
    And the plan coverage output should not contain "cancel_reason"

  Scenario: cancel_plan in non-rich format with reason
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service can cancel a plan
    When I invoke cancel with "--format" "json" and reason "not needed"
    Then the plan coverage command should succeed
    And the plan coverage output should contain "cancel_reason"
    And the plan coverage output should contain "not needed"

  Scenario: cancel_plan in rich format with reason
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service can cancel a plan
    When I invoke cancel in rich format with reason "obsolete"
    Then the plan coverage command should succeed
    And the plan coverage output should contain "Plan cancelled"
    And the plan coverage output should contain "Reason: obsolete"

  Scenario: cancel_plan in rich format without reason
    Given a plan lifecycle CLI runner for coverage
    And a mocked lifecycle service for plan coverage commands
    And the service can cancel a plan
    When I invoke cancel in rich format without reason
    Then the plan coverage command should succeed
    And the plan coverage output should contain "Plan cancelled"
