Claude Code prepares a schema migration that replaces customer_status with a new enum. The patch is tidy. Migration tests pass. The review packet contains this line:

Rollback: restore the previous schema and redeploy the prior commit.

It sounds responsible. It proves almost nothing.

The old application may fail to read rows written after the migration. The database snapshot may have expired. A feature flag may cover the main read path while a queue consumer keeps writing the new format. The restore might take forty minutes against a ten-minute incident budget.

The agent has described recovery. It has not shown that recovery is available.

Before approving a production state change, I want a rollback rehearsal tied to the exact change. The rehearsal verifies the target, runs the real recovery mechanism in isolation, covers state outside Git, checks compatibility, and records how long recovery took.

Claude Code rollback rehearsal

verify the recovery target exists

A vague instruction such as “deploy the old version” leaves the reviewer to reconstruct the target. Start with exact identifiers:

rollback_target:
  app_commit: 3d5a9be
  schema_version: 184
  database_snapshot: customer-db-pre-184
  feature_flag_state:
    customer_status_v2: false

Then resolve each target before approval. Check that the deployment artifact for commit 3d5a9be still exists, that schema version 184 matches it, and that the snapshot belongs to the correct database and environment.

Fail closed when one target is missing. An expired snapshot does not become a recovery plan because the prompt contains its old name.

This is the next step after writing a rollback note before Claude Code changes code. The note defines the intended path. The rehearsal tests it.

run the real restore in isolation

A copied runbook command is weak evidence. Run the same recovery mechanism against an isolated copy or disposable environment:

rehearsal:
  environment: restore-sandbox-2026-08-08
  source_snapshot: customer-db-pre-184
  restore_command: ./ops/restore-schema --to 184
  deploy_artifact: app-3d5a9be.sha256:8f71...
  started_at: 2026-08-08T08:04:12Z
  finished_at: 2026-08-08T08:10:37Z
  exit_code: 0

The sandbox identity matters. A rehearsal must not become an unapproved production write. Bind the agent’s capability record to the sandbox environment and the narrow restore methods it needs. Production remains read-only during the exercise.

An exit code of zero is useful, but it only says the command finished. It does not prove the old service can operate on the restored state.

map every forward effect to recovery

git revert covers repository state. A production run may also change a database, a feature flag, an MCP object, a queue schema, or an external service.

List the forward effects and map each one to a recovery action:

side_effect_coverage:
  - forward: deploy app commit 91c2f40
    recovery: deploy app commit 3d5a9be
  - forward: migrate schema 184 to 185
    recovery: restore snapshot customer-db-pre-184
  - forward: set customer_status_v2=true
    recovery: set customer_status_v2=false

One unmapped effect blocks approval. This rule is especially important for MCP writes because the repository may contain no trace of the external object that changed. An effect receipt can identify the observed target and resulting state. The rehearsal record should point to that receipt rather than relying on the agent’s summary.

test old code against new data

The awkward rollback test is the one most likely to find a real problem:

  1. Start from schema version 184.
  2. Apply migration 185.
  3. Write a customer record that uses the new enum value.
  4. Restore the old application and schema path.
  5. Ask the old application to read the record.

A failed probe might look like this:

compatibility_probe:
  fixture: customer-created-under-v185
  old_reader_result: fail
  reason: unknown_status_value
  rollback_decision: blocked

This failure is good evidence. The restore command worked, but the recovered application cannot read data produced after the migration. The team may need an expand-and-contract migration, a dual-read period, or a forward fix instead of rollback.

Claude Code should report that constraint. It should not turn a clean restore log into confidence about the whole service.

put a time budget on the rehearsal

Recovery that finishes eventually may still fail the operating target. Record the full duration and compare it with the incident budget:

recovery_budget:
  maximum_minutes: 10
  rehearsed_minutes: 6.42
  includes:
    - artifact retrieval
    - snapshot restore
    - application deployment
    - cache warm-up
    - verification probes
  result: pass

A rehearsal against ten fixture rows cannot predict the restore time for a two-terabyte database without an explicit scaling assumption. Record the data volume and test environment beside the timing result. Otherwise the number is decoration.

verify the recovered behaviour

A generic health endpoint can stay green while the customer path is broken. Check the behaviour affected by the change:

post_recovery_checks:
  schema_version: 184
  old_app_health: pass
  customer_read_path: pass
  customer_write_path: pass
  queue_consumers: pass
  error_rate: within_baseline
  synthetic_customer_read: pass

Choose probes from the failure mode. A queue format change needs a producer and consumer check. A permission change needs an allowed action and a denied action. A feature flag change needs evidence that every relevant reader and writer follows the restored state.

These checks belong in the Claude Code review packet beside the patch, test evidence, boundary events, and approval record.

save a compact rollback rehearsal record

The reviewer should not need to read a full terminal transcript. Preserve the raw evidence, then summarize it on one page:

rollback_rehearsal:
  change_id: customer-status-v2
  forward_commit: 91c2f40
  recovery_target: 3d5a9be
  target_verified: true
  external_effects_covered: 3/3
  compatibility_probe: pass
  duration_minutes: 6.42
  budget_minutes: 10
  post_recovery_checks: pass
  rehearsed_at: 2026-08-08T08:10:37Z
  evidence_refs:
    - restore-log-sha256:4a17...
    - probe-results-sha256:9c02...
  decision: rollback_path_verified

Expire this record when the code, schema, tool configuration, deployment artifact, or recovery environment changes. Recovery evidence belongs to a specific change and state. It is not a permanent property of the project.

My approval rule is straightforward: name the target, rehearse the real restore path in isolation, cover every external effect, test compatibility, meet the recovery budget, and verify the recovered service. If one check is missing, the rollback plan is still a draft.

Claude Code: Building Production Agents That Actually Work connects rollback rehearsals with MCP permissions, effect receipts, evals, traces, and review packets for production coding-agent work.