Additional inspector-held lifetime acceptance is repaired on main: canonical console buffers retain the real timer object and realm before attachment, replay it through actual remote handles, then allow collection after console entries and their group are cleared. Dormant injected-script roots retire only after checking actual native bound handles, pending calls and debugger state. A retained unrelated handle and pending async call survive clearing. Node and Home lifecycle assertions pass unchanged after the earlier Node oracle mistakes were corrected from observed reference behavior; the original failures remain archived. Verified merged evidence de6aea239. Complete module/export/callback and all owner/test-realm shutdown acceptance remain open. Evaluated inspector realms outside explicit console clearing remain #888/#428.
The native cached-Structure owner is identified and repaired in source 7cfff1c01, with verified merged-main evidence 4852a2330. JSC sets the supplied Structure's realm during global initialization; reusing the parent-cached Structure retained the last VM global. Each instance now receives its own Structure. The pinned old executable retains the last of two contexts; Node and repaired Home collect both. The unchanged original lifetime assertion passes, and the real measured graph has zero leftover contexts. Five unchanged earlier controls collect all array/fill/map/Math contexts in either ordering, preserve callable closures/compiled functions, and preserve reusable Scripts. Announced unused realms emit owner-loop destruction. Normal and merged builds, 33 native groups and required ABI/report gates pass.
This issue remains open for complete retained module/export/callback, native owner-thread shutdown and test-realm lifetime acceptance. Inspector-held remote objects/controller roots remain #888. Earlier failed assertions and root investigations remain immutable below.
Earlier investigation
Latest investigation on main: ordering controls retain the last evaluated context, while earlier contexts collect. Reversing an array/fill/map/Math.max sequence changes which context remains live. Temporary native probes report Output realm roots. Refined probes show the observed JIT-worklist code roots belong to host globals, so they do not establish the retained sandbox owner. Direct Script.runInContext has the same last-context ordering behavior; the actual retaining owner is still unproved. The earlier blanket evaluated-context and fill-specific interpretations are superseded. Original failed lifetime assertions remain unchanged; production sources are restored.
Investigation and verification, 58 immutable artifacts.
Original reproduction:
Evaluated VM contexts remain reachable after their application references are dropped. In an ordinary control, an unused context's WeakRef clears while a context that evaluated globalThis.payload = new Array(8000).fill(9); undefined remains live across four event-loop turns/full collections. The same comparison runs without calling measureMemory, so measurement result construction is not needed to reproduce the retention.
The installed Bun 1.4.2 reference also retains the evaluated context in the control that never measures memory.
The new native memory graph correctly reports the remaining live context; suppressing that row would hide the lifetime gap. Node's equivalent memory/lifetime control retires the context. Retain the failing lifetime assertion separately from passing allocation/shape tests.
Acceptance:
- Identify and retire the actual strong owner of an evaluated context when no reachable user object requires it.
- Preserve retained Script, compiled function, module, callback and exported-value lifetimes.
- Verify ordinary dropped-context collection and memory graph membership without filtering live contexts or weakening assertions.
- Cover native owner-thread shutdown and test-realm retirement alongside ordinary VM conformance.
Parents: #66 and #870. Related inspector lifecycle: #428. This remains incomplete VM lifetime work, distinct from native memory attribution.