You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
gc-root-dominance: 7/3 gated (8/4 unfiltered) NEW root-dominance regressions landed 2026-08-15..25 while the gate was red #8809
scripts/gc_root_dominance_check.py reports 7 violations across 4 distinct fingerprints on clean main (db821b5fd), against an allowlist whose entries list is empty. CLAUDE.md states every new hit is a red build, so this is either a real regression or the gate is currently red on main.
Measured with a perry-dev binary; gc-root-dominance.yml uses release, and it passes flags this invocation did not (--moving-only, --min-binds, --min-funcs, plus its env knobs). So the absolute count may not match CI's. Worth confirming at release profile before treating the count as authoritative — but the violations are real IR at the profile measured.
Found while auditing #8783; an identical A/B showed that PR introduces none of these.
The 3 gated fingerprints are all js_closure_alloc -> js_object_set_field_by_name: object_create_method_this::main (×5), array_iterator_manual_next::main (×1), gc_method_receiver_rooting::…__make (×1). The 4th, class_advanced::main::js_object_alloc_class_inline_keys_stamped -> js_private_brand_add, prints MOVING : no and is therefore dropped by --moving-only — but it is a genuine bug, not a false positive.
Correct wording is 7/3 gated, 8/4 unfiltered. Measured at release profile with the workflow's exact flags, so the earlier "dev-profile only, confirm at release" caveat is discharged: the violations are real at the profile that matters.
2. These are NEW regressions, not pre-existing debt
I said these were pre-existing on main. They are not. The last green gc-root-dominance run on main is 31870805900 (2026-08-15T06:59Z) with 144/144 sources compiled and === violations: 0. The gate has been red at --audit-poll-reach since 2026-08-15T13:00Z (see #8821, which I have also corrected), so both regressions landed inside a 10-day window where the gate could not report.
Both are #7192-shaped — root store emitted after a call that can allocate:
perry-codegen/src/expr/object_literal.rs::lower_by_name_props — lowers a this-capturing method closure, installs it via js_object_set_field_by_name, and only then pushes its root. The object handle is re-read from its root; the closure is not. The installer can run a user setter or Proxy trap, and the closure is reachable from the object by then, so an evacuating minor moves it — the push publishes a moved-from address. Arm became reachable from TypeScript via perf(hir): lower static method literals directly #8793 (landed in perf: static method literals, captured closure reuse, argument shape facts #8796, 2026-08-25).
perry-codegen/src/lower_call/new.rs::construction_runs_user_code — returns false for a class whose only private elements are methods/accessors, while emit_field_inits still emits js_private_brand_add for it (merge: land #8630 (class semantics tail) with six audit fixes #8643, 2026-08-23). That helper calls js_object_set_field_by_name, and its own body notes the marker-key allocation "can evacuate both the receiver and any live value".
Note the first of these landed via a PR I merged earlier today. The gate that would have caught it was red, and is label-gated so it would have been skipped on the PR regardless.
changed the title [-]gc-root-dominance: 7 violations / 4 fingerprints on clean main against an empty allowlist[/-][+]gc-root-dominance: 7/3 gated (8/4 unfiltered) NEW root-dominance regressions landed 2026-08-15..25 while the gate was red[/+]on Aug 25, 2026
Activity
proggeramlug commentedon Aug 25, 2026
Correcting the counts in this issue, and re-dating the finding.
Two corrections to my original report.
1. "7 violations across 4 fingerprints" is a pairing that never occurs
The count depends on which filter you run, and I mixed them:
--moving-only(whatgc-root-dominance.ymlactually gates on)The 3 gated fingerprints are all
js_closure_alloc -> js_object_set_field_by_name:object_create_method_this::main(×5),array_iterator_manual_next::main(×1),gc_method_receiver_rooting::…__make(×1). The 4th,class_advanced::main::js_object_alloc_class_inline_keys_stamped -> js_private_brand_add, printsMOVING : noand is therefore dropped by--moving-only— but it is a genuine bug, not a false positive.Correct wording is 7/3 gated, 8/4 unfiltered. Measured at release profile with the workflow's exact flags, so the earlier "dev-profile only, confirm at release" caveat is discharged: the violations are real at the profile that matters.
2. These are NEW regressions, not pre-existing debt
I said these were pre-existing on
main. They are not. The last greengc-root-dominancerun onmainis 31870805900 (2026-08-15T06:59Z) with144/144 sources compiledand=== violations: 0. The gate has been red at--audit-poll-reachsince 2026-08-15T13:00Z (see #8821, which I have also corrected), so both regressions landed inside a 10-day window where the gate could not report.Both are #7192-shaped — root store emitted after a call that can allocate:
perry-codegen/src/expr/object_literal.rs::lower_by_name_props— lowers athis-capturing method closure, installs it viajs_object_set_field_by_name, and only then pushes its root. The object handle is re-read from its root; the closure is not. The installer can run a user setter or Proxy trap, and the closure is reachable from the object by then, so an evacuating minor moves it — the push publishes a moved-from address. Arm became reachable from TypeScript via perf(hir): lower static method literals directly #8793 (landed in perf: static method literals, captured closure reuse, argument shape facts #8796, 2026-08-25).perry-codegen/src/lower_call/new.rs::construction_runs_user_code— returnsfalsefor a class whose only private elements are methods/accessors, whileemit_field_initsstill emitsjs_private_brand_addfor it (merge: land #8630 (class semantics tail) with six audit fixes #8643, 2026-08-23). That helper callsjs_object_set_field_by_name, and its own body notes the marker-key allocation "can evacuate both the receiver and any live value".Note the first of these landed via a PR I merged earlier today. The gate that would have caught it was red, and is label-gated so it would have been
skippedon the PR regardless.[-]gc-root-dominance: 7 violations / 4 fingerprints on clean main against an empty allowlist[/-][+]gc-root-dominance: 7/3 gated (8/4 unfiltered) NEW root-dominance regressions landed 2026-08-15..25 while the gate was red[/+]fix(gc): two root-dominance regressions + restore the corpus's missin…
fix(gc): unblock the poll-reach audit and run it on every PR (#9012)