Follow-up to #66, #465, and #468. The post-WorkTask audit found a distinct lifetime family in Blob read/write operations: a task can leave the shared work pool, register with the process-wide io.Loop poller, and later re-enter the work pool using the same intrusive task node.
The existing per-VM native work-pool barrier is correct for one-shot jobs that publish their owner-thread completion before leaving the barrier. Applying it directly to Blob I/O would be unsafe in both obvious forms:
- Holding admission while a request is parked on a blocked FIFO/stdin/socket can make Worker teardown wait forever.
- Releasing admission while the poller retains the request allows a later readiness callback to touch a destroyed VM.
The I/O request state machine and FileCloser callback swap provide useful primitives, but they do not by themselves define ownership across poll registration, readiness dispatch, work-pool re-entry, cancellation, and VM teardown.
Implement a per-VM shutdown protocol for poll-parkable Blob I/O:
- register ownership before the first work-pool dispatch and close new admission during teardown;
- synchronize poll registration/readiness/cancellation so exactly one path owns final cleanup;
- cancel or detach parked requests without waiting indefinitely for external readiness;
- close owned descriptors and release Blob/JS roots on the correct thread;
- prevent work-pool re-entry and user callbacks after the VM stops;
- preserve normal completion, error, partial-I/O, and retry behavior;
- audit all Blob-backed read/write entry points that use this task family;
- add bounded repeated-worker regressions with deliberately blocked reads/writes, exact admission/cancellation counters, natural cleanup, and no relaxed timeouts or memory thresholds.
This is a source-audited ownership gap, not a claimed reproduced use-after-free and not evidence of whole-suite parity. One-shot DNS and native node:zlib work use the existing barrier/cancellation protocol; generic queued task disposal remains tracked in #465.