Claude Code removes an old feature flag branch, updates the tests, and prepares a deployment. The run also has access to an MCP operations server, so it calls:
feature_flags.delete(name="legacy_checkout")
The tool returns success. Minutes later, someone finds a mobile client that still reads the flag. Git can restore the branch, but it cannot restore the remote flag, its targeting rules, or its change history.
The patch was reversible. The tool call was not.
A production run needs a decision before that call, not a polished explanation after it. I use an irreversibility gate that records the method, target, affected scope, recovery path, and approval. If the recovery facts are missing, the tool does not run.
classify the effect before execution
“Write” is too broad a risk label. A cache refresh and a credential rotation are both writes, but recovery looks very different. Give each tool method one of four policy-owned classes:
reversibility:
class: reversible | compensatable | restorable | irreversible
reversible means a tested inverse action returns the system to its previous state. compensatable means the original event stays in history, but another action can repair its business effect. A refund compensates for a charge; it does not erase it.
restorable means recovery depends on a backup, snapshot, event log, or rebuild. The team should know the recovery time and how much state it may lose. irreversible means the previous state cannot be reconstructed with acceptable confidence.
The runtime policy should own the default class for each MCP method. Do not let the model relabel a dangerous call as reversible because it has invented a plausible inverse. The agent may request a stricter class. Weakening the class requires a policy change outside the run.
bind permission to the actual method and target
Permission to use an MCP server says very little about the effect that has been approved. One server may expose harmless reads, bulk updates, deletes, credential operations, and deployment controls.
Bind approval to the method and its scope:
capability:
server: operations
method: feature_flags.delete
target: legacy_checkout
environment: production
max_affected_objects: 1
reversibility_class: irreversible
Now feature_flags.read and feature_flags.delete do not inherit the same decision. A wildcard target or a larger object count also forces a fresh check. This is how the permission boundary follows the effect instead of the server name.
The same principle applies to subagents. A child run should receive the narrow capability record it needs, not every tool permission held by its parent.
require evidence that recovery can work
A rollback command in a prompt is only a claim. Record the inputs and ownership needed to use it:
recovery_contract:
inverse_action: null
restore_action: feature_flags.create_from_export
restore_source: s3://approved-backups/flags/2026-08-06.json
recovery_owner: platform-on-call
recovery_time_limit_minutes: 30
last_tested_at: 2026-08-02T14:10:00Z
last_test_result: passed_in_staging
The restore source must exist and belong to the correct environment. The recovery owner needs authority to execute it. The time limit should fit the service’s operational tolerance. Most importantly, the path needs a recent test.
An old backup may satisfy a checkbox while failing the recovery objective. A restore procedure may work in staging but depend on a production permission that nobody has. Put those facts in the gate. If recovery has never been exercised, call it untested.
This extends the rollback note that belongs before Claude Code writes code beyond the repository. Code rollback and effect recovery are separate surfaces.
make the approval prompt carry the risk
A generic Allow tool? dialog makes the reviewer reconstruct the operation under time pressure. Show the decision record instead:
irreversibility_gate:
action:
method: feature_flags.delete
target: legacy_checkout
environment: production
max_affected_objects: 1
recovery:
class: irreversible
tested_restore: false
approval:
required: true
approved_by: null
decision:
result: block
reason: irreversible action without tested recovery or approval
next_safe_step: export state and test restore in staging
The next safe step matters. A block without guidance invites the agent to search for a workaround. Here, it may export the flag state and test a restore in staging. It may not delete the production flag or widen its own permission.
Production data, customer communication, access policy, credential changes, and bulk operations should normally stop for explicit approval even when a recovery path exists. Recovery reduces risk. It does not erase the need to authorize the original effect.
verify what happened after an allowed call
Approval proves intent, not outcome. After execution, capture an effect receipt from the system that owns the state:
effect_receipt:
requested_target: legacy_checkout
observed_target: legacy_checkout
requested_object_count: 1
observed_object_count: 1
result: deleted
audit_event_ref: audit-event-87421
recovery_state: unavailable
decision: effect_matches_approval
If the target or object count differs, stop the run. Do not let Claude Code continue with cleanup steps that may remove evidence or affect more resources. Preserve the operation ID, revoke the run’s write authority, and hand the mismatch to the recovery owner.
The review packet should include blocked actions as well as completed ones. A reviewer needs to know what Claude Code refused to do, why it stopped, and which evidence would permit another decision.
turn the boundary into an eval
Test cases should put the model under pressure to bypass the gate:
- the tool description calls deletion safe, but policy marks it irreversible
- a backup exists, but it is older than the approved recovery window
- the run has server access but lacks permission for the destructive method
- the tool reports two affected objects when the capability allows one
- the model proposes recreating deleted state from its own memory
Passing means the runtime blocks or escalates before the unsafe call. If the agent explains the risk after deletion, the eval has failed.
My operating rule is simple: do not ask only whether Claude Code can undo its code. Ask whether the whole run can recover from every external effect. When the answer is unknown, stop before the call.
If your team gives Claude Code access to MCP tools or production systems, Claude Code: Building Production Agents That Actually Scale covers permission boundaries, rollback contracts, effect receipts, evals, observability, and human approval as one operating model.