Files

AI Vault scanner late cancellation

The scanner child kept cancellation IDs after their requests had already settled. Its response can still be in transit when the parent sends a cancellation, so this does not require an invalid caller. The completed request has already run its cleanup; nothing remains to delete the newly inserted ID.

The fix admits cancellation only while the existing pending set owns the request. That set includes both queued and running requests. Their cancellation and cleanup remain unchanged. No protocol or history-retention policy changes.

Proof

Run from the repository root with dependencies installed:

ORCA_BACKGROUND_LAUNCH=1 node --max-old-space-size=128 docs/audits/scanner-late-cancel/reproduce.mjs

The script loads the checked-out production entry and derives the before version by removing only the three-line pending membership guard in memory. It checks that the guard occurs exactly once; no historical commit or Git access is needed. Esbuild strips TypeScript before both versions run in separate VM contexts. Only imported collaborators are stubbed: the production message handler, request lanes, sets, and cleanup run. After each synthetic first-prompt request completes, its matching cancel arrives.

The script asserts the counts and emits JSON with both source SHA-256 hashes and Node/platform/heap-limit provenance. results.json records a run on Node v26.6.0 with a 128 MiB old-space limit. This measures retained entries, not RSS.

Source Requests/responses Pending Controllers Retained cancel IDs
Before 1,000 / 1,000 0 0 1,000
After 1,000 / 1,000 0 0 0

The regression test imports the production entry and directly observes its existing cancellation set through an admitted cancellation. It repeats late cancels after both successful and failed requests, verifies queued/running cancellation, and checks shutdown cleanup. No production diagnostics or test-only exports were added.

ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ai-vault/session-scanner-service-cancellation.test.ts src/main/ai-vault/session-scanner-service-entry.test.ts src/main/ai-vault/session-scanner-service-client.test.ts

Incident scope

The same unconditional insertion exists in release v1.4.198. This retains numeric IDs in the scanner child, not transcript contents in Electron main. It is a concrete small leak; it does not explain the reported roughly 26 MB/s main-process growth in #19768 or establish the cause of #19831's scope-level peak memory measurements.