Feature: Cross-plan correction cascading
  When a correction's affected subtree includes child plans, the child
  plan's state determines the cascade behaviour: cancel, cancel with
  sandbox rollback, or reject the correction entirely.

  # ─────────────────────────────────────────────────────────────────
  # Not-yet-started child plans → cancel only
  # ─────────────────────────────────────────────────────────────────

  Scenario: Correction cascades to not-started child plan — cancel
    Given a cross-plan correction service
    And the child plan "CP1" has state "not_started"
    When I execute a cascade correction for correction "C1" with child plans "CP1"
    Then the cascade should succeed
    And the cancelled child plans should contain "CP1"
    And the rolled back child plans should be empty

  # ─────────────────────────────────────────────────────────────────
  # In-progress child plans → cancel + sandbox rollback
  # ─────────────────────────────────────────────────────────────────

  Scenario: Correction cascades to in-progress child plan — cancel and rollback
    Given a cross-plan correction service
    And the child plan "CP1" has state "in_progress"
    When I execute a cascade correction for correction "C1" with child plans "CP1"
    Then the cascade should succeed
    And the cancelled child plans should contain "CP1"
    And the rolled back child plans should contain "CP1"

  # ─────────────────────────────────────────────────────────────────
  # Completed-unapplied child plans → cancel + rollback
  # ─────────────────────────────────────────────────────────────────

  Scenario: Correction cascades to completed-unapplied child plan — cancel and rollback
    Given a cross-plan correction service
    And the child plan "CP1" has state "completed_unapplied"
    When I execute a cascade correction for correction "C1" with child plans "CP1"
    Then the cascade should succeed
    And the cancelled child plans should contain "CP1"
    And the rolled back child plans should contain "CP1"

  # ─────────────────────────────────────────────────────────────────
  # Already-applied child plans → rejection
  # ─────────────────────────────────────────────────────────────────

  Scenario: Correction cascades to already-applied child plan — rejection with CorrectionRejection
    Given a cross-plan correction service
    And the child plan "CP1" has state "applied"
    When I execute a cascade correction for correction "C1" with child plans "CP1"
    Then the cascade should be rejected
    And the rejection reason should mention "already applied"
    And the rejection should list applied child plan "CP1"

  # ─────────────────────────────────────────────────────────────────
  # Mixed child plan states
  # ─────────────────────────────────────────────────────────────────

  Scenario: Mixed child plan states — reject if any applied even if others are cancellable
    Given a cross-plan correction service
    And the child plan "CP1" has state "not_started"
    And the child plan "CP2" has state "in_progress"
    And the child plan "CP3" has state "applied"
    When I execute a cascade correction for correction "C1" with child plans "CP1,CP2,CP3"
    Then the cascade should be rejected
    And the rejection should list applied child plan "CP3"
    And the cancelled child plans should be empty

  # ─────────────────────────────────────────────────────────────────
  # Atomic cascade: failure during one cancel rolls back all previous
  # ─────────────────────────────────────────────────────────────────

  Scenario: Atomic cascade — failure during one cancel rolls back all previous cancels
    Given a cross-plan correction service with failing canceller on "CP2"
    And the child plan "CP1" has state "not_started"
    And the child plan "CP2" has state "not_started"
    When I try to execute a cascade correction for correction "C1" with child plans "CP1,CP2"
    Then the cascade should raise an error
    And the cascade error should mention "CP2"

  # ─────────────────────────────────────────────────────────────────
  # Evaluation (dry-run) scenarios
  # ─────────────────────────────────────────────────────────────────

  Scenario: Evaluate cascade without executing — no child plans
    Given a cross-plan correction service
    When I evaluate a cascade for correction "C1" with no child plans
    Then the cascade evaluation should succeed
    And the cascade actions should be empty

  Scenario: Evaluate cascade — mixed states preview
    Given a cross-plan correction service
    And the child plan "CP1" has state "not_started"
    And the child plan "CP2" has state "in_progress"
    When I evaluate a cascade for correction "C1" with child plans "CP1,CP2"
    Then the cascade evaluation should succeed
    And the cascade action for "CP1" should be "cancel"
    And the cascade action for "CP2" should be "cancel_and_rollback"

  # ─────────────────────────────────────────────────────────────────
  # Validation
  # ─────────────────────────────────────────────────────────────────

  Scenario: Empty correction_id raises validation error
    Given a cross-plan correction service
    When I try to evaluate a cascade with empty correction_id
    Then a cross-plan validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # Multiple not-started child plans — all cancelled
  # ─────────────────────────────────────────────────────────────────

  Scenario: Multiple not-started child plans all cancelled
    Given a cross-plan correction service
    And the child plan "CP1" has state "not_started"
    And the child plan "CP2" has state "not_started"
    And the child plan "CP3" has state "not_started"
    When I execute a cascade correction for correction "C1" with child plans "CP1,CP2,CP3"
    Then the cascade should succeed
    And the cancelled child plans should contain "CP1"
    And the cancelled child plans should contain "CP2"
    And the cancelled child plans should contain "CP3"
    And the rolled back child plans should be empty

  # ─────────────────────────────────────────────────────────────────
  # Integration with CorrectionImpact — execute_correction_with_cascade
  # ─────────────────────────────────────────────────────────────────

  Scenario: Execute correction with cascade — no child plans returns result
    Given a cross-plan correction service
    When I execute a correction with cascade for correction "C1" with no child plans in revert mode
    Then the correction with cascade result should be applied

  Scenario: Execute correction with cascade — applied child plan returns rejection
    Given a cross-plan correction service
    And the child plan "CP1" has state "applied"
    When I execute a correction with cascade for correction "C1" with child plan "CP1" in revert mode
    Then the correction with cascade result should be a rejection

  Scenario: Execute correction with cascade — cancellable child plan succeeds
    Given a cross-plan correction service
    And the child plan "CP1" has state "not_started"
    When I execute a correction with cascade for correction "C1" with child plan "CP1" in revert mode
    Then the correction with cascade result should be applied

  # ─────────────────────────────────────────────────────────────────
  # classify_cascade_action unit-level checks
  # ─────────────────────────────────────────────────────────────────

  Scenario: classify_cascade_action for not_started returns cancel
    When I classify cascade action for state "not_started"
    Then the classified action should be "cancel"

  Scenario: classify_cascade_action for in_progress returns cancel_and_rollback
    When I classify cascade action for state "in_progress"
    Then the classified action should be "cancel_and_rollback"

  Scenario: classify_cascade_action for completed_unapplied returns cancel_and_rollback
    When I classify cascade action for state "completed_unapplied"
    Then the classified action should be "cancel_and_rollback"

  Scenario: classify_cascade_action for applied returns reject
    When I classify cascade action for state "applied"
    Then the classified action should be "reject"

  # ─────────────────────────────────────────────────────────────────
  # Constructor validation
  # ─────────────────────────────────────────────────────────────────

  Scenario: Constructor rejects None plan_lookup
    When I try to create a cross-plan service with None plan_lookup
    Then a cross-plan validation error should be raised

  Scenario: Constructor rejects None plan_canceller
    When I try to create a cross-plan service with None plan_canceller
    Then a cross-plan validation error should be raised

  Scenario: Constructor rejects None sandbox_rollbacker
    When I try to create a cross-plan service with None sandbox_rollbacker
    Then a cross-plan validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # CorrectionRejection model validation
  # ─────────────────────────────────────────────────────────────────

  Scenario: CorrectionRejection model has correct fields
    When I create a CorrectionRejection with correction_id "C1" and reason "test" and plans "CP1,CP2"
    Then the rejection correction_id should be "C1"
    And the rejection reason should be "test"
    And the rejection affected plans should contain "CP1"
    And the rejection affected plans should contain "CP2"

  # ─────────────────────────────────────────────────────────────────
  # CascadeAction model validation
  # ─────────────────────────────────────────────────────────────────

  Scenario: CascadeAction with invalid action raises error
    When I try to create a CascadeAction with invalid action "destroy"
    Then a cross-plan pydantic validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # CorrectionStatus REJECTED
  # ─────────────────────────────────────────────────────────────────

  Scenario: CorrectionStatus includes REJECTED
    Then the CorrectionStatus enum should include "rejected"

  # ─────────────────────────────────────────────────────────────────
  # ChildPlanState enum completeness
  # ─────────────────────────────────────────────────────────────────

  Scenario: ChildPlanState enum has all four states
    Then the ChildPlanState enum should have 4 members
    And the ChildPlanState enum should include "not_started"
    And the ChildPlanState enum should include "in_progress"
    And the ChildPlanState enum should include "completed_unapplied"
    And the ChildPlanState enum should include "applied"

  # ─────────────────────────────────────────────────────────────────
  # Validation — execute_cascade with empty correction_id
  # ─────────────────────────────────────────────────────────────────

  Scenario: execute_cascade with empty correction_id raises validation error
    Given a cross-plan correction service
    When I try to execute a cascade with empty correction_id
    Then a cross-plan validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # Validation — execute_correction_with_cascade with empty id
  # ─────────────────────────────────────────────────────────────────

  Scenario: execute_correction_with_cascade with empty correction_id raises validation error
    Given a cross-plan correction service
    When I try to execute a correction with cascade with empty correction_id
    Then a cross-plan validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # Append mode — no child plans
  # ─────────────────────────────────────────────────────────────────

  Scenario: Execute correction with cascade in append mode — no child plans returns result with empty lists
    Given a cross-plan correction service
    When I execute a correction with cascade for correction "C1" with no child plans in append mode
    Then the correction with cascade result should be applied
    And the correction result reverted decisions should be empty
    And the correction result archived artifacts should be empty

  # ─────────────────────────────────────────────────────────────────
  # Append mode — cancellable child plan
  # ─────────────────────────────────────────────────────────────────

  Scenario: Execute correction with cascade in append mode — cancellable child plan succeeds with empty lists
    Given a cross-plan correction service
    And the child plan "CP1" has state "not_started"
    When I execute a correction with cascade for correction "C1" with child plan "CP1" in append mode
    Then the correction with cascade result should be applied
    And the correction result reverted decisions should be empty
    And the correction result archived artifacts should be empty

  # ─────────────────────────────────────────────────────────────────
  # CorrectionImpact model validation — invalid rollback_tier
  # ─────────────────────────────────────────────────────────────────

  Scenario: CorrectionImpact rejects invalid rollback_tier
    When I try to create a CorrectionImpact with rollback_tier "invalid_tier"
    Then a cross-plan pydantic validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # CorrectionImpact model validation — invalid risk_level
  # ─────────────────────────────────────────────────────────────────

  Scenario: CorrectionImpact rejects invalid risk_level
    When I try to create a CorrectionImpact with risk_level "catastrophic"
    Then a cross-plan pydantic validation error should be raised

  # ─────────────────────────────────────────────────────────────────
  # Protocol runtime checks
  # ─────────────────────────────────────────────────────────────────

  Scenario: Mock plan lookup satisfies ChildPlanLookup protocol
    Given a cross-plan correction service
    Then the plan lookup should satisfy the ChildPlanLookup protocol

  Scenario: Mock plan canceller satisfies ChildPlanCanceller protocol
    Given a cross-plan correction service
    Then the plan canceller should satisfy the ChildPlanCanceller protocol

  Scenario: Mock sandbox rollbacker satisfies SandboxRollbacker protocol
    Given a cross-plan correction service
    Then the sandbox rollbacker should satisfy the SandboxRollbacker protocol

  # ─────────────────────────────────────────────────────────────────
  # Whitespace-only correction_id treated as empty
  # ─────────────────────────────────────────────────────────────────

  Scenario: execute_cascade with whitespace-only correction_id raises validation error
    Given a cross-plan correction service
    When I try to execute a cascade with whitespace-only correction_id
    Then a cross-plan validation error should be raised

  Scenario: execute_correction_with_cascade with whitespace-only correction_id raises validation error
    Given a cross-plan correction service
    When I try to execute a correction with cascade with whitespace-only correction_id
    Then a cross-plan validation error should be raised
