Skip to content

ci: repair three broken gates — Windows cp1252, llvm-inprocess vacuous green, gc-native-roots (#7977, #7971, #7970) - #7986

Merged
proggeramlug merged 4 commits into
mainfrom
ci/gate-repair-7977-7971-7970
Aug 12, 2026
Merged

ci: repair three broken gates — Windows cp1252, llvm-inprocess vacuous green, gc-native-roots (#7977, #7971, #7970)#7986
proggeramlug merged 4 commits into
mainfrom
ci/gate-repair-7977-7971-7970

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Repairs three broken CI gates. They are independent; the commits are separate and
can be read in any order.

No arm was disabled or weakened. Two arms are red because they found real
defects; those are filed (#7984, #7985) and the arms stay red. A third defect
(#7982) was found by making a gate report what it was doing.

#7977windows-build was red before it ran anything

scripts/check_thread_locals.py walked crates/perry-runtime/src with a bare
Path.read_text(), which decodes with the host locale — cp1252 on a Windows
runner. 15 runtime sources carry a byte cp1252 cannot map; i18n.rs is reached
first at offset 31552 with 923 newlines before it, so under core.autocrlf the
failure lands at position 32475, matching the traceback to the byte.

That is the first step of windows-build, so the seven steps behind it were
skipped on every PR. Unprotected: the only Windows build of the runtime,
stdlib and both Windows UI crates; the only Windows run of the perry-runtime
unit tests; the Windows parity smoke; the VERSIONINFO check; the COFF-trimming
test.

#7882 fixed the path-separator half of this class in this same file, and the
encoding in gc_runtime_root_holders.py. These four readers were missed.

  • explicit encoding="utf-8" at every call site (plus newline="" on write, so
    --update is byte-stable across hosts). The verified files map is unchanged.
  • one latent instance in gc_runtime_root_holders.py, one in tests/test_gc_ratchet.py.
  • new gate scripts/check_locale_independent_io.py: AST-scans exactly the six
    Python files the Windows audit step runs. Static rather than
    PYTHONWARNDEFAULTENCODING because that only fires on executed calls, and
    because test_gc_ratchet.py embeds open(...) inside probe source literals
    that an AST correctly ignores and a grep would not. Runs in lint — Linux,
    per-PR, already required — so the class is caught before it reaches Windows.
  • PYTHONUTF8=1 on the Windows step as belt-and-braces.

Validation: main's version exits 1 with UnicodeDecodeError under a non-UTF-8
locale, the fixed version exits 0, and the full eight-command Windows audit
sequence passes under that locale. Sabotage-tested: replanting the exact
defect makes the new gate exit 1 naming the call site, so a green run means the
detector works rather than that nothing was tried.

#7971llvm-inprocess reported green while skipping its only real job

Three independent problems:

  1. Vacuous green. changes=success, native-backend=skipped concluded
    success (runs 31505530279 / 31499833415 / 31476724152). A path filter is a
    cost control, not a verdict. New llvm-inprocess-complete fan-in prints
    EXERCISED / NOT EXERCISED and fails when native-backend failed, was
    cancelled, or was skipped on a non-PR event — every non-PR event sets
    relevant=true unconditionally, so a skip there means the post-merge anchor
    stopped anchoring (the All macos-14 GC gates have been starved of runners for 2+ days: gc-ratchet and tls-budget have 0 successful main runs, 27 queued #7856 shape).
  2. No diagnostic. Bare grep -q / cmp under set -euo pipefail with each
    compile's stderr redirected to a file nothing printed, so three main
    failures ended at Generating code... + exit 1. Rebuilt around
    run/assert_grep/assert_same: names the failing command with its real
    exit status, dumps captured output, diffs on parity failure. (Status is
    captured after the command — inside an if ! cmd branch $? is the negated
    status and always 0.)
  3. Frozen corpora, invisibly. New currency diagnostic step.

The defect this arm was correctly reporting → #7982. Reproduced locally:
native IR construction failed … in line: %r5 = alloca ptr addrspace(1).
dialect/mod.rs::basic_type has no ptr addrspace(N) arm. Not a one-liner — I
added one, rebuilt, and the reader then fails on store ptr addrspace(1). Probe
reverted; filed rather than folded into a CI change.

The unit gate never caught it because all three tracked .ll corpora were frozen
on 2026-08-03, 151 codegen commits ago, and contain zero addrspace(1).
corpus_spike ... ok proves the tests ran, not that they test today's IR —
hazard 4 inside the liveness assert written to prevent hazard 4.

#7970gc-native-roots had never been green on any branch

Three failing arms, three unrelated causes.

macos-14 — gate defect, FIXED. The step asserted evacuation liveness without
PERRY_GC_DIAG=1, and [gc-copy-minor] — the assert's only input — prints only
under that flag. Reproduced exactly on macOS aarch64:

run [gc-copy-minor] assert
workflow env as-was 0 (98 bytes of #gcmetric) evacuated NOTHING — exit 1
+ PERRY_GC_DIAG=1 150 evacuation live — 75 copying minors, 16277 objects copied — exit 0

The whole step then passes locally. #7970 wondered if this was #7965-shaped
("the collector stopped running copying minors"). It is not
— the collector was
evacuating throughout; the gate was asserting on telemetry it had not enabled.
gc_evacuation_liveness_assert.py now separates instrument off from subject
dead
and gained a 5-direction --self-test, wired into lint.

ubuntu-24.04-arm — REAL DEFECT → #7984. Not a SIGSEGV; a Rust assertion.
Under PERRY_STACKMAP_WALKER=verify the fast fp-chain walker and the unwinder
resolve the same root to addresses 96 bytes apart. The fast walker is what
runs when verify is off. This arm must stay red until #7984 is fixed.

windows-latest — REAL DEFECT → #7985, plus one workflow bug fixed here.
link.exe 1120: /MT-vs-/MD CRT mismatch, LNK2005 on malloc/free from the
rpmalloc bundled in the LLVM release, LNK2019 for target backends inkwell
references. Filed — it needs a Windows-toolchain decision. Likely also latent in
windows-build, whose build step has not executed since #7977; expect it to
surface when this PR lands.
The arm's second failure was a workflow bug and is
fixed: Git-bash GNU tar read $RUNNER_TEMP (D:\a\_temp) as a remote
host:path (Cannot connect to D: resolve failed) — now tar --force-local
with a post-extraction check.

A STATUS block at the top of the workflow records which arm is which.

Promotion

Nothing was promoted to a required context.

Notes

Working notes, including every measurement above: gc-handoff/CIFIX-NOTES.md.

Per repo convention this PR carries no version bump and no CHANGELOG.md edit.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Windows, macOS, and LLVM continuous-integration checks with more reliable diagnostics and failure reporting.
    • Fixed platform-dependent text encoding issues to ensure consistent validation across environments.
    • Improved garbage-collection liveness checks and native-root test reliability.
  • Tests

    • Added broader coverage for LLVM execution modes, output consistency, exception handling, and skipped or incomplete test runs.
    • Added validation for locale-independent file handling and garbage-collection diagnostics.
  • Documentation

    • Documented CI repairs, workflow status, known limitations, and related backend/toolchain issues.

Ralph Küpper added 3 commits August 12, 2026 19:30
`scripts/check_thread_locals.py` walked `crates/perry-runtime/src` with a bare
`Path.read_text()`, which decodes with `locale.getencoding()` — cp1252 on a
GitHub Windows runner. Fifteen runtime sources carry a byte cp1252 cannot map
(0x81/0x8d/0x8f/0x90/0x9d); `i18n.rs` is reached first, at offset 31552 with 923
newlines before it, so `core.autocrlf` puts the failure at position 32475 —
matching the reported traceback to the byte.

That is the FIRST step of `windows-build`, so the seven steps behind it were
`skipped` on every PR: no Windows build of the runtime, stdlib or either Windows
UI crate, no Windows run of the `perry-runtime` unit tests, no Windows parity
smoke, no VERSIONINFO check, no COFF-trimming test.

#7882 fixed the path-separator half of this class in this same file and the
encoding in `gc_runtime_root_holders.py`; these four readers were missed.

- route all reads/writes through `read_source()` / `write_source()` helpers that
  pass `encoding="utf-8"` (and `newline=""`, so `--update` is byte-stable across
  hosts). The verified `files` map is unchanged.
- fix a latent instance in `gc_runtime_root_holders.py`'s fixture writer and a
  bare `read_text()` in `tests/test_gc_ratchet.py`.
- add `scripts/check_locale_independent_io.py`: an AST scan of exactly the six
  Python files the Windows audit step runs, failing on locale-defaulted text
  I/O. Static rather than `PYTHONWARNDEFAULTENCODING` because that only fires on
  executed calls, and because `test_gc_ratchet.py` embeds `open(...)` inside
  probe source *literals* that an AST correctly ignores and a grep would not.
- run it in `lint` (Linux, per-PR, already required) so the class is caught
  before it reaches Windows, and set `PYTHONUTF8=1` on the Windows step as
  belt-and-braces.

Validated: main's version exits 1 with `UnicodeDecodeError` under a non-UTF-8
locale and the fixed version exits 0; the full eight-command Windows audit
sequence passes under that locale; and replanting the exact defect makes the new
gate exit 1 naming the call site, so a green run means the detector works.
… a skip (#7971)

Three problems, all of which let the gate report success while asserting nothing.

1. VACUOUS GREEN. On a PR that touched no IR-affecting path the workflow ran
   `changes=success, native-backend=skipped` and concluded `success`. Sampled
   runs 31505530279, 31499833415 and 31476724152 are all that shape. "Green"
   meant "not relevant", but read as "the in-process backend passed".

   Add a `llvm-inprocess-complete` fan-in that states the verdict in the log and
   the step summary — EXERCISED vs NOT EXERCISED — and that can fail in two
   directions: `native-backend` failing or being cancelled, and `native-backend`
   being SKIPPED on a non-PR event. Every non-PR event sets `relevant=true`
   unconditionally, so a skip there means the post-merge anchor has stopped
   anchoring, which is how #7856 starved this gate for eight days unnoticed.

2. NO DIAGNOSTIC. `Native-mode smoke` ran bare `grep -q` / `cmp` under
   `set -euo pipefail`, with each compiler's stderr redirected to a file nothing
   ever printed. The three 2026-08-11 `main` failures therefore ended at
   "Generating code..." with a naked `exit 1`.

   Rewrite it around `run` / `assert_grep` / `assert_same` helpers: the failing
   command is named with its real exit status, captured stdout and stderr are
   dumped, a parity failure prints the diff, and every liveness assert says what
   it is protecting. (The status is captured after the command rather than inside
   an `if !` branch, where `$?` is the negated status and always 0.)

3. STALE CORPORA, INVISIBLE. The unit gate asserts `corpus_spike ... ok` to prove
   the corpus tests ran, but not that the corpora are current. All three were
   frozen on 2026-08-03, 151 codegen commits ago, and contain zero
   `addrspace(1)` — so they stayed green while the end-to-end arm could not
   build a single RS4GC root slot. Print corpus age and the IR-affecting commit
   count so that gap is visible instead of rediscovered.

The underlying backend defect this arm was correctly reporting is filed as #7982:
`PERRY_LLVM_INPROCESS=native` cannot construct `ptr addrspace(1)` roots. It is
reproduced locally and is not a one-line fix, so it is left to that issue rather
than folded into a CI change.
…cts (#7970)

`gc-native-roots` had never had a green run on any branch. Three of its four arms
failed for three unrelated reasons; they are not one bug.

macos-14 — GATE DEFECT, fixed. The in-process step asserted evacuation liveness
without setting `PERRY_GC_DIAG=1`, and `[gc-copy-minor]` — the only input
`gc_evacuation_liveness_assert.py` reads — is printed only under that flag. The
assert saw an empty trace and reported "the forced-evacuation arm evacuated
NOTHING (0 copying minors, 0 objects copied)" on every run since the arm was
written, so it could never pass. Measured on macOS aarch64: without the flag,
stderr is 98 bytes of `#gcmetric` and 0 copying minors; with it, the same binary
under the same GC env reports 75 copying minors and 16277 objects copied, and the
whole step passes (gcmap section present, stackmaps absent, control-vs-forced
stdout identical). #7970 wondered whether this was #7965-shaped. It is not — the
collector was evacuating the whole time. `--probe` was missing too, which is why
the logs say the literal `<probe>`.

To stop that misdiagnosis recurring, `gc_evacuation_liveness_assert.py` now
distinguishes "the instrument was off" from "the subject was dead": a trace
containing no `[gc-...]` marker at all is reported as `PERRY_GC_DIAG=1` not being
set, with "fix the RUN, not the collector", instead of being blamed on the GC. It
gains a `--self-test` covering five directions, run in `lint`.

windows-latest — the second of its two failures was a workflow bug: Git-bash GNU
tar reads `$RUNNER_TEMP` (`D:\a\_temp`) as a remote `host:path` and tries to
connect to a host named `D`. Fixed with `--force-local` and a post-extraction
check so a truncated download reports itself rather than surfacing later as "no
matched opt+clang pair".

The two remaining failures are REAL DEFECTS and are filed, not silenced:

  * #7984 — aarch64-linux: under `PERRY_STACKMAP_WALKER=verify` the fast fp-chain
    walker and the unwinder resolve the same root to addresses 96 bytes apart.
    The fast walker is what runs when verify is off, so this is a potential live
    rooting bug on that target. The arm is red because it found the bug it exists
    to find, and must stay red until fixed.
  * #7985 — windows: `perry.exe` cannot link against the official LLVM 22 release
    (/MT-vs-/MD CRT mismatch, bundled rpmalloc redefining malloc, and inkwell
    referencing target backends the release does not build). Likely also latent
    in `windows-build`, whose build step has not run since #7977.

A STATUS block at the top of the workflow records which arm is which, and states
that this gate must NOT be promoted to a required context while #7984/#7985 are
open — two legitimately-red arms would block every PR.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 613ce71d-6158-44f1-b3a9-f5c1ad1b2070

📥 Commits

Reviewing files that changed from the base of the PR and between b847afd and c3bf87f.

📒 Files selected for processing (10)
  • .github/workflows/gc-native-roots.yml
  • .github/workflows/llvm-inprocess.yml
  • .github/workflows/test.yml
  • changelog.d/7986-ci-gate-repair.md
  • gc-handoff/CIFIX-NOTES.md
  • scripts/check_locale_independent_io.py
  • scripts/check_thread_locals.py
  • scripts/gc_evacuation_liveness_assert.py
  • scripts/gc_runtime_root_holders.py
  • tests/test_gc_ratchet.py

📝 Walkthrough

Walkthrough

The pull request repairs Windows encoding checks, strengthens GC native-root assertions, expands LLVM in-process validation, adds fan-in status handling, and documents CI defects and gate status.

Changes

CI gate repair

Layer / File(s) Summary
Locale-independent encoding audit
scripts/check_locale_independent_io.py, scripts/check_thread_locals.py, scripts/gc_runtime_root_holders.py, tests/test_gc_ratchet.py, .github/workflows/test.yml, changelog.d/..., gc-handoff/...
Windows CI source reads and writes now use explicit UTF-8 handling. An AST checker and self-tests validate locale-independent text I/O.
GC liveness and native-root diagnostics
scripts/gc_evacuation_liveness_assert.py, .github/workflows/gc-native-roots.yml, .github/workflows/test.yml, changelog.d/..., gc-handoff/...
The liveness checker reports distinct diagnostic and trace conditions. Native-root workflow arms enable GC telemetry, verify Windows extraction, and report arm status.
LLVM in-process validation and fan-in
.github/workflows/llvm-inprocess.yml, changelog.d/..., gc-handoff/...
LLVM smoke tests now cover multiple execution modes with command diagnostics, liveness checks, and parity checks. An always-running fan-in job evaluates relevance and backend results.
Gate status and filed defect records
changelog.d/7986-ci-gate-repair.md, gc-handoff/CIFIX-NOTES.md
The changelog and handoff notes record repairs, filed backend and toolchain defects, workflow status, and non-promotion decisions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • PerryTS/perry issue 7977 — The UTF-8 changes and locale-independent audit address the Windows encoding failure described by this issue.

Possibly related PRs

  • PerryTS/perry#7304 — Introduced the LLVM in-process workflow that this pull request expands with diagnostics and fan-in validation.
  • PerryTS/perry#7322 — Modified the native-roots workflow that this pull request updates with arm diagnostics and assertions.
  • PerryTS/perry#7414 — Introduced related macOS evacuation liveness logic used by the updated workflow and checker.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/gate-repair-7977-7971-7970

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug
proggeramlug marked this pull request as ready for review August 12, 2026 17:52
@proggeramlug
proggeramlug merged commit c81b26d into main Aug 12, 2026
0 of 19 checks passed
@proggeramlug
proggeramlug deleted the ci/gate-repair-7977-7971-7970 branch August 12, 2026 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant