Summary
python3 scripts/gc_runtime_root_holders.py exits 1 on main at 35c36f425 ("style: cargo fmt", 2026-09-06). It is a lint step, so it is part of the required pr-gate and every PR opened against this main inherits the failure regardless of its own content.
Reproduction
git checkout 35c36f425
python3 scripts/gc_runtime_root_holders.py; echo RC=$?
gc_runtime_root_holders: the inventory itself is malformed. Each of
these entries silences a holder without recording a usable reason.
crates/perry-runtime/src/gc/census.rs:PASS1_MARKED: non_moving_snapshot source changed: crates/perry-runtime/src/gc/policy.rs; re-audit the window before updating its pin
RC=1
Cause
PASS1_MARKED's non_moving_snapshot window pins five source files by SHA-256 (scripts/gc_snapshot_contracts.py::source_digest, i.e. the sha256 of the file's UTF-8 text). Four still match; the policy.rs pin is stale:
| pinned file |
pin (prefix) |
actual on 35c36f425 |
crates/perry-runtime/src/gc/census.rs |
388414f9629f |
ok |
crates/perry-runtime/src/gc/cycle.rs |
2e2f5adca222 |
ok |
crates/perry-runtime/src/gc/mod.rs |
7dd42b9506a9 |
ok |
crates/perry-runtime/src/gc/policy.rs |
2c49102ee846 |
e589c928ce2d |
crates/perry-runtime/src/gc/progress.rs |
a5ad3971bbe4 |
ok |
The last two commits on main that touch policy.rs are 7284a17c8 (perf(gc): route the trigger path and dirty-page barrier through hot TLS, #9827) and 35c36f425 (style: cargo fmt). They landed through the 2026-09-06 train without the window being re-audited and re-pinned; the gate is doing exactly what it exists to do (an entry whose pinned source changed must be re-reviewed, not silently accepted).
Fix
Re-audit the census_pass1_if_armed → census_take_if_armed_at_full_sweep_start window against the two policy.rs changes (neither should alter mark/sweep control flow between the window's start and end: #9827 is TLS routing, the fmt commit is whitespace), append the re-audit note to the entry's why (append-only, per the inventory's own rule) and replace the policy.rs pin with the current digest. One-line JSON change plus the note; nothing in Rust.
Related hazard for #9838's landing (same file)
main no longer has the SHAPE_CACHE_YOUNG holder (removed with #9756's "drop the shape-cache young log"), and its inventory entry is gone with it. #9838's branch is based on d36a1af0c, whose inventory still carries that entry. A merge resolution that takes the union of both sides' holder entries re-adds SHAPE_CACHE_YOUNG, and the gate then fails on the merged tree with a different message (a stale entry that matches nothing). Observed on a measurement-only merge of #9838 onto 35c36f425 (perry-b4, 2026-09-06). Whoever lands #9838 should start from main's inventory, add only its own GC_TINY_PARSE_PRESSURE_BASE_BYTES verdict, and re-pin policy.rs from the merged file — not merge the two JSONs.
https://claude.ai/code/session_014knX724SYDogwzsXybCGxp
Summary
python3 scripts/gc_runtime_root_holders.pyexits 1 onmainat35c36f425("style: cargo fmt", 2026-09-06). It is alintstep, so it is part of the requiredpr-gateand every PR opened against thismaininherits the failure regardless of its own content.Reproduction
Cause
PASS1_MARKED'snon_moving_snapshotwindow pins five source files by SHA-256 (scripts/gc_snapshot_contracts.py::source_digest, i.e. the sha256 of the file's UTF-8 text). Four still match; thepolicy.rspin is stale:35c36f425crates/perry-runtime/src/gc/census.rs388414f9629fcrates/perry-runtime/src/gc/cycle.rs2e2f5adca222crates/perry-runtime/src/gc/mod.rs7dd42b9506a9crates/perry-runtime/src/gc/policy.rs2c49102ee846e589c928ce2dcrates/perry-runtime/src/gc/progress.rsa5ad3971bbe4The last two commits on
mainthat touchpolicy.rsare7284a17c8(perf(gc): route the trigger path and dirty-page barrier through hot TLS, #9827) and35c36f425(style: cargo fmt). They landed through the 2026-09-06 train without the window being re-audited and re-pinned; the gate is doing exactly what it exists to do (an entry whose pinned source changed must be re-reviewed, not silently accepted).Fix
Re-audit the
census_pass1_if_armed→census_take_if_armed_at_full_sweep_startwindow against the twopolicy.rschanges (neither should alter mark/sweep control flow between the window's start and end: #9827 is TLS routing, the fmt commit is whitespace), append the re-audit note to the entry'swhy(append-only, per the inventory's own rule) and replace thepolicy.rspin with the current digest. One-line JSON change plus the note; nothing in Rust.Related hazard for #9838's landing (same file)
mainno longer has theSHAPE_CACHE_YOUNGholder (removed with #9756's "drop the shape-cache young log"), and its inventory entry is gone with it. #9838's branch is based ond36a1af0c, whose inventory still carries that entry. A merge resolution that takes the union of both sides' holder entries re-addsSHAPE_CACHE_YOUNG, and the gate then fails on the merged tree with a different message (a stale entry that matches nothing). Observed on a measurement-only merge of #9838 onto35c36f425(perry-b4, 2026-09-06). Whoever lands #9838 should start frommain's inventory, add only its ownGC_TINY_PARSE_PRESSURE_BASE_BYTESverdict, and re-pinpolicy.rsfrom the merged file — not merge the two JSONs.https://claude.ai/code/session_014knX724SYDogwzsXybCGxp