fix: the committed ruleset would lock the owner out on its first apply - #32
Merged
Conversation
No ruleset is applied on this repository. `gh api repos/ChelseaKR/perimeter/rulesets` returned `[]` on 2026-08-15 and again on 2026-08-28, so nothing is broken yet; what was committed was a first application that would have broken it. `.github/rulesets/main.json` carried `"bypass_actors": []`, and the ruleset README argued for it by name, calling the empty list "the stricter reading" of CICD-15. It is not the stricter reading. `bypass_actors` now holds exactly the repository owner's standing bypass, `RepositoryRole` 5 with `bypass_mode: always`, deliberately and permanently: an agent once applied a ruleset with no bypass and locked the owner out of their own repository, and restoring access took a sweep across eighteen repositories. An empty list there is not a stricter gate, it is the lockout. That matters more here than anywhere else in the portfolio, because this profile has never met a live ruleset. It asks for `required_signatures`, `required_linear_history`, a strict up-to-date policy and five required contexts, two of which do not exist on `main` yet. That is a lot of ways for a first application to wedge, and the empty list would have removed the only way back in that does not go through GitHub support. `bypass_mode: always` rather than CICD-15's `pull_request` for the same reason: a bypass that only works inside a pull request is no use when the pull request is the thing that is wedged. The README bullet is replaced with the reversal and the reasoning rather than quietly rewritten, the apply procedure gains a step that checks the bypass actually came through, and "what is true today" now states plainly that no ruleset is applied and that this file has therefore never been corrected by a live one. Nothing here touches a live setting; there is none to touch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ordered list is what a reader follows, so the check belongs in it as a step rather than only in the confirmation after the fence. Records that POST adds a ruleset rather than replacing one: rules from every applicable ruleset combine and bypass actors are per-ruleset, so posting twice without deleting leaves an empty-bypass ruleset over `main` that blocks the owner whatever the first one allows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file said "Two of the five contexts in `main.json` do not exist on `main` yet" and made landing them step 1 of the apply order. It said the same thing a second time in the `bypass_actors` bullet. Both are now false. The sentence was written on 2026-08-15 in 18827df, at 17:14. `zizmor`, one of the two contexts it meant, landed on `main` in #16 (839557e) the same day, and `sast` had been a job in ci.yml since ea06580 on 2026-08-07. So it was true when written on its own branch and false within the day, and nothing here noticed for two weeks, which is the shape #30 fixed in the Standards Conformance table and ADR-0009 turned out to have in its fire dates. Re-measured 2026-08-29 rather than reasoned about. `verify`, `secret-scan`, `sast` and `zizmor` are jobs in .github/workflows/ci.yml on `main`; `codeql (actions · python · javascript)` is the `name:` of the `analyze` job in codeql.yml, which triggers on `pull_request: branches: [main]`. Read off the two most recent pull request head commits, 62c406e (#30) and ae0e2d6 (#31), each reported all five contexts. The other dated claims in the file were re-read at the same time and are unchanged: no ruleset is applied, `"protected": false`, and the branch protection endpoint still 404s. The dependabot queue that step 2 said to drain is drained, measured as 0 open. The two completed steps are kept as history rather than deleted, so the order stays legible, and the remaining steps are renumbered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The previous commit in this branch corrected `"bypass_actors": []` in .github/rulesets/main.json. Correcting the file is not the fix, because nothing in this repository reads that file. Measured before writing this: no test and no source module referenced `ruleset` or `bypass_actors` anywhere, so the value could regress to the empty list in one edit and every gate would stay green. That is what made the committed file dangerous rather than merely wrong. It is never applied, so no live ruleset has ever contradicted it, and nothing read it, so nothing here could contradict it either. The apply command in the README posts the file as it stands, which means following this repository's own documented procedure was enough to lock the owner out of it. tests/test_ruleset.py makes the empty list red. `lockout_risk` is a pure function of a parsed document, run against the five shapes that lose the bypass (empty list, absent key, wrong type, a different actor, and the owner with `bypass_mode: pull_request`) as well as against the committed file, with a positive control so it is not passing by refusing everything. `load_ruleset` fails on a missing or unparseable file rather than returning an empty document. A check that passes when its subject is absent is the defect class this repository keeps finding, and it would be a poor joke to introduce one here. This does not apply the ruleset and does not ask anyone to. Applying it changes a live repository setting and remains the owner's call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… applied Records the three things the ruleset entry did not yet cover: that nothing read main.json before this branch so correcting it once would not have held, that the apply order's claim about missing required contexts was re-measured and is false, and that landing this changes nothing on the server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ChelseaKR
force-pushed
the
fix/record-owner-bypass-in-ruleset
branch
from
August 29, 2026 17:28
16a2491 to
81e2eba
Compare
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.
The hazard, which is not the same one as elsewhere in the portfolio
No ruleset is applied on this repository.
gh api repos/ChelseaKR/perimeter/rulesetsreturned[]on 2026-08-15 and again on 2026-08-28. So nothing is currently broken. What was committed was a first application that would have broken it:.github/rulesets/main.jsoncarried"bypass_actors": [], and.github/rulesets/README.mdargued for it by name, calling the empty list "the stricter reading" of CICD-15.That reasoning is what this PR reverses. It was not wrong about the risk an admin bypass carries; it was wrong about which risk is larger, and the larger one has already happened elsewhere in this portfolio.
The change
bypass_actorsnow holds exactly the repository owner's standing bypass,RepositoryRole5 withbypass_mode: always, deliberately and permanently: an agent once applied a ruleset with no bypass and locked the owner out of their own repository, and restoring access took a sweep across eighteen repositories. An empty list there is not a stricter gate, it is the lockout.It matters more here than in a repo with a working ruleset, because this profile has never met a live one. It asks for
required_signatures,required_linear_history, a strict up-to-date policy and five required contexts, two of which do not exist onmainyet. That is a lot of ways for a first application to wedge, and the empty list would have removed the only way back in that does not go through GitHub support.bypass_mode: alwaysrather than CICD-15'spull_requestfor the same reason: a bypass that only works inside a pull request is no use when the pull request is the thing that is wedged.Prose:
bypass_actorsbullet in.github/rulesets/README.mdis replaced with the reversal and the reasoning it replaces, quoted, rather than quietly rewritten.main.jsonhas therefore never been corrected by a live one.POST, that checksbypass_actorsholds the owner entry, and a step after it that confirms the bypass actually came through. It also records thatPOSTadds a ruleset rather than replacing one, and that rules from every applicable ruleset combine while bypass actors are per-ruleset, so posting twice without deleting leaves an empty-bypass ruleset overmainthat blocks the owner whatever the first allows.[Unreleased]CHANGELOG entry.Scope
Nothing here touches a live setting; there is none to touch. Reads against the API only.
make verifyexits 0 on this branch.