# What invariants prevent cancellation from racing with already-dispatched work? **Date of Record:** 2026 November 2 **Scope:** Applies to cancellation protocols where a cancel message is sent to stop work already in flight. Does not apply to cancellation of not-yet-dispatched work, which is a queue operation rather than a race. ## Core Distinction Cancellation is not the removal of work; it is a request that in-flight work stop. If the work has already been dispatched to a worker that has begun executing, cancellation can arrive at the worker's queue but not at the worker's hands. The work completes, side effects fire, and the requester believes the work was cancelled. **Invariant.** Cancellation is effective if and only if `cancel_received_time < side_effect_commit_time`, OR the work is `explicitly_compensable` (a rollback exists and is executed). Cancellation that arrives after commit is a no-op; compensation is the only remedy. **Violation.** The **Post-Dispatch Cancel**. The requester sends cancel at t=100ms, the cancel reaches the worker's queue at t=110ms, the worker committed the side effect at t=105ms. The worker sees the cancel and stops nothing — the work is done. The requester believes it cancelled, and the duplicate effect (cancelled-per-requester, executed-per-worker) surfaces later as an unexplained side effect. **Asymmetry.** The cancel is delivered and acknowledged. The side effect is committed. Both are correct in isolation. The window between cancel-commit and worker-commit is invisible from either end because each observes its own success. **Recorded elements.** The `cancel-deadline-receipt` (the last instant at which cancel is effective), the `commit-timestamp-signature` (per-execution, when side effects committed), and the `compensability-receipt` (whether the work has an explicit rollback) together make the effective-or-compensated check possible. ## What Was Observed **Signature.** Side effects from work that was logged as cancelled. The requester's cancel record and the worker's execution record both exist, on different sides of the boundary, and neither references the other. **Trace.** Cancellation sent at 100ms. Worker commits at 105ms. Cancellation arrives in worker queue at 110ms. Worker's cancel handler finds the work already complete and exits. Side effect exists; cancel record exists; the two are in different logs and not correlated. Subsequent operations see the side effect and cannot reconcile with the cancel. **Why monitoring stays green.** Cancel was sent and acknowledged. Worker executed successfully. Both metrics pass. The unreconciled side effect shows up only if a downstream operation is confused by it, and confusion looks like a downstream bug. ## Condition of Validity The instance must ensure at least one holds before it treats cancellation as effective: 1. **Must know the cancel deadline.** The last instant at which cancel is effective is defined and communicated; cancel after that deadline is expected to be ineffective. 2. **Must compensate after deadline.** Work that commits after the cancel deadline is compensated (rolled back) if it has a rollback path. 3. **Must surface the gap.** Requests whose cancel is received but whose work committed are logged with both timestamps, so the gap is visible rather than reconciled silently. ## Decision Boundary - Cancel arrives before commit: effective. - Cancel arrives after commit, work is compensable, compensation executed: effective via compensation. - Cancel arrives after commit, work is not compensable: cancellation failed; side effect stands and must be surfaced. - Cancel deadline unknown: every cancel is a guess; the boundary between effective and post-commit is undefined. ## Operational Effect The invariant couples a request to stop with the reality that dispatched work may not stop. The instance's correct posture is to define the deadline, compensate after it, and surface the gap. **Trap.** Require workers to check cancel before every operation. That closes some races but not the one between "check cancel" and "commit side effect" — a check is not a lock, and the commit can interleave after the check. The correct fix is compensation after the deadline, not tighter checks. ```python from dataclasses import dataclass class CancelRaceError(RuntimeError): """Raised when cancellation can race with a side-effect commit and cannot compensate.""" @dataclass(frozen=True) class CancelGate: cancel_deadline_defined: bool cancel_arrives_before_commit: bool work_compensable: bool compensation_available: bool = True def verify(self) -> bool: if not self.cancel_deadline_defined: raise CancelRaceError( "cancel deadline undefined: effective-versus-post-commit boundary is undecidable" ) if self.cancel_arrives_before_commit: return True if self.work_compensable and self.compensation_available: return True raise CancelRaceError( "cancel arrives after commit and work has no compensation: side effect will persist" ) def dispatch_cancel_mode(gate: CancelGate) -> str: try: gate.verify() except CancelRaceError: return "surface-post-commit-side-effect" return "cancel-effective" ```