Parent: #476 Related: #480, #493, #884
Problem
The owned zig-gc collector has several mark-work publication paths that set a cell's marked bit before a fallible worklist append and then swallow allocation failure:
incrementalBarrierHeaderwinsclaimMark(h), then usesbarrier_buf.append(...) catch {}ormark_stack.append(...) catch {};publishCellLockedpublishes a born-marked cell, then usesborn_concurrent.append(...) catch {};- concurrent finish paths use fallible
appendSlice/appendoperations and clear the source worklist even when the destination append failed.
Once claimMark succeeds, a terminal root rescan cannot repair a lost work item: visiting the same cell observes it already marked and does not enqueue it for tracing. The cell itself survives, but white children reachable only through it may be swept. A newly born marked cell whose born_concurrent publication is lost has the same untraced-child gap.
This surfaced while auditing #884's rejection-root insertion barrier. The intrusive rejection FIFO remains a durable Context root, but its Promise payload can hold a wrapper, rejection reason, reactions, and async links that still require tracing. A swallowed barrier-work OOM is therefore not an acceptable publication proof.
Required implementation
- Make every transition from white/newly-born to trace-pending atomic with durable work ownership: either the trace item is infallibly published, the mark claim is safely rolled back with a concurrency proof, or the collection aborts before any sweep can occur.
- Make concurrent handoff-to-marker and finish-time worklist transfer preserve the source until destination publication succeeds.
- Treat worklist reservation failure as an explicit fail-closed collection outcome; never continue to sweep a graph whose marking closure is incomplete.
- Cover single-mutator concurrent marking, parallel mid-script marking, ordinary stop-the-world marking, nursery remembering, born-cell publication, deferred tracing, and terminal root rescans.
- Keep barrier fast paths bounded and allocation-free after cycle setup where a live-cell bound proves sufficient; do not add an unbounded emergency pool or a global mutator lock.
- Land the collector fix in the owned
zig-gcdependency and pin/integrate it in zig-js with exact regression evidence.
No-workaround rules
- No swallowed OOM, leaked marked-but-untraced state, unconditional process abort for recoverable bounded-heap pressure, disabling concurrent/moving GC, retaining all cells, skipping sweep without resetting cycle state, or relying on a later root scan after the mark bit already suppresses tracing.
- A mark bit alone is not durable ownership of the cell's outgoing graph.
Acceptance
- Deterministic failing-allocator witnesses hit each barrier, born-cell, worklist-transfer, and finish failure boundary and prove no reachable child is reclaimed or relocated stale.
- Collector state returns to an exact reusable idle/aborted state after every recoverable failure; a later full collection succeeds.
- Concurrent and parallel stress force root insertion, black-to-white stores, new-cell graphs, weak/ephemeron work, and queue handoff at capacity boundaries.
- Suppression-free TSan, zig-gc units, zig-js moving/concurrent GC units, mid-GC/threadfuzz profiles, full units, and leak checks pass.
- Exact Test262/PR-249/Wasm coverage has zero regressions; performance evidence shows no new per-store allocation or global contention.
Security boundary
Heap pressure and graph shape are script-controlled. A worklist OOM must fail closed without turning a reachable JavaScript object graph into marked shells with reclaimed children, stale pointers, or relocation corruption.