@phase2 @plan_lifecycle @rollback Feature: PlanLifecycleService rollback_plan method As a developer using the plan lifecycle service I want a rollback_plan method on PlanLifecycleService So that rollback operations go through the service layer with proper state validation and domain event emission # ─────────────────────────────────────────────────────────── # Service method existence and basic delegation # ─────────────────────────────────────────────────────────── Scenario: rollback_plan delegates to CheckpointService and returns RollbackResult Given plr-a plan lifecycle service with a mock checkpoint service And plr-a plan in Execute phase with a sandbox And plr-a checkpoint exists for the plan When plr-I call rollback_plan with the plan id and checkpoint id Then plr-the rollback should succeed And plr-the result should be a RollbackResult And plr-the checkpoint service selective_rollback should have been called Scenario: rollback_plan raises PlanError when checkpoint_service is not configured Given plr-a plan lifecycle service without a checkpoint service And plr-a plan in Execute phase When plr-I call rollback_plan with the plan id and checkpoint id Then plr-a PlanError should be raised with "requires a CheckpointService" # ─────────────────────────────────────────────────────────── # State validation # ─────────────────────────────────────────────────────────── Scenario: rollback_plan rejects a plan in APPLIED terminal state Given plr-a plan lifecycle service with a mock checkpoint service And plr-a plan in APPLIED terminal state When plr-I call rollback_plan with the plan id and checkpoint id Then plr-a PlanError should be raised with "terminal state" And plr-the checkpoint service selective_rollback should NOT have been called Scenario: rollback_plan rejects a plan in CANCELLED terminal state Given plr-a plan lifecycle service with a mock checkpoint service And plr-a plan in CANCELLED terminal state When plr-I call rollback_plan with the plan id and checkpoint id Then plr-a PlanError should be raised with "terminal state" And plr-the checkpoint service selective_rollback should NOT have been called Scenario: rollback_plan accepts a plan in Execute/PROCESSING state Given plr-a plan lifecycle service with a mock checkpoint service And plr-a plan in Execute/PROCESSING state with a sandbox And plr-a checkpoint exists for the plan When plr-I call rollback_plan with the plan id and checkpoint id Then plr-the rollback should succeed Scenario: rollback_plan accepts a plan in Execute/QUEUED state Given plr-a plan lifecycle service with a mock checkpoint service And plr-a plan in Execute/QUEUED state with a sandbox And plr-a checkpoint exists for the plan When plr-I call rollback_plan with the plan id and checkpoint id Then plr-the rollback should succeed Scenario: rollback_plan accepts a plan in Strategize phase Given plr-a plan lifecycle service with a mock checkpoint service And plr-a plan in Strategize/QUEUED state with a sandbox And plr-a checkpoint exists for the plan When plr-I call rollback_plan with the plan id and checkpoint id Then plr-the rollback should succeed # ─────────────────────────────────────────────────────────── # Domain event emission # ─────────────────────────────────────────────────────────── Scenario: rollback_plan emits PLAN_ROLLED_BACK domain event on success Given plr-a plan lifecycle service with a mock checkpoint service and event bus And plr-a plan in Execute phase with a sandbox And plr-a checkpoint exists for the plan When plr-I call rollback_plan with the plan id and checkpoint id Then plr-the rollback should succeed And plr-a PLAN_ROLLED_BACK domain event should have been emitted And plr-the event should contain the checkpoint_id Scenario: rollback_plan does not emit events when event_bus is None Given plr-a plan lifecycle service with a mock checkpoint service but no event bus And plr-a plan in Execute phase with a sandbox And plr-a checkpoint exists for the plan When plr-I call rollback_plan with the plan id and checkpoint id Then plr-the rollback should succeed # ─────────────────────────────────────────────────────────── # NotFoundError propagation # ─────────────────────────────────────────────────────────── Scenario: rollback_plan raises NotFoundError for non-existent plan Given plr-a plan lifecycle service with a mock checkpoint service When plr-I call rollback_plan with a non-existent plan id Then plr-a NotFoundError should be raised