Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 36 additions & 14 deletions config/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,42 @@ REST API.
standing push right.

| Integration ids 56611 (codacy), 827041 (gitar-bot), 254 (codecov), 2740 (renovate), 57789 (advanced-security), 1561, 85455, 946600
| R1/R4 removals, advanced-security needs no bypass. Scanned 2026-09-20: all
1,026 rulesets over all 453 repos in both estates. No active ruleset grants bypass to
any uninstalled app. 1561, 85455 and 946600 (Codex MCP) each appear exactly once, all
on one *disabled* ruleset — `hyperpolymath/anvomidaviser` id 14968878 `Optimus-Branch`,
which also still grants `always` bypass to RepositoryRole 2 (maintain) and keeps
`code_coverage` / `required_deployments` / `copilot_code_review`, the set §7.3 retired;
delete it rather than re-enabling it. O5 closes for 1561/85455 (no such app, no active
grant) and stays open for 946600. An earlier draft of this row quoted 96-repo sample
counts, including "67 repo rulesets" for 946600, and read this table as if it listed live
grants; both are superseded by the scan above.
Same scan: the only integration bypasses on *active* repo rulesets are oikosbot (6) and
claude (2). Dependabot, github-actions, gitguardian, sonarqubecloud and coderabbitai hold
none — so installing CodeRabbit also means adding its bypass actor to the estate ruleset,
or its auto-fix and commit-queue pushes cannot reach a protected branch.
| R1/R4 removals, advanced-security needs no bypass.
*This row was wrong twice and is now cut from a complete scan: 1,036 rulesets over all 453
repos, every ruleset fetched individually.* `GET /repos/{o}/{r}/rulesets` omits `rules` and
`bypass_actors` from the objects it lists, so a list-only sweep reports 1,026 rulesets with
empty arrays and finds nothing — which is precisely the "no active ruleset grants bypass to
any uninstalled app" sentence that stood here, and it was false. Read per-ruleset instead.
*Live, active, 2026-09-20:* *gitar-bot (827041) holds `always` bypass on the
`metadatastician` org ruleset `EstateBranching` (id 18225024)*, i.e. on all 43 repos —
`always` defeats `required_signatures` and the PR requirement on every default branch, for an
app that is not installed. 946600 sits on the same org ruleset plus 13 active repo rulesets
(56 in total); 1561 and 85455 on 9 each. RepositoryRole 2 (maintain) with `always` is on 53
active rulesets, so "the maintain role is dropped" is not yet true. codecov 254, codacy 56611,
renovate 2740 and advanced-security 57789: zero grants — those four are genuinely clear.
*Retired constraints still enforced:* `code_coverage` 130 rulesets (120 repos; the
`minimum_coverage: 95, max_coverage_drop: 3` gate that keeps pushing repos toward an external
coverage uploader), `code_quality` 137, `copilot_code_review` 162, `required_deployments` 78,
`update` 430 (only 11 admit the planner's precondition). `code_scanning` is kept: GitHub-native,
no third-party app behind it.
*Not applied, and not applicable by CI:* ruleset *writes* are refused for this estate's
fine-grained PAT — `PATCH`/`PUT` → 404 while `GET` → 200 and `POST`/`DELETE` are accepted
(measured on `marches`, `standards`, `anvomidaviser` 2026-09-20; `POST` created a probe
ruleset that was deleted again, so treat that capability as real and dangerous). Remediation
therefore ships as reviewable PUT bodies, in the shape
`scripts/plan-ruleset-constraint-repair.rb` already
intends: 96 repo rulesets + the one org ruleset, dry-run validated, each body guarded by a
re-read of live state so a concurrent owner edit is skipped rather than overwritten.
`anvomidaviser/Optimus-Branch` (14968878) is *disabled*, so it fails that planner's
active-only precondition: delete it outright rather than repairing it.
*Still unresolved by design:* 1561 / 85455 / 946600 (`/user/installations` → 403, marketplace
index → 404, no `GET /apps/{slug}` candidate reproduces them), plus two ids this scan is new
to — 262318 and 1658531 — which hold `always` bypass on `EstateBranching`. Unidentified
standing push rights on every default branch in the org; resolve in Settings → Applications
before assuming they are wanted. O5 stays open for all five.
*Keep-list note, still valid:* on active *repo* rulesets the only integration bypasses are
oikosbot (6) and claude (2), so installing CodeRabbit also means adding `coderabbitai`
(347564) to the estate ruleset or its auto-fix pushes cannot reach a protected branch.
Note `123` (codecov PR gate) was never a bypass actor — it only ever appears as
a jtv-lang required-check context, which is the §11.3 hand-typed-context bug.
|===
Expand Down
6 changes: 1 addition & 5 deletions config/rulesets/Optimus-Extras.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
{ "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "pull_request" }
],
"rules": [
{ "type": "code_scanning", "parameters": { "code_scanning_tools": [] } },
{ "type": "required_deployments", "parameters": { "required_deployment_environments": [] } },
{ "type": "code_quality" },
{ "type": "code_coverage" },
{ "type": "copilot_code_review" }
{ "type": "code_scanning", "parameters": { "code_scanning_tools": [] } }
]
}
48 changes: 48 additions & 0 deletions config/rulesets/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
= config/rulesets — canonical protection definitions
:toc:

These files are the estate's intended ruleset state. `base.json` is the default-branch
baseline; `Optimus-Extras.json` carries the extras for repos on the Optimus profile;
`gates-only.json` / `gates.json` describe which workflow files become required contexts;
`immutable-tags.json` is the tag canon.

== Nothing here is applied automatically

`scripts/apply-tag-ruleset-canon.sh` converges *only* the tag ruleset, and its own header
records the consequence: after the 2026-09-11 deployment wave the branch-side remediation was
"manual per-repo `gh api` PUTs, because no committed script in this repository globs
`config/rulesets/`". That gap is why `Optimus-Extras.json` could keep four constraints §7.3
retired (`code_coverage`, `code_quality`, `required_deployments`, `copilot_code_review`) and
re-add them on the next manual convergence, on 120-162 active rulesets estate-wide. The file is
now trimmed; the propagation mechanism is still missing by design, and this note is the
reminder that a template fix without an applier is a half fix.

`scripts/plan-ruleset-constraint-repair.rb` is the sanctioned planner: it emits a reviewable PUT
body for exactly those retired constraints, optionally dropping one integration's bypass once
GitHub has rejected that app as no longer installed. It never calls GitHub.

== What a repository token can do

Measured 2026-09-20 with the estate's fine-grained PAT:

|===
| operation | result

| `GET /repos/OWNER/REPO/rulesets`, `GET .../rulesets/ID`
| 200 — readable, but the list payload omits `rules` and `bypass_actors`; fetch each id
individually or you will conclude the estate is clean when it is not.

| `PATCH` / `PUT .../rulesets/ID`
| 404 — updates refused, so no workflow or PR can converge protection.

| `POST .../rulesets`, `DELETE .../rulesets/ID`
| accepted. Treat as a live capability: an over-eager applier can drop a ruleset and leave the
default branch unprotected in the gap, which is worse than the drift it set out to fix.

| `PATCH /orgs/ORG/rulesets/ID`
| 404 (no `admin:org`), and org rulesets are not writable through the repo endpoint either —
only readable. The `metadatastician` `EstateBranching` fix must be run by an owner session.
|===

Consequence for any future applier: PUT-by-id against the live object, never
delete-then-create, and re-read before writing so a concurrent owner edit is skipped.
Loading