Skip to content

Promote develop to main: the bypass list leaves the fleet config - #548

Merged
ptr727 merged 3 commits into
mainfrom
develop
Aug 3, 2026
Merged

Promote develop to main: the bypass list leaves the fleet config#548
ptr727 merged 3 commits into
mainfrom
develop

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Three commits. Merged as a merge commit, never squashed, per the branching model.

CommitPRWhat
e48d64d#544Regenerates the divergence ledger against the previously promoted main
90e3255#545Takes the ruleset bypass list out of the fleet config entirely
362aec8#546Queues 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.

ptr727and others added 3 commits August 3, 2026 14:51
Generated output only, no hand edits. `reports/divergences.md` reads
each repo's ground-truth `main`, so before #542 promoted it was
measuring a hub `main` that predated this session and reporting the hub
as not carrying a section it had just authored.
## What moved, and how each was established
**Predicted and correct.** `AGENTS.md > Fleet Bootstrap` no longer lists
ProjectTemplate. The hub's own `main` carries it now, so that row was
pre-promotion state rather than fleet drift.
**Predicted and wrong.** I expected `repo-config/configure.sh` to fall
from 9 repos to 7. It fell to **8**. ProjectTemplate dropped off
correctly, but PhotoCleaner is **genuinely stale** rather than an
artifact: #540 moved the canonical, so a copy that was current yesterday
is behind today. That is the intended consequence of changing a
`verbatim` file, flagged in #540 itself and then forgotten when
predicting here.
**Not predicted, and checked rather than assumed.** PhotoCleaner dropped
off five carried-section stale rows and off the `GOVERNANCE.md >
Representative Data` not-carried row. The cause is external to this
work: that repo merged its own promotion `c457ff3` earlier today and its
`main` now carries the section. An unexplained improvement deserves the
same scrutiny as an unexplained regression, so it was verified against
that repo's commits rather than accepted.
## What the report now says about the fleet
- **19 repos owe `AGENTS.md > Fleet Bootstrap`.** The bootstrap shipped
and nothing downstream has it. That is the propagation job #536 exists
to make possible, and it is the honest measure of where the fleet
stands.
- **Financial-Modeling is the only repo still stale on the carried
governance sections**, at 18 rows.
- **`.markdownlint-cli2.jsonc` still lists 16 repos**, because this
session's fleet merges landed on each repo's `develop` and have not
promoted to their own `main`. The ledger reads `main`, correctly, so
these clear as those repos promote.
- **`repo-config/configure.sh` at 8**, five of which additionally need
the `ruleset-*.json` payload migration before they can take the
canonical at all.
## Verification
Regenerated by `python3 spec/fidelity_honesty.py --report` from a live
fleet pass after the promotion merged. editorconfig clean, CRLF
preserved, diff-scoped `prose_lint` clean.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 on `main` and `develop` in 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
`apply` sends the payload with `gh api --method PUT`, which **replaces
the whole document**. A payload with no `bypass_actors` would 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 `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 and none is sent, so a new ruleset starts at
GitHub's own empty default and nothing is deleted, because nothing
existed.
## `check` reports and never asserts
No 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. `check` now prints the live list on every run, the way it
already prints the secrets manual-verify note:
```text
ruleset 'develop' bypass list: RepositoryRole 5 always (not managed by this script)
```
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**:
```text
live bypass: [{"actor_id":5,"actor_type":"RepositoryRole","bypass_mode":"always"}]
payload declares: ABSENT
composed PUT body would send: [{"actor_id":5,"actor_type":"RepositoryRole","bypass_mode":"always"}]
```
`bash -n` clean, `prose_lint` clean, editorconfig clean, live read-only
`check` against 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 `check` now
makes each one visible so they can be reviewed rather than forgotten.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two backlog entries, both raised by the maintainer, both with worked
examples from the session that produced them.
## An agent works in its own checkout
Own directory, own feature branch, and never writes to a repository
because it happened to be on disk.
The reason care is not enough is that **the operations which cross the
boundary are the ordinary ones**. A blanket `git add -A` sweeps another
agent's uncommitted work into your commit. A `git reset --hard` deletes
it. A branch switch carries it into a change it has nothing to do with.
Each command is correct in isolation and wrong only because of who else
is in the directory.
This session produced the first of those. A blanket add swept a second
agent's in-progress onboarding work into an unrelated commit and pushed
it. It survived only because it was rescued and restored by hand
afterwards, and the same directory had already seen a `reset --hard` and
two branch switches that would have destroyed it outright rather than
merely misfiling it.
## The same rule extends to reading, which is the commoner case
A clone on disk is not the branch it names. It is whatever that clone
was last fetched to. Reading it to answer *what does this repository
carry* answers instead *what did this clone last see*, and **the failure
is silent**: the read succeeds and returns a plausible answer.
Two instances, both from this session, both wrong in a way nothing
flagged:
- A detection pass over local clones reported a repository as still
drifted on a file whose fix had already merged, because that clone's
refs predated the merge. The correction came from a `fetch`, not from
the read.
- A second pass reported a repository as missing a file it does carry,
because the local checkout sat on a branch behind the one being
measured.
The entry states the practice alongside the rule: read the live ref
through the API where a claim will be acted on, or fetch immediately
before reading, and name the ref and commit in any finding derived from
a local read so a reader can tell what was actually measured. It also
keeps the honest exception, that a local clone is the right tool for
anything needing history or a build, since an API read cannot give
either.
## Every carried file is a copy that breaks when it is not current
`repo-config/configure.sh` is the worked example, and it happened twice
today. A defect in its check mode was fixed once in the hub and
immediately became a re-vendor owed by every repository carrying it.
Until each takes the fix they run different logic while reporting the
same command, and a repository that misses the sweep **does not fail
loudly** — it quietly audits itself with an older script.
Hosting the tool in the hub and having downstream call it removes the
stale copy rather than detecting it. This is the same argument as the
existing reusable-workflow entry, so the entry says to settle them
together rather than separately. Three things to decide are recorded:
what genuinely needs vendoring against what only needs to be reachable,
how a consumed tool is pinned, since calling hub code at run time is a
floating dependency unless it names a commit, and what a repository does
when the hub is unreachable, since a vendored copy still works offline
and a called one does not.
## Verification
`prose_lint` clean, `markdownlint-cli2` clean, CRLF preserved. One file
in the commit, staged by explicit path rather than a blanket add, for
the reason the first entry describes.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings August 3, 2026 22:38

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Promotes the current develop state to main, with the key behavior change being removal of the ruleset bypass_actors grant from the fleet-managed payloads while preserving any existing live bypass list during configure.sh apply.

Changes:

  • Remove bypass_actors from the committed ruleset payload JSON files so the fleet config no longer grants admin bypass by default.
  • Update repo-config/configure.sh apply to read the live ruleset’s bypass_actors and write it back unchanged on PUT, aborting if the live list cannot be read.
  • Update repo-config/configure.sh check to report (but not assert) the live bypass list for visibility without managing it.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
TODO.mdAdds backlog entries capturing follow-up work around reducing vendored surface area and agent isolation practices.
reports/divergences.mdRegenerates the divergence ledger to reflect the post-promotion baseline.
repo-config/operational/develop.jsonRemoves bypass_actors from the operational develop ruleset payload.
repo-config/main.jsonRemoves bypass_actors from the main ruleset payload.
repo-config/develop.jsonRemoves bypass_actors from the release develop ruleset payload.
repo-config/configure.shPreserves live bypass_actors on update, omits it on create, and reports it in check mode without asserting.

@ptr727
ptr727 merged commit 7194f91 into mainAug 3, 2026
7 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727