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
POLL_CAPABLE_RUNTIME audit cannot see macro-generated exports: js_string_replace_regex_fn reads as missing #10389
scripts/ GC root-dominance audit fails on main with:
error: POLL_CAPABLE_RUNTIME entries that name no runtime symbol:
=== POLL_CAPABLE_RUNTIME: 147 entries vs 4099 exported js_* symbols
The two entries are js_string_replace_regex_fn and js_string_replace_all_regex_fn.
The symbols are real; the scanner is blind
Both are exported. The perex-adoption commit 178a2bb367 turned them into macro-generated exports:
regex_value!(js_string_replace_regex_fn,false)
runtime_symbols()'s _EXTERN_C_FN_RE is a literal source-text regex, so it cannot see a symbol that a macro produces. The entries are correct, the audit's view of the tree is not.
This is the same class as a bug the auditor's own comments already record, in its words: "The name was never wrong; the scanner was."
Why this matters more than a red gate
The gate's advice when an entry names no symbol is to replace the entry, not delete it, and that advice is right. Deleting these two would remove a genuine poll-capable declaration for a function that really is poll-capable — converting a scanner blind spot into a silent hole in the GC root-dominance analysis. A future reader would have no way to tell the entry had ever existed.
So the fix belongs in the scanner: teach runtime_symbols() to expand the macro forms that now generate js_* exports, regex_value! first. Anything else that generates exports through a macro is in the same position and should be enumerated at the same time.
How this was established
Found while triaging PR #10354 (the turnloop migration), where the initial hypothesis was that a deleted crate had left stale entries. That hypothesis was wrong, and three independent checks disproved it:
Reconfirmed during measurement/gate audit of #10960 on 2026-09-22. This now blocks bothgc-root-dominance and gc-root-dominance-statepoints before either compiler build or IR corpus runs.
Evidence from the API (actual conclusion, not gh pr checks rendering):
Clean main a022cf2e41ec90a95a05fa0a6a298a69f79c4025, scheduled run 35689113373: both jobs are failure, at POLL_CAPABLE_RUNTIME entries must name real runtime symbols. Job log names all three below.
Running python3 scripts/gc_root_dominance_check.py --audit-poll-capable in the unchanged main checkout returns 2, with 147 entries vs 3876 exported js_* symbols and exactly:
The assertion is POLL_CAPABLE_RUNTIME <= runtime_symbols(), after a minimum population of 500 exports. runtime_symbols() scans literal source under crates/perry-runtime/src and crates/perry-stdlib/src using extern\s+"C(?:-unwind)?"\s+fn\s+(js_\w+). This is a source inventory assertion, not an observed root-dominance violation.
There are two mechanisms, requiring different fixes:
The regex pair are real exports generated by regex_value! at crates/perry-runtime/src/regex/perex_replace_compat.rs:89–90 (macro body at line 66). Perex adoption 178a2bb36 introduced this form. The literal-source scanner misses fn $name. The names remain declared in crates/perry-codegen/src/runtime_decls/strings_part2.rs:404–405 and called in crates/perry-runtime/src/regex/replace_fn.rs:641,663. Keep these poll-capable entries. Repair export enumeration generally, with macro-generated export coverage; do not whitelist two symbol strings or assume every macro!(js_...) invocation exports a function. Also audit the related runtime-body/call-graph scanner so recognizing a name does not conceal missing reach edges.
js_ratelimit_new_from_options really was removed: dcb8a760a8a5abd39cf4b8187ff60b966b1aa05b deleted crates/perry-stdlib/src/ratelimit.rs, its codegen declarations/dispatch and native binding. git grep js_ratelimit_new_from_options a022cf2e4 -- crates scripts finds only the audit entry. That entry is stale and may be removed with this provenance; it is not evidence that the regex pair should be removed.
Acceptance: restore both real exports to the inventory; remove only the proved deleted export; run alloc, poll-capable, and poll-reach audits with population floors; plant a nonexistent export and prove the audit exits 2 naming it, then remove it; sabotage macro-export recognition and prove a regression test fails. Finally run both full GC jobs to completion on the same SHA and inspect every step. Clearing this preflight alone must not be reported as green dominance corpora.
The cheap symbol-existence audit currently lives behind the extended-test label on PRs; move its repaired preflight into the always-on lint path (next to the existing poll-reach audit), retain the scheduled/full corpus runs, and verify a first green main run before promoting any additional required gate. This prevents the next unrelated labelled PR from inheriting a hidden broken inventory.
Task status: repair deferred to this issue; no audit entry was silenced. Existing main-health tracker: #9925.
Additional independent proof: a clean Linux release build of main a022cf2e4 completed successfully (compiler plus both static-wrapper packages). nm -g --defined-only target/release/libperry_runtime.a finds exactly both regex exports as defined T symbols. Runtime archive SHA-256: 19cc952e11efed942b0719aefdabe5849ab234e690d91378dc92ca68d6d913a9. This distinguishes a scanner blind spot from a declaration that exists in source but is absent from the built library.
Separate #10960 lint inspection found real failures beyond the known benchmark fingerprint: missing changeset and PASS1_MARKED's non_moving_snapshot source pin changed for gc/policy.rs in the runtime-holder audit (lint job). Those require their own resolution; they are not evidence for blaming this inherited GC preflight failure on #10960.
Activity
proggeramlug commentedon Sep 22, 2026
Reconfirmed during measurement/gate audit of #10960 on 2026-09-22. This now blocks both
gc-root-dominanceandgc-root-dominance-statepointsbefore either compiler build or IR corpus runs.Evidence from the API (actual
conclusion, notgh pr checksrendering):a022cf2e41ec90a95a05fa0a6a298a69f79c4025, scheduled run 35689113373: both jobs arefailure, atPOLL_CAPABLE_RUNTIME entries must name real runtime symbols. Job log names all three below.python3 scripts/gc_root_dominance_check.py --audit-poll-capablein the unchanged main checkout returns 2, with147 entries vs 3876 exported js_* symbolsand exactly:The assertion is
POLL_CAPABLE_RUNTIME <= runtime_symbols(), after a minimum population of 500 exports.runtime_symbols()scans literal source undercrates/perry-runtime/srcandcrates/perry-stdlib/srcusingextern\s+"C(?:-unwind)?"\s+fn\s+(js_\w+). This is a source inventory assertion, not an observed root-dominance violation.There are two mechanisms, requiring different fixes:
regex_value!atcrates/perry-runtime/src/regex/perex_replace_compat.rs:89–90(macro body at line 66). Perex adoption178a2bb36introduced this form. The literal-source scanner missesfn $name. The names remain declared incrates/perry-codegen/src/runtime_decls/strings_part2.rs:404–405and called incrates/perry-runtime/src/regex/replace_fn.rs:641,663. Keep these poll-capable entries. Repair export enumeration generally, with macro-generated export coverage; do not whitelist two symbol strings or assume everymacro!(js_...)invocation exports a function. Also audit the related runtime-body/call-graph scanner so recognizing a name does not conceal missing reach edges.js_ratelimit_new_from_optionsreally was removed:dcb8a760a8a5abd39cf4b8187ff60b966b1aa05bdeletedcrates/perry-stdlib/src/ratelimit.rs, its codegen declarations/dispatch and native binding.git grep js_ratelimit_new_from_options a022cf2e4 -- crates scriptsfinds only the audit entry. That entry is stale and may be removed with this provenance; it is not evidence that the regex pair should be removed.Acceptance: restore both real exports to the inventory; remove only the proved deleted export; run alloc, poll-capable, and poll-reach audits with population floors; plant a nonexistent export and prove the audit exits 2 naming it, then remove it; sabotage macro-export recognition and prove a regression test fails. Finally run both full GC jobs to completion on the same SHA and inspect every step. Clearing this preflight alone must not be reported as green dominance corpora.
The cheap symbol-existence audit currently lives behind the extended-test label on PRs; move its repaired preflight into the always-on lint path (next to the existing poll-reach audit), retain the scheduled/full corpus runs, and verify a first green main run before promoting any additional required gate. This prevents the next unrelated labelled PR from inheriting a hidden broken inventory.
Task status: repair deferred to this issue; no audit entry was silenced. Existing main-health tracker: #9925.
Additional independent proof: a clean Linux release build of main
a022cf2e4completed successfully (compiler plus both static-wrapper packages).nm -g --defined-only target/release/libperry_runtime.afinds exactly both regex exports as definedTsymbols. Runtime archive SHA-256:19cc952e11efed942b0719aefdabe5849ab234e690d91378dc92ca68d6d913a9. This distinguishes a scanner blind spot from a declaration that exists in source but is absent from the built library.Separate #10960 lint inspection found real failures beyond the known benchmark fingerprint: missing changeset and
PASS1_MARKED'snon_moving_snapshotsource pin changed forgc/policy.rsin the runtime-holder audit (lint job). Those require their own resolution; they are not evidence for blaming this inherited GC preflight failure on #10960.