An outbound write is in flight. In another window, an administrator revokes the role of the user who started it.
Minutes later someone asks: did that call stop?
If the system answers only "stopped", that answer covers three questions at once: what allowed the call in the first place, whether it actually stopped, and whether what landed on the target has been undone.
One sentence: after a revocation the system can hand over three layers of evidence — the request, the confirmation, and the reconciliation. "Stopped" is the conclusion of the middle layer only; it cannot stand in for the other two.
If that is enough, the list and the four boundaries can be skipped.
One word covering three things
Revoking the grant answers "may this run next time". It takes effect before the next call: that call is refused at the execution point, and the record carries the role and policy revision the decision was made under.
Cancelling answers "did this run stop". It happens during execution, and the record is a three-state process — requested, confirmed, unknown — resolving to aborted or abandoned.
Revoking the effect answers "can what already happened be taken back". It happens after execution, and the record is a revoke window (unacknowledged / awaiting_target) plus a reconciliation conclusion (unknown / executed / not_executed).
The three can be delivered separately, and they can fail separately. Folding them into "stopped" treats three different kinds of evidence as one.Layer one: the request
The first thing an execution record can prove is what allowed it at the time.
In KeelBase the decision basis is frozen onto the write claim: the role and the governance policy revision are read at decision time and stored with the claim. A revocation happens later, and the row can still answer "under which role and which policy revision was this allowed".
The point is "frozen". When a reader cannot resolve either value it reports unknown, and never backfills it. Looking up today's role after a revocation returns today's fact, not the one in force then.
This layer answers the request, not the outcome. It proves the call was permitted. It does not prove it ran.
- Layer two: the confirmation A cancellation is a process, not a click.
When we ask, the state is requested. Only when the tool actually returns is it confirmed. If we cannot tell, it is unknown. The conclusion has one rule:
reading = abortable === 'yes' && state === 'confirmed' ? 'aborted' : 'abandoned'
Only a tool that declares it can stop, plus a confirmed cancellation, earns aborted. Every other combination is abandoned, with the outcome left unknown.
That line guards a specific misreading: a tool classified no or unknown may return too. What returned is our wait, not the target's work. Recording that as "aborted" would dress a local return up as a remote stop.
There is a boundary that has to be stated: revocation does not cancel an in-flight call. After the execution point and before the tool returns, authorization is not re-checked. Revocation affects the next call. For the one in flight, the only control is cancellation, and for an external tool that cannot be stopped, cancellation only ends our wait.
- Layer three: the reconciliation The first two layers are about what we know. The third asks the target.
After an outbound write times out, the ledger sits at unknown: the request may have arrived, or it may not. The only way to resolve it is one read-only question to the target, constructed so it cannot become a second write — GET only, once, no retry.
Only two answers converge: the target says executed, and the claim settles; the target says not_executed, and the claim is released. A timeout, an unreachable host, a non-2xx, an unrecognised body — all unknown, and the ledger stays where it is.
Silence is not an answer. This is where the layer is easiest to get wrong: reading "we could not find out" as "it did not run" manufactures evidence out of the absence of evidence.
One more record in this layer is easy to misread. An administrator can release a stuck claim with a written note. The disposition becomes closed_unknown while status stays claimed. That is not a verdict about the write; it is a decision about the handling: nobody knows whether it landed, and nobody will quietly resend it.
- A point in time: the "all three are zero" in two earlier pieces When the two earlier pieces were written, these layers really were zero in the code: no cancellation contract, no reconciliation, no in-flight listing, no grant version on the claim row. Writing "all three are zero" was accurate at the time.
That changed on 2026-11-07 with v1.3.1. It carries the cancellation contract and its three states, the in-flight claim listing with evidence-carrying release, read-only reconciliation with a query history, operation identity, role and policy revisions on the claim, tool capability declarations, and record-level review receipts.
This does not overturn the earlier conclusion, but it changes what can be delivered today. The sentence that still holds is narrower: there is no kill switch. Cancellation is a contract, not a guarantee. A tool classified no or unknown may still run to completion, and the ledger says abandoned. What was added is the ability to reconcile and revoke afterwards, not to stop the call.
- Four boundaries still open Release validates the length of the note (8–500 characters), not whether the reason is real. "Queue pressure" passes. The gap is still there. Only the WebSocket entry has a cancellation signal. An SSE client disconnect and the R4 retry path have none. Reconciliation needs the target to offer a read-only query. A target that does not offer one is recorded as a target-status-unavailable gap in the coverage statement; "never asked" cannot be read as "did not run". Revocation does not re-check mid-flight. That window is kept on purpose: adding a re-check means defining the points and the failure policy, and an unanswerable check is unknown, never "stopped". Closing What a revocation hands over should not be a sentence. It should be three records that can each be verified: what allowed the call, whether it stopped, and what actually happened on the other side.
"Stopped" is one of them. It can be questioned; it cannot answer for the other two.
The implementation described here comes from the open-source project KeelBase:
https://github.com/rain6fish/KeelBase
Drafted with an AI assistant. The system, the positions and the mistakes are mine.

