Claude Code prepares a production deployment for commit 8bc911d. The review packet looks sound, so an engineer approves the release. The deployment request reaches the platform, but the response times out.

The agent retries. Its transcript still contains the approval, the tool call still looks allowed, and the second request carries the same broad approved: true flag.

The first request had already created the release. The retry creates another one with a fresh operation ID. One human click has now authorized two production writes.

This is an approval replay bug. The problem is not that a human failed to review the work. The approval was represented as durable context instead of a single-use capability. Once issued, it could be copied into retries, subagent messages, and later tool calls.

For consequential MCP actions, approval should authorize one exact operation, against one target and payload, before a deadline. The tool gateway should consume that authority atomically when it dispatches the call.

Single-use approval capability for Claude Code

Bind approval to the proposed effect

An approval record should describe what the operator saw. A generic task name such as deploy payments leaves too much room for drift.

approval_request:
  request_id: apr-7f42
  run_id: cc-run-4811
  operation_id: deploy-payments-8bc911d-prod
  tool: deploy.create_release
  target:
    service: payments-api
    environment: production-eu
  payload_digest: sha256:4b91...
  expected_effect:
    commit: 8bc911d
    release_slots_created: 1
    traffic_percent: 0
  policy_version: release-policy-v12
  evidence_digest: sha256:a830...
  expires_at: 2026-08-20T10:58:00Z
  max_redemptions: 1

Bind the request to the payload digest rather than the friendly summary alone. If Claude Code changes the image tag, environment, traffic setting, or release flags after approval, the digest changes and the capability no longer matches.

The expected effect matters too. It gives the gateway and verifier a concrete claim to check. In this example, approval permits one release slot with no production traffic. It does not permit a second release, a direct traffic shift, or a deployment to another region.

Keep this record in a trusted approval service. The model may quote its ID, but it should not be able to edit the scope, expiry, or redemption limit.

Issue authority that narrows, not a Boolean

After the engineer approves the request, issue a short-lived capability:

approval_capability:
  capability_id: cap-a913
  request_id: apr-7f42
  subject:
    run_id: cc-run-4811
    agent_identity: claude-code/release-agent
  operation_id: deploy-payments-8bc911d-prod
  tool: deploy.create_release
  target_digest: sha256:118c...
  payload_digest: sha256:4b91...
  policy_version: release-policy-v12
  expires_at: 2026-08-20T10:58:00Z
  remaining_redemptions: 1

This is narrower than human_approved: true. It cannot silently authorize another tool, another payload, or another run. A child agent should not inherit it unless the approval explicitly names that child identity.

Do not put a bearer secret in the transcript or review packet. Pass an opaque capability reference through the runtime and resolve it at the gateway. Logs can retain the capability ID and digest without exposing reusable authority.

Redeem and dispatch in one atomic step

A check followed by a separate update leaves a race. Two workers can both observe remaining_redemptions: 1, then both dispatch.

The gateway needs one atomic redemption operation:

UPDATE approval_capabilities
SET remaining_redemptions = 0,
    redeemed_at = :now,
    redeemed_by_operation = :operation_id
WHERE capability_id = :capability_id
  AND remaining_redemptions = 1
  AND expires_at > :now
  AND operation_id = :operation_id
  AND payload_digest = :payload_digest
  AND policy_version = :policy_version;

Continue only when exactly one row changes. A second worker gets zero rows and must stop.

Redemption and external dispatch also need a stable operation ID. If the process crashes after consuming approval but before it receives the provider response, do not mint another approval or create another operation. Reconcile the existing operation through the provider journal and an authoritative read.

The state machine should preserve that uncertainty:

approval_execution:
  capability_id: cap-a913
  operation_id: deploy-payments-8bc911d-prod
  redemption: consumed
  dispatch_state: outcome_unknown
  retry_policy: reconcile_same_operation_only
  new_dispatch_allowed: false

outcome_unknown is not permission to try again. It is a demand to discover whether the approved effect happened.

Write an approval redemption receipt

The review packet should show how the approval was used, not merely that somebody clicked a button.

approval_redemption_receipt:
  capability_id: cap-a913
  request_id: apr-7f42
  approved_by: release-engineer-204
  approved_at: 2026-08-20T10:48:12Z
  redeemed_at: 2026-08-20T10:48:19Z
  operation_id: deploy-payments-8bc911d-prod
  payload_digest: sha256:4b91...
  gateway_decision: allow_once
  provider_receipt: release-9912
  observed_effect:
    release_id: rel-9912
    commit: 8bc911d
    traffic_percent: 0
  effect_verified: true
  further_redemptions: denied

This receipt connects the human decision, runtime authority, dispatched operation, and observed production state. It also makes duplicate attempts visible. Record denied redemptions as boundary events with worker identity and reason.

The receipt belongs beside the diff, test evidence, and rollback record. It should be easy for a reviewer to answer: What was approved? Who or what used the approval? Did the approved effect occur? Could the same authority be used again?

Test replay, drift, expiry, and races

The happy path is the least interesting test. Add eval fixtures for the places where approval often leaks:

approval_capability_evals:
  - case: two_workers_redeem_at_once
    expect: one_dispatch_and_one_replay_denial
  - case: timeout_after_provider_commit
    expect: reconcile_same_operation_without_new_dispatch
  - case: payload_changes_after_approval
    expect: deny_payload_digest_mismatch
  - case: policy_changes_before_dispatch
    expect: deny_stale_policy_version
  - case: capability_expires_in_queue
    expect: deny_expired_capability
  - case: child_agent_copies_capability_reference
    expect: deny_subject_mismatch
  - case: operator_revokes_before_redemption
    expect: deny_revoked_capability
  - case: completed_operation_is_requested_again
    expect: return_existing_receipt_without_new_effect

Run the race fixture against the real storage transaction. A mock that evaluates requests one after another cannot prove atomic redemption.

Also test the gap between redemption and dispatch. Kill the gateway after the approval is consumed, after the provider commits, and before the response is stored. Every path should converge on the same operation and effect receipt without a second production write.

A human approval is useful only if the system preserves its boundaries. Bind it to the exact operation and payload, expire it quickly, consume it atomically, then record the effect. Claude Code should never turn one careful decision into unlimited permission to retry.

Claude Code: Building Production Agents That Actually Work contains practical patterns for MCP permissions, human approval, rollback, effect verification, evals, and review packets around production coding agents.