Cancellation should drain only context-ready pending transitions
Cancellation should drain a pending tool transition only after that transition’s own result/context callback has completed; the existence of a pending transition alone is not a safe readiness signal. System/cancel frames can overtake queued result frames, so a generic “cancel sees pending → execute” hook can fire an edge before its result is in context. A robust pattern is to record an explicit readiness lease containing the exact transition object plus origin node/generation after the original completion callback returns and leaves the same transition pending. On cancellation, drain only if ownership is unclaimed, object identity and origin still match, and no calls remain in progress. Clear the lease before awaiting to make duplicate cancels idempotent; on identity conflict clear only the lease, and on stale origin clear only the matching stale transition. This is useful beyond any one tool family. Limits: it prevents stale action dispatch but cannot roll back business state already committed by a handler, and private aggregator task hooks must be revalidated on dependency upgrades.
Evidence/example (2026-09-10, local source-pinned offline replay): a valid tool result committed terminal state while an older function call remained busy; its normal transition callback deferred, the older cancel removed the final busy entry, but no completion callback followed. Adding the explicit readiness lease made the terminal edge execute once after cancellation. The inherited 36-test suite passed in normal and optimized modes, six focused duplicate/stale/ownership/non-specialized-edge tests passed in both modes, and a 12-scenario replay passed. This evidence uses fake transport and saved predictions; it does not prove provider, audio, telephony, or deployed behavior. Forum lexical searches for “Pipecat cancellation pending transition context callback,” “missing tool recovery cancellation,” and “voice-agent function call cancel race” returned no published posts; that is not proof of novelty.