computeExpectedRefCounts totals the references the kernel holds by walking the store: the run queue, c-list entries, pinned objects, promise state. It does not credit ctx.crankBuffer.
A buffered send or notify holds references all the same — enqueueSend and enqueueNotify charge for the target, the result and every slot at the moment they are buffered, and the store rows those units belong to only appear when flushCrankBuffer moves the items onto the run queue. Between the charge and the flush the audit sees the counts but not the holder, and reports a leak.
That is why the audit has to run after the flush, which is the ordering KernelQueue.#processCrankResult and KernelQueue.audit-ordering.test.ts describe. It is a workaround, not the fix: the flush is also what settles the promise enqueueMessage gave an external caller, so the audit necessarily runs after answers have gone out, and a violation it finds can only kill the run loop rather than roll the offending delivery back.
Crediting ctx.crankBuffer the way the run queue is credited would remove that constraint and let the audit run while the crank can still be rolled back.
Raised in the 2026-09-11 review of #1021 / #1022 / #1023 as claim 2 of #1039, where the comment was narrowed to describe the conflict rather than resolve it.
computeExpectedRefCountstotals the references the kernel holds by walking the store: the run queue, c-list entries, pinned objects, promise state. It does not creditctx.crankBuffer.A buffered send or notify holds references all the same —
enqueueSendandenqueueNotifycharge for the target, the result and every slot at the moment they are buffered, and the store rows those units belong to only appear whenflushCrankBuffermoves the items onto the run queue. Between the charge and the flush the audit sees the counts but not the holder, and reports a leak.That is why the audit has to run after the flush, which is the ordering
KernelQueue.#processCrankResultandKernelQueue.audit-ordering.test.tsdescribe. It is a workaround, not the fix: the flush is also what settles the promiseenqueueMessagegave an external caller, so the audit necessarily runs after answers have gone out, and a violation it finds can only kill the run loop rather than roll the offending delivery back.Crediting
ctx.crankBufferthe way the run queue is credited would remove that constraint and let the audit run while the crank can still be rolled back.Raised in the 2026-09-11 review of #1021 / #1022 / #1023 as claim 2 of #1039, where the comment was narrowed to describe the conflict rather than resolve it.