I built a small harness to watch what happens when you try to stop an agent that has already started doing things. The first run answered the question faster than I expected.
I killed the agent process 1.300 seconds into the run. It was gone 3 milliseconds later. The operations it had already handed to a downstream service kept running, and the last of them completed 4.936 seconds after I issued STOP, and 4.933 seconds after the process itself was dead. Nothing cancelled them. They finished. The process was stopped. The work it had launched was not.
That was not specific to a long call. I ran the same intervention against work the agent had queued and work it had delegated. A job it had inserted into a queue ran to completion 2.907 seconds after I issued STOP, and 2.906 seconds after the process was gone, because the queue is a row in a table and the row did not know the producer was dead. A subtask it had handed to a separate worker finished on its own, because killing a parent does not necessarily kill an independently running child. Same result three ways. Stopping execution does not, by itself, stop the effects execution has already set in motion.
So I tried the other obvious control
Instead of killing the agent, I left it running and revoked its credential. What happened next depended entirely on where revocation was enforced, and the two cases are worth stating precisely, because people collapse them into one.
When the downstream service used a stateless validator that checked signature and expiry only, revocation at the issuer changed nothing. After I revoked, the service authorized 13 more operations on the revoked token, the last one 5.077 seconds later. Nothing was refused, because nothing consulted the revocation list. I had revoked the credential in the issuer's records and nowhere that mattered.
When the downstream service checked the revocation list on every access decision, revocation worked. Not one call on the revoked token was authorized after I revoked it, and the agent's retries were refused 13 times. The authority system did exactly its job.
And two operations still completed after the revocation, the last one 3.382 seconds later.
Those two had been authorized before I revoked. In this harness, authorization happened once, at acceptance, and revocation did not re-authorize or cancel an operation already in flight, so even flawless revocation left them alone. Enforcing revocation at the resource was worth measuring. It cut the effect residual by 1.847 seconds and drove new authorizations from 13 to zero. It did not touch the work already in flight.
Three states, not two
At that point the two-state picture I had started with was already wrong. Stopping and revoking are not two operations on one problem. They are operations on three different states.
- Execution state. Is the agent still computing? STOP changes this.
- Authority state. Can the actor still initiate newly authorized work? REVOKE changes this, and only where revocation is actually enforced.
- Effect state. What work has already crossed a boundary and is now progressing on its own? Neither STOP nor REVOKE changes this.
The third state is the one that hides, because from the operator's chair it looks like it should already be covered by the first two. It is not. That is the finding, and the harness exists to make it a measurement instead of an argument.
What a real responder does
So I did what a responder actually does once they understand the problem. I killed the runtime and revoked the credential, both, correctly. Six effects still completed. A queued job, a delegated subtask, and four operations that had already been accepted. All six landed after the process was already dead. One of them was irreversible.
And one of them exposed something I had not been looking for. The subtask I had delegated was running on a credential of its own, issued to the child and descended from the parent token I revoked. I revoked the parent. I did not revoke the task. The child's authority was never touched, so it authorized and completed fresh work after my intervention, on a resource that was correctly checking the revocation list the whole time. Enforcement did not fail. The scope was wrong. Revoking a credential and revoking everything descended from a task are different operations, and I only saw the difference because the run let the child act after the parent was gone.
The thing that changed the count
What cancellation changed was the number of effects that ultimately completed, not the residual window that had already accumulated before the cancel arrived. The cancel was propagated by task ancestry to every hop still working. In the stop-and-revoke baseline, six effects completed after the intervention. With propagated cancellation, three did. Four cancellations were acknowledged across three downstream components, the resource twice, the queue worker, and the delegated worker, and they accounted for the cancellable work that would otherwise have run to completion: the queued job, the delegated subtask, a five-second call, and a six-second stream, all stopped in flight. The three effects that did complete all landed inside the roughly two-second responder gap before the cancel arrived, the last of them 0.419 seconds after the intervention. The identical profile without cancellation ran its six effects to completion, the last 4.216 seconds after the intervention.
Here is the whole arc in one view. Every figure is a query over one append-only event log, and the runs asserted 36 predictions about these values before I looked at them. All 36 held.
| Intervention | New authority after revoke | Effects still completing | Irreversible, already done | Effect residual |
|---|---|---|---|---|
| STOP alone | not applicable | 3 | 0 | 4,936 ms |
| REVOKE, resource not checking | 13 | 15 | 0 | 5,229 ms |
| REVOKE, resource checking | 0, and 13 refused | 2 | 0 | 3,382 ms |
| STOP and REVOKE the token | 0 | 6 | 1 | 4,216 ms |
| STOP, REVOKE, and propagated CANCEL | 0 | 3 | 1 | 419 ms |
Effect residual is measured from the first intervention to the final completed effect. In the cancellation run the 419 ms residual closed before CANCEL was issued; cancellation did not shrink that window, it prevented later outstanding work from completing at all.
The floor
One effect completed anyway, in the run with cancellation and in the run without it. It was an irreversible operation that had already finished inside the responder gap, before the cancel could reach it. Cancellation collapses what is still cancellable. It cannot reach what has already completed, and some effects cannot be reversed at all once they land. That remainder is not a limitation of the harness. It is the shape of the problem. There is a floor beneath stop, revoke, and cancel, and the size of that floor is set earlier, by how much an agent is allowed to finish before anyone else gets to weigh in. That is a different essay.
Why I can say any of this
I can make every claim above for one reason. The task's identity survived each handoff. Every token carried its ancestry, and every effect carried the correlation ID of the task that launched it, from the agent to the queue to the delegate to the resource. That thread is what let me ask, after each intervention, whether anything belonging to this task completed afterward, and get an answer rather than a guess. Without it, incident response for an agent cannot separate an attempt that was blocked from an effect that completed. It can only assert containment. Once work has been handed to a queue, a sub-agent, and a remote service, that thread is the difference between knowing and hoping, and it is the cheapest thing in the whole design to add and the easiest to leave out.
What this changes
A serious containment mechanism has to answer three questions, not one. Can I stop the computation. Can I invalidate the authority it has already propagated, at the place enforcement actually happens, and at the scope that covers everything the task spawned. Can I prove the effects it already launched have stopped, or account for the ones that cannot be. Stopping the agent answers the first. Revoking its credential answers the second, sometimes, and at the scope you remembered to use. Neither answers the third, and the third is where the harm lives.
The line that sent me down this path is one sentence in an enterprise agent-security guide: stopping execution and revoking authority are separate operations. It is correct, and it is smaller than what is actually true. They are separate operations on three separate states, and I could not see the third clearly until I built the thing and watched execution go quiet while the effects kept completing.
Limitations
This is a miniature. The services are local, the credentials are not real, every transfer is a counter, and the durations were chosen to make the windows visible rather than to model production latency. The numbers describe the mechanism, not a benchmark. The propagated cancellation looks clean here because every hop was built to honor it, and in a real system that last clause is the expensive part. A hop that ignores a cancel shows up as a missing acknowledgment rather than a silent success, which is at least detectable, but only if you threaded the identity to detect it with. The two-second responder gap in the last run is not incidental either. It is where the irreversible effect completed, and in a real incident the interval between deciding to act and the cancel actually landing is where the floor gets set.
None of that changes the finding. You can stop an agent and revoke its authority, both correctly, and still have work from that task completing seconds later, some of it past the point where anything can call it back. The measurement I care about now is not whether I can stop an agent. It is the residual window after I do, and whether I threaded enough of the task's identity through the system to know when that window has actually closed.