Branch protection went live on main on 2026-07-30, as repository rulesets rather than classic branch protection (the classic /branches/main/protection endpoint 404s - worth knowing, because that 404 reads like "unprotected" and is not).
Current state, read from the API rather than the UI:
Ruleset EJ (id 20021316, enforcement: active, targets ~DEFAULT_BRANCH):
deletion
non_fast_forward
required_status_checks: context verify (integration 15368 = GitHub Actions), strict_required_status_checks_policy: false
bypass_actors: [] - nobody can override, including the owner. That is the right call and should stay.
Ruleset EJNOFORCE (id 19839107, enforcement: active, same target): deletion + non_fast_forward only.
This already does the main job: #55 and #57 were correctly blocked as soon as it turned on, because their branches predate .github/workflows/verify.yml and therefore had no verify run at all. Three follow-ups, in descending order of how much they matter.
1. There is no "require a pull request" rule
EJ requires the verify check but does not require changes to arrive via a pull request. A direct push to main is not structurally forbidden.
Whether that matters depends on intent. The required check makes a direct push awkward but the repo's own history says the risk is real: a commit meant for a feature branch has landed on main here before (see the note in CLAUDE.md about verifying git branch --show-current in the same command as a commit). If PR-only is the intent, add a pull_request rule. If direct pushes for docs-only fixes are wanted, leave it and this can be closed as deliberate.
2. strict_required_status_checks_policy is false
So a branch does not have to be up to date with main to merge. A PR can pass verify against an older main and land on a main it was never tested against.
That is not hypothetical here - it happened twice this week in the same afternoon:
A PR whose green check predates either of those was measured against a different gate.
Leaving it false is defensible while several PRs are open, since true forces every branch to rebase and re-run before it can merge. But it should be a decision, not a default. Suggestion: flip it to true once the current queue (#55, #57) is drained, when the cost is near zero.
3. EJNOFORCE is fully subsumed by EJ
Both are active, both target the default branch, and EJNOFORCE's two rules (deletion, non_fast_forward) are both already in EJ. Two overlapping active rulesets is a state where a future edit to one looks effective and is not. Fold it in and delete it, or rename it to say what it is for.
Why this is worth writing down rather than just doing
The prerequisite relationship runs the other way from how it looks: narrow automerge is only safe because this ruleset exists (see the separate issue), so weakening any of it later silently weakens that too.
Branch protection went live on
mainon 2026-07-30, as repository rulesets rather than classic branch protection (the classic/branches/main/protectionendpoint 404s - worth knowing, because that 404 reads like "unprotected" and is not).Current state, read from the API rather than the UI:
Ruleset
EJ(id 20021316,enforcement: active, targets~DEFAULT_BRANCH):deletionnon_fast_forwardrequired_status_checks: contextverify(integration 15368 = GitHub Actions),strict_required_status_checks_policy: falsebypass_actors: []- nobody can override, including the owner. That is the right call and should stay.Ruleset
EJNOFORCE(id 19839107,enforcement: active, same target):deletion+non_fast_forwardonly.This already does the main job: #55 and #57 were correctly blocked as soon as it turned on, because their branches predate
.github/workflows/verify.ymland therefore had noverifyrun at all. Three follow-ups, in descending order of how much they matter.1. There is no "require a pull request" rule
EJrequires theverifycheck but does not require changes to arrive via a pull request. A direct push tomainis not structurally forbidden.Whether that matters depends on intent. The required check makes a direct push awkward but the repo's own history says the risk is real: a commit meant for a feature branch has landed on
mainhere before (see the note in CLAUDE.md about verifyinggit branch --show-currentin the same command as a commit). If PR-only is the intent, add apull_requestrule. If direct pushes for docs-only fixes are wanted, leave it and this can be closed as deliberate.2.
strict_required_status_checks_policyisfalseSo a branch does not have to be up to date with
mainto merge. A PR can passverifyagainst an oldermainand land on amainit was never tested against.That is not hypothetical here - it happened twice this week in the same afternoon:
vite.config.ts(testTimeout) andCLAUDE.mdverifyitself means, adding acheck:vuephase (verifyis nowvp check && pnpm run check:vue && vp test && pnpm run preview:test)A PR whose green check predates either of those was measured against a different gate.
Leaving it
falseis defensible while several PRs are open, sincetrueforces every branch to rebase and re-run before it can merge. But it should be a decision, not a default. Suggestion: flip it totrueonce the current queue (#55, #57) is drained, when the cost is near zero.3.
EJNOFORCEis fully subsumed byEJBoth are active, both target the default branch, and
EJNOFORCE's two rules (deletion,non_fast_forward) are both already inEJ. Two overlapping active rulesets is a state where a future edit to one looks effective and is not. Fold it in and delete it, or rename it to say what it is for.Why this is worth writing down rather than just doing
The prerequisite relationship runs the other way from how it looks: narrow automerge is only safe because this ruleset exists (see the separate issue), so weakening any of it later silently weakens that too.