Consider a hypothetical release task. Claude Code makes one MCP call to ask the deployment service for a preview. Another queues a database compatibility check. A subagent prepares a ticket update. Then the operator sees the wrong commit in the run and presses stop.
The model stops producing tokens. The queue does not care.
Thirty seconds later, the compatibility job finishes and publishes its result. The ticket update follows. The deployment preview reserves a shared environment. The run was stopped, yet its authority kept moving through systems that had already accepted work.
Killing the client process is not a production stop control. I want a cancellation barrier that revokes the run’s authority, rejects work that has not started, reconciles work already in flight, and proves that no later effect can still appear under the old run ID.
Updated 26 September 2026 with a downloadable responder handoff worksheet and separate restart-approval gates. The records below illustrate a control-plane design; they are not built-in Claude Code or MCP configuration fields.
Separate four meanings of stopped
Teams often use one word for four different states:
stop_states:
generation_stopped:
meaning: model is no longer producing output
dispatch_stopped:
meaning: gateway will not accept new calls from the run
queued_work_cancelled:
meaning: accepted calls that have not started cannot execute
effects_settled:
meaning: in-flight calls are reconciled and no unknown effect remains
Only the last state is safe enough for an operator to hear “the run has stopped.”
A terminal interrupt may achieve the first state. Closing the session may eventually achieve the second. Neither tells you what a remote queue, CI worker, deployment controller, or SaaS API will do with work it already holds.
This becomes harder with subagents. A parent run can stop while a child process still has a valid lease and a queued tool call. If the child identity is not tied to the parent’s authority, the supposed kill switch only removes the visible part of the run.
Put the run ID on every piece of work
Cancellation needs an address. Give every run an immutable ID, then propagate it into child runs, gateway decisions, queue messages, provider metadata, and effect receipts.
run_authority:
run_id: cc-run-4421
task_id: release-payments-8bc911d
root_lease: lease-90e4
child_runs:
- run_id: cc-run-4421-checks
lease: lease-90e4-checks
- run_id: cc-run-4421-ticket
lease: lease-90e4-ticket
valid_until: 2026-08-16T11:20:00Z
revocation_epoch: 17
Every queued message should carry at least run_id, lease, revocation_epoch, operation_id, and an expiry. A worker checks those fields when it starts, not only when the request enters the queue.
That second check closes a common race. A gateway can approve a call at 10:51:03, the operator can revoke the run at 10:51:04, and a worker can pick up the message at 10:51:12. If the worker trusts the old gateway decision without checking current authority, revoked work still runs.
This follows the same rule as checking MCP authority immediately before dispatch. Approval belongs to a specific capability at a specific time. It is not a permanent property of a queue message.
Use a cancellation barrier, not a fire-and-forget signal
A stop request should create a barrier record before the system starts cancelling components:
cancellation_barrier:
barrier_id: stop-cc-run-4421-01
run_id: cc-run-4421
requested_at: 2026-08-16T10:51:04.018Z
requested_by: operator:thomas
reason: wrong_commit_detected
revoke:
root_lease: lease-90e4
descendants: true
new_revocation_epoch: 18
queue_policy:
reject_not_started: true
retain_for_audit: true
in_flight_policy:
request_provider_cancel: true
reconcile_effects: true
completion_rule:
pending_calls: 0
unknown_outcomes: 0
active_leases: 0
Write the barrier durably first. If the cancellation worker crashes halfway through, another worker can resume from the same record. An in-memory flag disappears at exactly the moment you need it most.
The barrier gives cooperating services one ordering point. Work stamped with epoch 17 is stale after epoch 18 exists, but the number alone enforces nothing. Workers must check current authority, and consequential writes need a fence or equivalent check at the effect boundary. A stale cache must not authorize work when that check is unavailable.
Do not delete queued messages silently. Mark them cancelled and preserve their operation IDs. During an incident, you need to know which actions the system rejected and which ones had already crossed into execution.
Treat in-flight calls as unresolved until proven otherwise
Some providers support cancellation. Many only acknowledge that a cancellation request arrived. Others cannot cancel once an operation reaches a commit stage.
Record that difference:
in_flight_call:
operation_id: reserve-preview-payments-8bc911d
tool: deployment.reserve_preview
provider_job: preview-7712
state_at_barrier: running
cancellation:
requested_at: 2026-08-16T10:51:04.233Z
provider_response: accepted
effect_check:
expected_absence:
reservation_for_run: cc-run-4421
observed: none
checked_at: 2026-08-16T10:51:09.804Z
settled_state: cancelled_without_effect
provider_response: accepted is not the settled state. The provider may race with cancellation and commit the effect first. Query the authoritative system for the expected effect, just as you would after an external write with a missing response.
There are three useful outcomes:
cancelled_without_effect
The provider stopped and authoritative state shows no effect.
completed_and_verified
The operation crossed the commit point. Record the effect and start the approved recovery path.
unknown
The system cannot prove whether an effect occurred. Keep the barrier open and assign reconciliation to a person.
Do not translate unknown into cancelled. That makes the dashboard look tidy while leaving the operator blind to a possible production change.
Make descendants lose authority with the parent
A child run should receive less authority than its parent and should never outlive the parent’s lease unless a human explicitly promotes it to an independent task.
The gateway can enforce the relationship:
child_dispatch_check:
child_run: cc-run-4421-ticket
parent_run: cc-run-4421
child_epoch: 17
current_parent_epoch: 18
parent_lease_status: revoked
decision: deny_parent_revoked
external_call_sent: false
Do not rely on the parent to notify every child. The parent may be the process that crashed. Workers and gateways should resolve current authority from the control plane before starting consequential work.
Also check scheduled retries. A retry timer is queued authority in another form. When the barrier opens, cancel timers, callbacks, delayed messages, and workflow continuations attached to the run. Otherwise the system can look quiet for five minutes and wake up under stale authority.
Return a stop receipt that can be reviewed
The operator needs evidence, not a green icon. Produce a stop receipt only after the completion rule passes:
stop_receipt:
barrier_id: stop-cc-run-4421-01
run_id: cc-run-4421
authority:
root_lease: revoked
descendant_leases_revoked: 2
current_epoch: 18
work:
queued_cancelled: 3
in_flight_settled: 1
scheduled_retries_cancelled: 2
effects:
completed_and_verified: 0
cancelled_without_effect: 1
unknown: 0
last_settled_at: 2026-08-16T10:51:09.804Z
quiet_period_seconds: 30
verified_at: 2026-08-16T10:51:39.921Z
decision: stopped_and_quiet
The quiet period may reveal delayed callbacks and queue visibility lag. It cannot prove that a sleeping worker will never wake up. Its duration should match the systems involved, and it must accompany enforcement checks rather than replace them. A local worker queue and a third-party deployment service have different settling behaviour.
Put this receipt in the review packet. If an effect completed before cancellation, include its effect receipt and rollback owner. If anything remains unknown, the final decision must say stop_incomplete and name the person holding the reconciliation task.
Test the stop path while nothing is on fire
A stop control that works only when every dependency is healthy is decoration. Add fixtures for the races you expect to see:
cancellation_evals:
- case: queued_call_starts_after_parent_revocation
expect: deny_parent_revoked
- case: provider_commits_during_cancel_request
expect: completed_and_verified
- case: cancellation_worker_crashes_mid_barrier
expect: resume_from_durable_barrier
- case: delayed_retry_fires_after_revocation
expect: deny_stale_epoch
- case: provider_status_unavailable
expect: stop_incomplete_unknown_outcome
- case: child_run_misses_parent_notification
expect: deny_on_gateway_authority_check
Run the test with a provider stub that deliberately commits during the cancellation race. Then break the status endpoint. The system should preserve the unknown outcome rather than invent a clean cancellation.
Hand the incident to a person before restarting the agent
Imagine the stop happens just before the on-call shift changes. The first responder leaves a message: “Agent stopped. Preview job probably cancelled.” The next engineer opens a fresh run and tries the release again.
That handoff has lost the fact that the provider job is unresolved. A new run ID does not cancel the old reservation or make it safe to repeat the operation.
Download the stop-and-restart handoff worksheet. It is a blank operational template, not a Claude Code setting or an MCP protocol feature. Keep a completed copy in your restricted incident system. Reference evidence there rather than copying credentials or raw tool payloads into a shared document.
The worksheet separates incident ownership from restart authority. The responder who accepts an unresolved operation owns the investigation. Accepting it does not turn its status into cancelled, and it does not approve a restart.
Record each outstanding operation separately. Include its provider job ID, the last authoritative status check, the next check due, and the person who has acknowledged ownership. “Platform team” is too vague during a shift change. Use an on-call identity that someone has actually accepted, with an escalation path if the next check fails.
A stop receipt is not restart permission
A safe restart needs a separate decision. First, the responsible operator reconciles the old run’s effects. Then the restart approver decides which remaining work may execute under fresh authority.
Use these gates:
- Show that old work cannot write later. Reference enforcement receipts for revoked leases or an effect-side fence. Where fencing is unavailable, obtain provider terminal states and effect checks. Keep restart blocked without that evidence.
- Give every operation a known outcome. Include jobs that committed during cancellation. Assign unresolved operations to reconciliation rather than retrying them blindly.
- Review the current target state and choose whether to continue, compensate or abandon the remaining work. Replan if the old action no longer fits.
- Record the new run identity, allowed operations, expiry and named approver. Do not dispatch without that fresh, scoped authority.
Do not reactivate the old lease to make recovery easier. If recovery requires a write, authorize that write separately. A compensating action can cause damage too, particularly when another engineer has changed the target since the incident began. Rollback must preserve later changes.
A quiet queue is supporting evidence, not proof that a sleeping worker has lost write access. The barrier needs enforcement at the point where an effect can occur. For shared workers, an effect-side fencing check can reject stale generations. For a provider you cannot fence, keep the operation unresolved until the provider’s terminal state and effect checks support a decision.
Rehearse the shift change with one unresolved job
Try the worksheet in a tabletop exercise before wiring it into automation. Use a synthetic provider job that acknowledges cancellation but has no terminal result yet. Keep the restart decision blocked.
Have the outgoing responder fill in the operation row. The incoming responder should be able to answer: which system must I query, what result am I waiting for, and when must I escalate? If they need the original chat to answer, the handoff is incomplete.
Next, give them evidence that the job completed and reserved an environment. The correct response is to record the effect and choose an approved recovery path. It is not to mark the job cancelled because that was the requested outcome. Only after reconciliation and fresh approval should the exercise reach restart_approved.
This is a proposed exercise, not a claim that a live provider or production cancellation system has passed it. The worksheet makes the decision reviewable. Your gateways and workers still have to enforce it.
A run with unknown effects remains stop_incomplete, even when a named person owns the next check. That distinction is worth keeping at three in the morning.
For the wider operating workflow, see what is inside the Claude Code production-agents book.