Related: #636, #637, #645, #655 Parent: #480
Problem
Context heap_limit_bytes never publishes a torn object under recovery pressure sets its reserve binding to null and invokes gc() inside the same native evaluation frame that created and touched the reserve graph. Full collection at that point intentionally scans the active native stack conservatively. Stale reserve pointers may therefore keep the graph live even though the JavaScript binding is null.
The result is platform/toolchain dependent: macOS normal and TSan reclaim enough to score complete post-OOM objects, while Linux TSan repeatedly retains the reserve and reports 500/500 post-recovery OOMs. That does not demonstrate a torn object or failed sweep; it disproves the test comment that the in-frame release guarantees reclamation.
Scope
- Build the live hoard and exact-shape reserve in one evaluation.
- Return across the host boundary, clear the reserve in a separate evaluation, return again, and collect from the quiescent host frame after stale creation-frame pointers are gone.
- Keep the hoard rooted, preserve the unchanged 4 MiB cap, require the initial pressure OOM, and score every successfully returned object for complete field/array/string publication.
- Retain exact full-collection and zero-leak assertions in normal and TSan runs.
No-workaround rules
- No larger heap cap, reduced hoard, ignored all-OOM result, disabled conservative scanning, forced pointer scrubbing, retry loop, platform skip, or sanitizer exclusion.
Acceptance
- The corrected witness deterministically observes pressure and at least one complete post-recovery object on macOS and Linux under normal and TSan builds.
- No torn object is ever published and the 4 MiB cap remains exact.
- Full units and the fresh Linux shard pass with zero leaks.