Skip to content

check_gc_env_knobs.py is wired into no workflow — it caught a real shipped bug only because someone ran it by hand #8166

Description

@proggeramlug

scripts/check_gc_env_knobs.py runs in no workflow

$ grep -rln check_gc_env_knobs --include=*.yml --include=*.sh --include=*.py .
  (only the script itself, plus gc-handoff/KNOBS-NOTES.md)

It is not in test.yml's lint job — I generated that job's real command list (27 invocations, lines 88–380) and it is absent — nor in any other workflow. gc_gate_wiring_check.py reports "GC gate wiring OK (7 gates main-line-reachable and able to fail)" without counting it, so the wiring checker does not know about it either.

This is CLAUDE.md's "★ Four ways a gate can be unable to fail", hazard 2, in its purest form: the gate exists, it works, and nothing runs it.

It works, and it just caught a real bug — locally, by hand

On main at 48935af78's parent it failed:

GC environment-knob drift check FAILED:
  - PERRY_GC_STACKMAP_TRACE: read for presence (var_os(..).is_some()/.is_none())
    in crates/perry-runtime/src/gc/roots/stack_maps.rs;
    'PERRY_GC_STACKMAP_TRACE=0' would ENABLE it.
    Use gc::env_flag_enabled (default-OFF) or gc::env_default_on_enabled (default-ON kill switch).

That is a genuine defect — #8131 introduced the knob with var_os(..).is_some(), so the spelling a reader is most likely to try, =0, turned the trace on. #8164 fixed it and the gate now passes (30 claimed knobs, 197 live env parsers, 3 historical documents exempt, 0 presence-only GC reads).

So the script earns its keep. It found a real bug in shipped code, on the first run. It simply never runs unless someone invokes it by hand — which is how #8131's knob reached main in the first place.

Correcting the record on #8164

#8164's changelog says the gate "failed on it, and a red lint on main blocks every open PR." The fix is right and should stay, but that justification is not accurate: lint was not red from this, because the gate is not in lint. Nothing in CI was red. The bug reached main and sat there precisely because no job would have caught it.

Worth stating plainly, because "a red gate forced this fix" and "no gate could have caught this" call for opposite follow-ups, and only the second one is true.

Ask

  1. Wire it into lint, next to the other scripts/*.py audits. It is fast and has a --self-test-style failure mode already.
  2. Teach gc_gate_wiring_check.py about it, so an unwired GC gate is itself a lint failure. That checker currently reports 7 gates as wired and cannot see the eighth that is not — which makes its "OK" narrower than it reads.
  3. Per CLAUDE.md's corollary, run it once on main before promoting it to required, so it does not immediately block every open PR.

Related

Same family as #8101's finding that parity and compile-smoke are required contexts that never run on an unlabeled PR. Between them, this repo currently has one gate that runs but cannot block, and two that block but cannot run.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions