The one unmet ask from #7280, refiled on its own.
Done and closed with #7280: ask 1 (keep the stopgap) and ask 2 (fix the overclaiming comment).
Still missing: ask 3 — a dependency-scale RUNTIME witness in .github/workflows/gc-moving-witnesses.yml. Everything that gates this family today is either a synthetic fixture or a static check over emitted IR. Neither can observe the failure mode #7154's residual actually presents as.
Why a runtime witness specifically
The #7154 class is invisible to every static instrument and to every runtime GC probe at the moment of collection — there is nothing for the collector to find. It surfaces cycles later, in a different function, as TypeError: value is not a function. The evidence that scale matters is in #7280 itself: the loop-polls configuration was red 0/30, and stock zod alone failed 5/40 — i.e. real dependency-shaped code found what the fixtures did not.
scripts/gc_root_dominance_check.py reads LLVM IR, so a runtime-side cache holding a raw heap pointer is structurally invisible to it. That population now has its own inventory and gate (#7231 / #7695), but an inventory is not a witness.
What the witness must assert, or it is worth nothing
This is the trap that has cost this repo the most, so treat it as the acceptance criterion rather than a nicety:
The four ways this gate could ship unable to fail
Per CLAUDE.md, all four have bitten this repo: continue-on-error: true; absent from branch protection's required contexts; concurrency with unconditional cancel-in-progress (scope it to pull_request, let main runs queue); and the gate running while its subject does not. Also: a new gate has never been green, so promoting it to required immediately blocks every open PR — run it once on main, then promote. Leaving that second step undone is exactly how gc-stress ended up reporting failures without blocking anything.
The one unmet ask from #7280, refiled on its own.
Done and closed with #7280: ask 1 (keep the stopgap) and ask 2 (fix the overclaiming comment).
Still missing: ask 3 — a dependency-scale RUNTIME witness in
.github/workflows/gc-moving-witnesses.yml. Everything that gates this family today is either a synthetic fixture or a static check over emitted IR. Neither can observe the failure mode #7154's residual actually presents as.Why a runtime witness specifically
The #7154 class is invisible to every static instrument and to every runtime GC probe at the moment of collection — there is nothing for the collector to find. It surfaces cycles later, in a different function, as
TypeError: value is not a function. The evidence that scale matters is in #7280 itself: the loop-polls configuration was red 0/30, and stock zod alone failed 5/40 — i.e. real dependency-shaped code found what the fixtures did not.scripts/gc_root_dominance_check.pyreads LLVM IR, so a runtime-side cache holding a raw heap pointer is structurally invisible to it. That population now has its own inventory and gate (#7231 / #7695), but an inventory is not a witness.What the witness must assert, or it is worth nothing
This is the trap that has cost this repo the most, so treat it as the acceptance criterion rather than a nicety:
[gc-zeal] forced_collections=N copying_minors=M moved_objects=Kat exit and exits 70 if N or M is zero. Use it. A run with zero copying minors witnesses nothing, andPERRY_GC_PROTECT_FROMSPACE=1on such a run protects nothing while exiting clean — I hit exactly that today on gc: js_native_call_method reuses one copy of its rooted receiver across ~1200 lines of allocating probes #7528 and nearly recorded the wrong conclusion.PERRY_GC_MOVING_LOOP_POLLS=1(default off since fix(gc): disable evacuating minor by default pending #7154 (use-after-free on dynamically-added fields) #7161), or the loops emit no back-edge poll and zeal never fires inside them.PERRY_GC_PROTECT_FROMSPACE_DEPTH. The default of 4 is far too small for real code — GC: evacuating minor drops an old-to-young field[1] edge, crashing with 'value is not a function' #7154's own reproducer needed 800, because the value crossed 600 polls between its last valid observation and its stale use.The four ways this gate could ship unable to fail
Per CLAUDE.md, all four have bitten this repo:
continue-on-error: true; absent from branch protection's required contexts;concurrencywith unconditionalcancel-in-progress(scope it topull_request, letmainruns queue); and the gate running while its subject does not. Also: a new gate has never been green, so promoting it to required immediately blocks every open PR — run it once onmain, then promote. Leaving that second step undone is exactly howgc-stressended up reporting failures without blocking anything.