From af127028a821b10380355a9f6836131fb39b7378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 29 Aug 2026 07:23:50 +0200 Subject: [PATCH 1/2] fix(gc): unblock the poll-reach audit and run it on every PR `--audit-poll-reach` exits 2 on `main`: #8965 added `js_string_concat_value_box` on 2026-08-28 without listing it in POLL_CAPABLE_RUNTIME. It is the exact analogue of `js_string_concat_box` directly above it -- an SSO fast arm, then `js_string_concat_value` as the fallback (string/concat.rs:496), and that callee is already listed -- so this is an omission at introduction, not a judgement call. That audit is an early step of `gc-root-dominance.yml`, ahead of the compiler build, so its failure masks the corpus and checker the workflow exists for (#8821). Note the issue's timeline needs one correction: the 2026-08-15 red was the THIRD wave, cleared by #8823 on 08-25; this is a fresh recurrence from 08-28, which is the pattern the list is designed to catch. Because the recurrence is the norm rather than the exception, also run the audit in `lint`. It is static, build-free and 0.6s including its self-test, while `gc-root-dominance.yml` is label-gated behind `run-extended-tests` and so is SKIPPED on every PR -- it can only report after the fact, on a scheduled `main` run. This is the same argument the runtime GC-pointer holder audit already makes in that file for its own placement: cheap and build-free, so it belongs in a required context. The expensive corpus arms stay where they are; #8821's remaining questions (label-gating the corpus, and #8809/#8810's findings) are untouched. --- .github/workflows/test.yml | 22 ++++++++++++++++++++++ scripts/gc_root_dominance_check.py | 11 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 28099130b4..34484383ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -407,6 +407,28 @@ jobs: python3 scripts/gc_runtime_root_holders.py --self-test python3 scripts/gc_runtime_root_holders.py + # #8821. The poll-reach audit is static, build-free and sub-second, but it + # lived ONLY in `gc-root-dominance.yml`, which is label-gated behind + # `run-extended-tests` — so it is skipped on every PR and speaks only on + # scheduled `main` runs, after the fact. It is also an early step there, + # ahead of the compiler build, so its failure masks the corpus and checker + # that workflow exists for: red from 2026-08-15, cleared by #8823, red + # again on 2026-08-28 when #8965 added `js_string_concat_value_box`. + # + # Same argument as the holder audit above — cheap and build-free, so it + # belongs in `lint`, which IS a required context. Running it here PREVENTS + # the omission at PR time rather than reporting it days later, and leaves + # the expensive corpus arms where they are. + # + # `--self-test` first, and it is not decoration: it PLANTS a fixture that + # makes the audit report, so a green run says the detector works rather + # than that nothing happened to be wrong today. + - name: Poll-capable reach audit (GC root dominance) + if: ${{ !cancelled() }} + run: | + python3 scripts/gc_root_dominance_check.py --self-test + python3 scripts/gc_root_dominance_check.py --audit-poll-reach + # #8530. Mutable-root scanner registries are thread-local, so guarding a # registration call with a process-global Once/OnceLock/AtomicBool (or a # mutex boolean) leaves every heap after the first without those roots. diff --git a/scripts/gc_root_dominance_check.py b/scripts/gc_root_dominance_check.py index 8c52b5c151..d3225a33a1 100755 --- a/scripts/gc_root_dominance_check.py +++ b/scripts/gc_root_dominance_check.py @@ -1360,6 +1360,17 @@ def is_collecting(callee): # reads the caller's options object and writes the result) "js_builtin_subclass_construct", "js_tls_create_secure_context", "js_tls_secure_context_new", + # Fourth wave. #8965 added `js_string_concat_value_box` on 2026-08-28 — + # AFTER #8823 cleared the third wave — and the audit went red again the + # same day, which is the recurrence this list is designed to catch rather + # than a new kind of problem. + # + # It is the exact analogue of `js_string_concat_box` above: an SSO fast arm + # that returns content-stable bits, then `js_string_concat_value(prefix, + # value)` as the fallback (string/concat.rs:496) — and that callee is + # already listed. So the entry is an omission at introduction, not a + # judgement call. + "js_string_concat_value_box", # `js_private_brand_add` is the referent-with-no-name that NEITHER audit can # ask for: `--audit-poll-reach` only walks symbols `ALLOC_RE` matches, and # `js_private_brand_add` matches no alloc/new/create convention, so the one From de5312ee6d41cfc00db7751f0cc77b9d4b613cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 29 Aug 2026 07:24:20 +0200 Subject: [PATCH 2/2] docs: add the changeset fragment for #9012 --- changelog.d/9012-poll-reach-audit-in-lint.md | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 changelog.d/9012-poll-reach-audit-in-lint.md diff --git a/changelog.d/9012-poll-reach-audit-in-lint.md b/changelog.d/9012-poll-reach-audit-in-lint.md new file mode 100644 index 0000000000..4ef85341ef --- /dev/null +++ b/changelog.d/9012-poll-reach-audit-in-lint.md @@ -0,0 +1,21 @@ +The GC poll-reach audit is unblocked and now runs on every PR. + +`--audit-poll-reach` was exiting 2 on `main`: #8965 added +`js_string_concat_value_box` without listing it in `POLL_CAPABLE_RUNTIME`. It is +the exact analogue of `js_string_concat_box` listed directly above it — an SSO +fast arm, then `js_string_concat_value` as the fallback (`string/concat.rs:496`), +and that callee was already listed — so it was an omission at introduction. + +That audit is an early step of `gc-root-dominance.yml`, ahead of the compiler +build, so its failure masks the corpus and checker the workflow exists for +(#8821). The issue dates the red to 2026-08-15; that was the third wave, cleared +by #8823 on 08-25, and this is a fresh recurrence from 08-28 — the second +occurrence of the pattern the list exists to catch. + +Because recurrence is the norm, the audit now also runs in `lint`. It is static, +build-free and 0.6 s including its self-test, whereas `gc-root-dominance.yml` is +label-gated behind `run-extended-tests` and therefore skipped on every PR, able +to report only after the fact on a scheduled `main` run. This is the placement +argument the runtime GC-pointer holder audit already makes in that file: cheap +and build-free, so it belongs in a required context. The expensive corpus arms +are unchanged, as are #8821's remaining questions and #8809/#8810's findings.