Skip to content

ci: detect commits reaching a default branch without a pull request - #28

Merged
jdwillmsen merged 2 commits into
mainfrom
ci/main-attribution
Aug 4, 2026
Merged

ci: detect commits reaching a default branch without a pull request#28
jdwillmsen merged 2 commits into
mainfrom
ci/main-attribution

Conversation

@jdwillmsen

Copy link
Copy Markdown
Member

Relates to JDWLABS-210.

What

Two halves of the same deliverable:

  • docs/branch-protection-bypass.md — records the live bypass_actors state across all five repositories and the reasoning behind each entry.
  • .github/workflows/main-attribution.yml — a daily job reporting any commit on a default branch that no merged pull request accounts for.

Why

JDWLABS-210 asked for the OrganizationAdmin: always bypass to be recorded as an accepted, justified cost rather than left looking like an oversight, and for a detection check asserting PR-attributability.

The bypass is load-bearing. Production Gates requires an approving review, CODEOWNERS is a catch-all for a single maintainer, and GitHub does not let an author approve their own pull request — so removing it does not tighten the rule, it stops all merges. The doc says so, and also names the narrowing that is available today: pull_request mode keeps the merge path working while making direct pushes to main impossible. That is recorded as the next step, not applied here, because the merge path is the one thing that must not break and the change is only safe to make immediately before a merge that can verify it.

Attributability, not signedness

.github#19 gated the signed tip of each default branch and was closed. After required_signatures came off main (unsatisfiable under rebase-only merges), an unsigned tip is the intended state, so that job would have gone red daily reporting correct behaviour.

The pull request association is the property that survives the merge. Verified against live history before the workflow was written — every rebase-merged commit on apps main still resolves to its PR:

318e5bc2  1  MERGED   chore(usersrole): cache only the build artifact
7928eb1f  0           chore(release): publish [skip ci]      <- the one exception
b176608d  1  MERGED   style(theming): wrap the over-width container role declarations

Scope: apps is excluded, deliberately

apps Baseline still carries Integration/4065387=always, unlike deployments which was narrowed to pull_request. nx release has to land the version commit before the release tags can point at it, and no pull request fits inside that.

Including apps would mean exempting that commit by committer name and message prefix — fields the commit supplies about itself, which is the same hole verify-pr-signatures.yml already refuses to open. A check expected to be red every release is also a check that stops being read. Re-sourcing versions from the tags (JDWLABS-273) removes the bypass; removing it and adding apps here are one change.

Window, not cutoff

Two classes of unattributable commit sit in history and are not findings: the direct-push chart bumps on deployments predating the move to pull requests, and everything on infrastructure older than the GPG re-sign, which rewrote every SHA and detached them all. A rolling 7-day window ages both out; a fixed cutoff reports them forever. The daily schedule means a finding has to be missed seven times running to be missed at all.

Verification

Run locally against live history:

Inspecting commits since 2026-07-28T04:25:54Z

  deployments      main: 61 commit(s), all attributable
  platform         main: 62 commit(s), all attributable
  infrastructure   main: 27 commit(s), all attributable
  .github          main: 17 commit(s), all attributable

main attribution: 167 commit(s) across 4 repositories, 0 issues — passed
EXIT=0

Failure paths exercised rather than assumed:

Path Result
Window widened to 10 days on deployments exit 1, 8 commits listed with URLs and committers
Window widened to 120 days on platform exit 1, page-cap truncation refused rather than partially graded
Non-existent repository exit 1, read failure refused rather than passing on an empty node list
Current 7-day window, all four repos exit 0

actionlint clean; shellcheck -s bash clean on the extracted step body.

🤖 Generated with Claude Code

jdwillmsen and others added 2 commits August 3, 2026 23:26
Every ruleset export carries a bypass_actors list, and a bypass with no
stated reason reads as an oversight. Records the live state across all
five repositories and the reasoning behind each entry.

The OrganizationAdmin bypass is deliberate and load-bearing: Production
Gates requires an approving review, CODEOWNERS is a catch-all for the
single maintainer, and GitHub does not let an author approve their own
pull request. Without the bypass the requirement is not stricter, it is
unmergeable — so the alternative would have been deleting the review
rule, which is worse because it would also stop applying once a second
reviewer exists.

Also names the narrowing that is available now (pull_request mode keeps
the merge path while blocking direct pushes) rather than presenting the
current mode as the only option, and separates the release App's
bypass on apps as temporary and ticketed rather than accepted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The OrganizationAdmin bypass cannot be enforced at push time without
making the repositories unmergeable, so the remaining control is
detection: an exercised bypass should be visible rather than silent.

Gates on pull request attributability rather than signature. The earlier
attempt gated the signed tip of each default branch and had to be
abandoned — rebase merges are re-created server-side and never signed,
so once required_signatures came off main an unsigned tip became the
intended state and the job would have gone red daily over correct
behaviour. The pull request association survives a rebase merge, which
was verified against live history before this was written.

Covers deployments, platform, infrastructure and this repo. apps is
excluded because its release App still needs an unconditional bypass to
land the version commit ahead of the release tags; exempting that commit
would require an allowlist keyed on committer name and subject line,
both of which the commit supplies about itself.

The window rolls rather than anchoring to a fixed date. The pre-fix
chart bumps on deployments and every commit predating the GPG re-sign
are unattributable for reasons already understood, and a fixed cutoff
would report them forever until nobody read the job at all.

Verified against live history: passes clean over the current 7-day
window across all four repositories, exits 1 with commit URLs when the
window is widened to reach the pre-fix bumps, and refuses to pass rather
than grading partial output on both API read failure and page-cap
truncation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jdwillmsen

Copy link
Copy Markdown
Member Author

Merged via organization-admin bypass. Human review performed and explicitly confirmed by the maintainer; GitHub does not permit self-approval, which is why OrganizationAdmin: always remains load-bearing for a single-maintainer org. All required status checks green at merge time.

@jdwillmsen
jdwillmsen merged commit c133e3b into main Aug 4, 2026
5 checks passed
@jdwillmsen
jdwillmsen deleted the ci/main-attribution branch August 4, 2026 04:59
Sign up for free to 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.

1 participant