Restore branch protection and assert it on every PR - #207
Merged
Conversation
Both branch rulesets were `enforcement: disabled`. `main` had no required status checks at all — PR #190 (212 files, +60766/-37428) merged green 29 minutes after its own CI failures were filed as issues #202, #203 and #204. Two structural traps kept the gates off, and both are now checked: Phantom context. Ruleset 7726557 required a check named "CI". No job reports that name, so enabling it would hang every PR pending forever. The reachable fix under pressure is to switch the ruleset off, which is what happened. Its required_status_checks rule is dropped; it now carries the PR requirement (squash-only, thread resolution) and ruleset 6154907 owns the checks. Path-filtered required job. ci-windows.yml skipped via `on: paths-ignore:`, which cannot be a required check: a filtered-out run never reports, and a required check that never reports blocks the merge forever. Converted to job-level `if:` skipping, which reports "skipped" and counts as passing, so windows-core can now be required. Both rulesets are active, with no bypass actors, and six required checks: Detect changed areas, Test/Format/Build & Validate, Rust Compiler, WebAssembly target, Website E2E, Windows Core. The wasm corpus differential was advisory by design — that is how a wasm-only miscompile (#202) could land unopposed. scripts/verify-branch-protection.mjs pins that list and runs in the `changes` job, the one required job that never skips. It fails on a disabled ruleset, a bypass actor, drift in either direction between the list and the ruleset, a context matching no job name, and a required job its workflow path-filters. Verified against the real broken state: it reports every one. Stale comments claiming these jobs are unrequired are corrected, and CLAUDE.md gains the rule this violated — a gate you can turn off is not a gate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
Both branch rulesets were
enforcement: disabled.mainhad no required status checks at all — any PR could merge red and nothing would stop it.PR #190 (212 files, +60,766/−37,428) merged at 10:38 on 2026-08-12. Issues #202 (wasm corpus aborts Node's WASI host), #203 (all 8 lldb-dap E2E tests time out on Linux) and #204 (website E2E flakes red) were filed at 10:09–10:10 — 29 minutes earlier. The failures weren't missed; they were written down and merged past. The websocket showstoppers #192–#197 were filed the previous evening.
Two structural traps kept the gates off
Phantom context. Ruleset 7726557 required a check named
"CI". No job reports that name — check runs are named by jobname:, not by workflow. Enabling it would hang every PR pending forever, so the reachable fix under pressure is to switch the ruleset off. Itsrequired_status_checksrule is dropped; it now carries the PR requirement (squash-only merges, thread resolution) and ruleset 6154907 owns the checks.Path-filtered required job.
ci-windows.ymlskipped website-only PRs viaon: paths-ignore:, which cannot be a required check: a filtered-out run never reports, and a required check that never reports blocks the merge forever. Its own comment said as much and concluded "so it isn't required". Converted to job-levelif:skipping — which reportsskipped, and a skipped check counts as passing — sowindows-corecan now be required.State now
Both rulesets
active, no bypass actors (applies to admins too), strict up-to-date policy, and six required checks:The part that stops it recurring
Branch protection lives in GitHub's settings, not in this tree, so it drifts silently and by definition no test covers it.
scripts/verify-branch-protection.mjsis that test. It runs in thechangesjob — the one required job that never skips — and fails on:active, or that has bypass actorsname:(the phantom trap)on:level (the deadlock trap)Verified against the real broken state rather than assumed: fed the exact 2026-08-12 configuration, it reports every fault, phantom
"CI"included. Fed the pre-fixci-windows.yml, it reports the path-filter deadlock.Stale comments asserting these jobs were unrequired are corrected in place, and
CLAUDE.mdgains the rule this violated: a gate you can turn off is not a gate, "advisory" means deleted, and an open issue about a red check is a blocker rather than a footnote.Not covered here
The failures themselves are still open — #202, #203, #204, the websocket showstoppers #192–#197, and the effects umbrella #200. This PR only ensures the next one can't merge past them.