Skip to content

fix: a missing org grant reds the repository, not every pull request - #3064

Merged
rbuergi merged 2 commits into
mainfrom
fix/arm-red-is-repo-scoped
Sep 2, 2026
Merged

rbuergi merged 2 commits into
mainfrom
fix/arm-red-is-repo-scoped

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

The red that was on every PR at once

Arm auto-merge on this PR asserted that the meshweaver-cloud App can mint a token carrying
Contents: write + Pull requests: write. That grant is missing, so the check failed on every
open pull request in the repository, simultaneously and permanently.

A signal carries information only to the extent that it varies. This one had variance zero — it
partitioned nothing, exonerated nothing, and the correct reading of it on any individual PR was
"ignore this". Worse, the red was still spent: the wall showed four red PRs, two of which were
entirely green on the required check, while the two with genuine shard failures looked exactly the
same as the two without.

The discriminator

the assertion is about… where it belongs what its red means
this branch's code, tests, build, contracts a per-PR check this PR is not ready
an org installation, a secret, a registry grant, a quota one repo-scoped lane the repository is degraded; every PR equally

An org installation's permissions are a property of the repository. Asserted per-PR they are
duplicated N times and actionable in none of them — nobody fixes an org installation from a pull
request's Checks tab.

What this does

The assertion moved; it was not dropped. arm-credential.yml mints the same token with the same
two permissions — four times a day, on dispatch, and on any change to the arm lane — and fails
exactly as red, naming the grant and the acceptance step that gets forgotten (a
requested-but-unaccepted permission reads as requested in the UI and is not granted to the token).
It then proves the minted token can actually push to this repo, which the mint alone does not: an
installation can lose access to a repository it was scoped to.

auto-arm.yml degrades honestly. A failed mint leaves the PR unarmed with a ::warning:: and a
job summary saying to merge it by hand. The arm step is skipped, not run without a token — falling
through to the runner's default credential is the #2916 outage exactly.

Why this is not the skip-trapdoor AGENTS.md forbids

The rule exists because a verification that silently does not run is indistinguishable from one that
passed. Arming is an action, not a gate. It asserts nothing about the pull request: every
required context still runs, Consolidate test results still decides, and the honest per-PR
consequence of a missing grant is "this PR was not armed, merge it by hand" — a lost convenience,
not a lost check. Reporting that as a failure of the pull request was a false statement about the
pull request, made on all of them at once.

The dangerous form is untouched and still enforced unconditionally at build time:
ArmedMergeMustTriggerMainsPushLanesGuard still fails if any workflow arms or merges with
secrets.GITHUB_TOKEN, still requires the mint, and still requires both permissions be named.

The coupling, because a hoist that loses its destination IS a trapdoor

The tolerance in auto-arm.yml is only defensible while the companion lane exists. Delete
arm-credential.yml and the mint fails, the arm is skipped, the job is green, and PRs quietly stop
landing with no red anywhere. So the two files are coupled by a test rather than by a comment:

ToleratingAFailedMintRequiresARepoScopedAssertion fails the build if auto-arm.yml tolerates a
failed mint while arm-credential.yml is missing, carries continue-on-error itself, loses its
schedule, or stops naming both permissions.

Mutation-proven — the guard is not vacuous:

mutation result
baseline ✅ 3 passed
delete arm-credential.yml ❌ caught
preflight tolerates its own failure ❌ caught
preflight drops its schedule ❌ caught

MeshWeaver.Documentation.Test: 177 passed, 0 failed (whole project, not a filtered run).

What still needs a human

This PR removes the false red. It does not create the grant — that is org-admin action and remains
yours: set Pull requests: Read and write on the App, then accept it on the org installation.
arm-credential.yml stays red until both halves are done, which is the point.

Closes #3063

🤖 Generated with Claude Code

https://claude.ai/code/session_01SD7aiLSo59xng2TzU32xEa

`Arm auto-merge on this PR` asserted that the meshweaver-cloud App can mint a
token with Contents:write + PullRequests:write. The grant is missing, so the
check failed on every open PR at once, permanently. That is variance zero: it
partitioned nothing and exonerated nothing, and the correct reading of it on any
individual PR was "ignore this" — while two of the four reds on the wall were
genuine shard failures the noise was hiding.

The discriminator is whether the asserted fact belongs to the pull request or to
the repository. An org installation's permissions belong to the repository: the
assertion is duplicated N times per-PR and actionable in none of them, because
nobody fixes an org installation from a PR's Checks tab.

So the assertion MOVED, it was not dropped. `arm-credential.yml` mints the same
token with the same two permissions, four times a day and on any change to the
arm lane, and fails exactly as red — naming both the grant and the acceptance
step that gets forgotten. It also proves the minted token can actually push to
this repo, which the mint alone does not: an installation can lose access to a
repository it was scoped to.

`auto-arm.yml` now degrades honestly. A failed mint leaves the PR unarmed with a
warning and a job summary saying to merge by hand, and the arm step is skipped
rather than falling through to the runner's default credential — that fallback
is the #2916 outage exactly. This is not the skip-trapdoor AGENTS.md forbids:
arming is an action, not a gate. It asserts nothing about the branch, every
required context still runs, and the honest per-PR consequence of a missing
grant is a lost convenience, not a lost check.

The tolerance is only safe while the companion lane exists, so the two files are
coupled by a test rather than by prose.
ToleratingAFailedMintRequiresARepoScopedAssertion fails the build if auto-arm
tolerates a failed mint while arm-credential.yml is missing, tolerates its own
failure, or loses its schedule. Proven by mutation: all three caught.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SD7aiLSo59xng2TzU32xEa
Copilot AI lite review requested due to automatic review settings September 2, 2026 07:21

Copilot AI 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.

🟢 Approval recommended

The repo-scoped assertion is preserved (and scheduled) while the per-PR workflow now degrades safely with clear warnings and a test that prevents the tolerance from becoming a silent trapdoor.

Pull request overview

This PR moves a repo-scoped GitHub App permission assertion out of the per-PR auto-merge arming workflow, so a missing org installation grant no longer produces an identical, permanent red on every open pull request while still failing loudly at the repository level.

Changes:

  • Add a new repo-scoped workflow (arm-credential.yml) that periodically mints and validates the App installation token (including required permissions) and fails red when misconfigured.
  • Update auto-arm.yml to tolerate a failed mint (emit warnings and skip arming) so PRs aren’t falsely marked failed for a repository-wide configuration problem.
  • Add a guard test and documentation clarifying the “variance zero” anti-signal and coupling the tolerated mint failure to the presence of the repo-scoped assertion lane.
File summaries
File Description
test/MeshWeaver.Documentation.Test/ArmedMergeMustTriggerMainsPushLanesGuard.cs Adds a test to enforce that tolerating a failed mint in auto-arm.yml is only allowed if a scheduled, repo-scoped assertion workflow exists and remains strict.
src/MeshWeaver.Documentation/Data/Architecture/ReadingCiSignals.md Documents why checks that are red on every PR indicate repo-scoped degradation, and how to triage by context rather than rollup color.
.github/workflows/auto-arm.yml Changes the mint step to continue-on-error, downgrades failure to warnings + step summary, and skips arming unless the mint succeeded to avoid falling back to default credentials.
.github/workflows/arm-credential.yml Introduces a scheduled/dispatch/push-on-change repo-scoped workflow that asserts the App token mint + repo access, failing red when the org installation grant is missing or unusable.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

602 tests  ±0   602 ✅ ±0   45s ⏱️ -7s
  2 suites ±0     0 💤 ±0 
  2 files   ±0     0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

231 tests  ±0   231 ✅ ±0   2m 26s ⏱️ -14s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

540 tests  +1   538 ✅ +1   1m 37s ⏱️ +2s
  4 suites ±0     2 💤 ±0 
  4 files   ±0     0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    3 files  ±0      3 suites  ±0   1m 12s ⏱️ -3s
1 226 tests ±0  1 034 ✅ ±0  192 💤 ±0  0 ❌ ±0 
1 227 runs  ±0  1 035 ✅ ±0  192 💤 ±0  0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

421 tests  ±0   421 ✅ ±0   1m 21s ⏱️ +2s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 780 tests  +5   1 780 ✅ +5   2m 4s ⏱️ -5s
    3 suites ±0       0 💤 ±0 
    3 files   ±0       0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   14 files  ±0     14 suites  ±0   9m 28s ⏱️ -25s
4 800 tests +6  4 606 ✅ +6  194 💤 ±0  0 ❌ ±0 
4 801 runs  +6  4 607 ✅ +6  194 💤 ±0  0 ❌ ±0 

Results for commit 457b3ea. ± Comparison against base commit 7186907.

♻️ This comment has been updated with latest results.

Coordination with the fleet-wide 45-minute hard cap landing in parallel
(check-workflow-timeouts.py refuses any job without a literal timeout-minutes
<= 45). This lane is new in this PR, so that sweep cannot see it, and main would
go red the moment both merged.

Ten rather than forty-five: this is four API calls and a token mint. A lane whose
job is to assert a credential must not be able to occupy a billed runner because
a mint hung on a socket.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi merged commit 0136ce9 into main Sep 2, 2026
25 of 26 checks passed
@rbuergi
rbuergi deleted the fix/arm-red-is-repo-scoped branch October 24, 2026 14:13
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.

A missing org grant reds every PR at once — hoist repo-scoped assertions out of the per-PR arm lane

2 participants