Take the ruleset bypass list out of the fleet config entirely - #545
Merged
Conversation
Who may bypass a ruleset decides who the rules do not apply to, so it is a human decision taken per repository rather than a value the fleet config hands out. The three payloads declared RepositoryRole 5 with bypass_mode always, and applying them granted repository admins a standing exemption on main and develop across every repo. The docs are explicit that a ruleset applies to administrators by default and that the bypass list starts empty, so the declaration was a real grant rather than a restatement of an inherent privilege. Whether these repos first got it from the config or from earlier hand-made rulesets is unknown, and it does not matter: the config should not be the thing maintaining it either way. Removing the declaration alone would have made this worse. apply sends the payload as a PUT, which replaces the whole document, so a payload with no bypass_actors would clear the live list on every run. That is code deleting a bypass, which is the one thing that must never happen here. apply now reads the live list and writes it back unchanged, and aborts rather than proceeding if that read fails, since applying without it would silently clear what it was meant to preserve. On create there is no live list to read and none is sent, so a new ruleset starts at GitHub's own empty default. check reports the list on every run and asserts nothing about it, because no payload declares a value to compare against and inventing one would put code back in charge of the decision. Verified by composing the PUT body against this repo's live ruleset without sending it: the payload declares nothing, the live list is RepositoryRole 5 always, and the composed body carries that same list back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR removes bypass_actors from the fleet ruleset payloads so bypass permissions remain a per-repository human decision, while updating the config script to preserve any existing live bypass list during ruleset updates and to report (but not assert) the live bypass list during check.
Changes:
- Removed
bypass_actorsfrom themainanddevelopruleset payload JSON files. - Updated
apply_rulesetto read the livebypass_actorslist on update and write it back unchanged to avoid accidental clearing via PUT replacement semantics. - Updated
check_rulesetto print the live bypass list each run without enforcing it against the payload.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| repo-config/operational/develop.json | Removes bypass list from operational develop ruleset payload. |
| repo-config/main.json | Removes bypass list from main ruleset payload. |
| repo-config/develop.json | Removes bypass list from release develop ruleset payload. |
| repo-config/configure.sh | Preserves live bypass list on ruleset update; reports bypass list during check without asserting it. |
This was referenced Aug 3, 2026
ptr727
added a commit
that referenced
this pull request
Aug 3, 2026
Three commits. Merged as a **merge commit**, never squashed, per the branching model. | Commit | PR | What | | --- | --- | --- | | `e48d64d` | #544 | Regenerates the divergence ledger against the previously promoted `main` | | `90e3255` | #545 | Takes the ruleset bypass list out of the fleet config entirely | | `362aec8` | #546 | Queues the agent-isolation rule and the vendored-tooling surface | ## The one that matters **#545 is a behaviour change to `configure.sh`, not a doc edit.** All three ruleset payloads declared `bypass_actors: [{ actor_id: 5, actor_type: RepositoryRole, bypass_mode: always }]`, so applying them granted repository admins a standing exemption on `main` and `develop` in every repo. GitHub's documentation is explicit that a ruleset applies to administrators by default and that the bypass list starts empty. Admins are *eligible* for bypass and must be added. So the declaration was a real grant rather than a restatement of something inherent, and the fleet config was the thing handing it out. Removing the declaration alone would have been worse than leaving it. `apply` sends the payload as a `PUT`, which replaces the whole document, so a payload with no `bypass_actors` would have **cleared the live list on every run**. That is code deleting a bypass. `apply` now reads the live list and writes it back unchanged, and aborts rather than proceeding if that read fails. On create it sends none, so a new ruleset starts at GitHub's own empty default and nothing is deleted, because nothing existed. `check` reports the list on every run and asserts nothing, since no payload declares a value to compare against and inventing one would put code back in charge of a human decision. This does **not** remove the bypass entries already live on the fleet's rulesets. Removing those would itself be code deleting a bypass. They are now visible on every `check` run so they can be cleared deliberately, per repo, in the UI. ## Expected consequence, stated rather than discovered `repo-config/configure.sh` is carried `verbatim` with `appliesTo: "*"`, so this promotion makes every repo holding a copy stale against the hub. That is the third re-vendor this file has generated in one session, which is exactly the evidence the vendored-tooling entry added in #546 now records. The follow-up plan is to fold that re-vendor into the payload migration already owed by five repos, so each gets one visit that renames its `ruleset-*.json` payloads and takes the current script, rather than two. ## Verification `spec/validate.py` OK (21 cataloged), diff-scoped `prose_lint` clean, `markdownlint-cli2` clean, editorconfig clean, live read-only `check` against this repo passes with the bypass list reported and unasserted. The preserve path was proven by composing the `PUT` body against this repo's live ruleset without sending it: the payload declares nothing, the live list is `RepositoryRole 5 always`, and the composed body carries that same list back.
ptr727
added a commit
that referenced
this pull request
Aug 3, 2026
Blog was stood up on 2026-08-01 and cut release `1.0.11` the same day, but it was never added to `registry/repos.json`. Every hub tool is registry-driven, so `spec/audit.py` and `spec/fidelity_honesty.py` had never measured it, and `reports/divergences.md` was under-reporting the fleet by exactly one repo while reading as complete. This catalogs it and lands its first audit. ## The entry records what ran, not what was predicted `reports/conformance-matrix.md` already named Blog as the reference repo for the `source-only` + `release` shape, and predicted `releaseTrigger: none` with an empty `publish[]` until a deploy ran. What actually shipped is a dispatch-only publisher that cut a tag and a source archive, so the entry is `dispatch-only` with the GitHub release declared, and the matrix row is corrected to match. The VPS deploy still has not run, so the static-site type stays deferred to #456 and `publish[]` gets revisited when it does. ## What the audit found Nine findings, no defects, verdict **operational**. Every one is the hub advancing after the carry rather than the repo regressing: - **Eight verbatim units behind the canonical.** Six are stale copies. Two never arrived at all, `AGENTS.md > Fleet Bootstrap` and `GOVERNANCE.md > Representative Data in Agent-Authored Text`, and that distinction matters because a stale copy still states the rule in an older form while an absent one states nothing. - **One undeclared section**, `AGENTS.md > Project Conventions`, whose four rules are genuinely repo-specific and duplicate no verbatim section. The convergence PRs against Blog follow separately, one per drift class per AUDIT.md section 10. ## Measured against `main`, with `develop` recorded separately The run reads hub `main` (`3a7cc64`) per AUDIT.md section 1. A re-run from a tree at `develop` (`362aec8`) reports two more re-vendors and two DEFECTs, all four from #545 taking `bypass_actors` out of the ruleset payloads an hour earlier. Those are recorded in the report rather than counted, because measuring against un-promoted hub content reports work in flight as a conformance failure. The same four will appear against every fleet repo when that promotes, and the live-ruleset half is a settings change on a protected branch, so it is yours to apply rather than an agent's. ## Two spec questions, raised rather than resolved 1. **The hub contradicts itself on whether a repo may carry its own `AGENTS.md` section.** `AGENTS.md` says a project's own conventions live in that project's `AGENTS.md`. `spec/section-model.md` says an undeclared section is drift to reconcile and lists four destinations, none of them `AGENTS.md`. A repo following the one is flagged for violating the other, and Blog is exactly that case. 2. **The character-set tiers do not classify the COPYRIGHT SIGN**, which any repo generating a feed or a rendered document will hit. A third, smaller one: the report directory here is `reports/blog/`, lowercase, matching every existing report directory rather than the repo's CamelCase name. AUDIT.md section 8 writes it as `reports/<repo>/audit.md`, which reads as exact case, and the two have quietly disagreed since the first report. Worth settling in the wording, and worth settling before someone clones on a case-insensitive filesystem. ## Verification - `python3 spec/validate.py`: 22 cataloged, 0 backlog, classifying cleanly. - `python3 spec/audit.py Blog` from a tree at hub `main`: 9 drift, 0 defect/letter/error. - `python3 spec/fidelity_honesty.py --report`: regenerated, and the whole diff is Blog appearing in the rows it belongs in. - `python3 scripts/prose_lint.py reports/blog/audit.md`: clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727
added a commit
that referenced
this pull request
Aug 3, 2026
…551) Regression fix. #545 removed `bypass_actors` from the ruleset payloads, and **every repo in the fleet has been reporting two ruleset DEFECTs since it promoted**. ## What broke `spec/audit.py` compares the live ruleset against the payload over a fixed subset, and that subset included `bypass_actors`. A payload that deliberately declares no bypass list, against a live ruleset that still has one, is a normalized diff — so the audit called it divergence: ```text == Utilities (csharp, nuget; release) @ main@e179288 == DEFECT ruleset: develop diverges from repo-config/develop.json (normalized diff) DEFECT ruleset: main diverges from repo-config/main.json (normalized diff) ``` Reproduced on Utilities, PlexCleaner and Blog. It is every repo, because every live ruleset carries the entry the payloads stopped declaring. ## The fix, and why it is this way round The field is dropped from the compared subset rather than restored to the payloads. Who may bypass a ruleset is a per-repository human decision taken in the UI, and `configure.sh` already treats it that way after #545: `apply` writes the live list back unchanged, `check` reports it without asserting. The audit asserting the same field contradicted that directly. **The defect was two tools comparing one field under opposite policies, not the field's value.** The comment says so and records what happened when it was not, because the next person to look at that list will otherwise put it back. ## How it was found While reviewing the Blog registration in #547. That report claims nine drift and no defects, which was true when it was written against hub `main` at `3a7cc64`, and stopped being true an hour later when #545 promoted. The PR body had even predicted it: "The same four will appear against every fleet repo when that promotes." It did. This is the same staleness the audit's own run-stamp rule exists to expose, arriving from the hub's side rather than the repo's. ## Verification - Utilities: 2 ruleset DEFECTs before, **0** after. Its remaining two findings are LETTER results for absent `GOVERNANCE.md` and `OPERATIONS.md`, which are the real propagation gap rather than this regression. - PlexCleaner: same, 2 before and 0 after. - `spec/validate.py` OK, diff-scoped `prose_lint` clean, editorconfig clean, syntax checked. ## Follow-on #547's `reports/blog/audit.md` should be regenerated against this corrected tool before it merges, so the committed report matches what the audit now reports rather than what it reported an hour before the payload change landed. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Who may bypass a ruleset decides who the rules do not apply to. That is a per-repository human decision, not a value the fleet config hands out.
All three payloads declared
bypass_actors: [{ actor_id: 5, actor_type: RepositoryRole, bypass_mode: always }], so applying them granted repository admins a standing exemption onmainanddevelopin every repo. Verified live: every fleet repo sampled carries exactly that entry on both branches.It was a real grant, not a restatement
GitHub's documentation is explicit that a ruleset applies to administrators by default and that the bypass list starts empty. Admins are eligible for bypass and must be added. The maintainer's UI screenshot confirms the same from the other side: the entry offers a Delete bypass action, so it is a deliberate list member rather than an inherent property of the role.
Whether these repos first got it from this config or from hand-made rulesets that predate the tooling is unknown, and it does not matter. The config should not be the thing maintaining it either way.
Removing the declaration alone would have made it worse
applysends the payload withgh api --method PUT, which replaces the whole document. A payload with nobypass_actorswould therefore have cleared the live list on every run. That is code deleting a bypass, which is the specific thing that must never happen.So
applynow reads the live list and writes it back unchanged, and aborts rather than proceeding if that read fails, since applying without it would silently clear what it was meant to preserve. On create there is no live list and none is sent, so a new ruleset starts at GitHub's own empty default and nothing is deleted, because nothing existed.checkreports and never assertsNo payload declares a bypass value, so there is nothing to compare against, and inventing an expectation would put code back in charge of the decision.
checknow prints the live list on every run, the way it already prints the secrets manual-verify note:Visible on every run, judged by nothing.
Verification
The preserve path was proven by composing the PUT body against this repo's live ruleset without sending it:
bash -nclean,prose_lintclean, editorconfig clean, live read-onlycheckagainst this repo passes.What this does not do
It does not remove the bypass entries that already exist on the fleet's rulesets. Removing those would itself be code deleting a bypass. Clearing them is a deliberate per-repo act in the UI, and
checknow makes each one visible so they can be reviewed rather than forgotten.