I would ask the product team which promise the button makes. “Stop working on this” is achievable for future work. “Undo everything already done” may not be. If the UI says only “Cancelled,” a user can reasonably assume a submitted write did not happen. That assumption can be false.

The runtime needs a durable cancellation request with an ordering point. Once accepted, it prevents new model steps and new external operations. The current worker observes it through its state transitions, not only through a best-effort process signal. A lease holder that wakes from a long pause must see the current run version before preparing another effect.

A cancellation request closes the gate to new work. An external operation already sent may enter an unknown state and must be reconciled before the run can report its final outcome.
Cancellation fences future effects. It does not erase an in-flight one.

For a model call, I can try to cancel the network request or stop consuming its stream. If the provider has already computed part of the answer, cost may still accrue. The runtime discards a late response unless it was already committed as part of a step before cancellation. It must not take a late tool proposal from that response and execute it.

A read-only tool can usually be cancelled or allowed to finish without business side effects. “Read-only” still needs a real contract. A query could create a paid job, reveal sensitive data to an external service, or write a log. For a genuinely pure read, a late result can be ignored. For something with hidden effects, classify it like a write.

The external write is where the promise becomes precise. Suppose the agent submitted a request to publish a report. We have three meaningful states: not sent, sent with confirmed outcome, and sent with unknown outcome. If cancellation wins before the durable send transition, do not send. If the request has left the process, a timeout or worker death does not prove the report was unpublished. Mark the operation as pending resolution, query the external system using the stable operation ID, and tell the user that cancellation is still settling this effect. If it committed, report that fact. Any removal of the report is a separate authorized action, not a rollback hidden inside cancellation.

The race with normal completion needs deterministic local ordering. Use a compare-and-swap on the run version or a serialized event stream. If completion commits first, the cancellation request can return “already completed” along with the result. If cancellation commits first, the worker cannot add a new operation after it. An operation already in flight may still finish later, so the final run state can be “cancelled, effect resolution pending” until reconciliation completes. A single terminal boolean cannot express this honestly.

One interviewer push is to kill the worker immediately when cancellation is clicked. That can help stop local compute, but it may make the state harder to recover. If the worker was about to record an external result, killing it turns a known success into an unknown one. Persist the request, fence future steps, let the operation ledger reconcile, then stop the worker. Temporal's TypeScript cancellation guide is an implementation reminder that an activity must observe cancellation, often through heartbeats. A cancel signal does not revoke bytes already sent to another API.

Then they ask what happens if the user cancels and immediately starts a new run to do the same task. The old operation identity must remain discoverable. A new run must not create a second publish while the first is unknown. The deduplication key may need to be tied to the business action, not merely the run ID, and the product has to decide whether the new request is a retry of the old action or a distinct new one. We cannot infer that from two identical prompts alone.

I would test the boundaries with a fake provider that pauses before accepting, after accepting but before responding, and after responding but before the worker records success. Cancel at each point, crash a worker, and resume on a different worker. The expected user message should differ across those cases. That is how we find out whether the button's wording is honest.