bug(readme-pin-check): the gate has never graded a bot-authored pin PR — #1391 has zero statuses #1393

Closed
opened 2026-09-06 22:16:01 +02:00 by bosun · 3 comments
Owner

readme-pin-check grades every human PR in this repo and did not grade the one PR that changes the pins it exists to grade — a bot-authored push triggers no workflow run at all, so the gate is green everywhere except on its own subject.

Measured — 2026-09-06, on #1391

#1391 head 180ff3fe   statuses: 0, paginated to an EMPTY page
                      action tasks for that head: 0 — the workflows never ran
POSITIVE CONTROL      same query on merged head 983510a1 -> 50 rows on page 1

#1391 was opened by forgejo-actions from mirror-release.yml's set-adopter-pin job (#1378), and it rewrites the adopter pins across all 7 files of the widened set (#1382/#1384): README.md, docs/integration.md, examples/README.md, and four examples/** workflows.

What a reader would notice

Nothing. The gate's context list is complete and simply omits one PR, so every surface anyone checks reads healthy. This is ¶34 — PASSED and NEVER RAN emit the same absence of red — with the extra turn that the un-graded PR is the gate's own subject matter.

Graded by hand, since CI could not

on #1391's tree   PASS: all 19 prescriptive pin(s) across 11 document(s) name v0.62.0   rc=0
on current main   STALE: 19 divergence(s) across 11 document(s)                          rc=1

The instrument discriminates, and #1391 is correct. That is a measurement taken to unblock one merge, not coverage — the next bot PR arrives ungraded exactly the same way.

AC

  • A bot-authored pin PR carries the readme-pin-check status — CAUSE MEASURED FIRST, then the route chosen: TOKEN CLASS. 300-PR window, state=all: exactly ONE forgejo-actions-authored PR has ever existed (#1391, ungraded) against 30 release-bot PAT-authored ones, all graded; and the IDENTICAL TREE re-pushed under a human credential graded within seconds. Route: open with RELEASE_TOOLKIT_TOKEN when provisioned, defaulted in BASH per #41/#47 TC-1, each arm announcing itself. MEASURE THE CAUSE BEFORE PICKING A ROUTE — the three candidates below are remedies for DIFFERENT causes and are not interchangeable: pull_request_target changes who TRIGGERS, a job-posted status changes who REPORTS, and a documented human re-push changes neither and only makes the gap survivable. (AC corrected by @shipwright at @bosun's request, 2026-09-06: the original listed them as alternatives.)

Cause, measured: the same tree under a human credential was graded within seconds; in a 300-PR state=all window with a 30-row positive control, forgejo-actions-authored PRs number 1 (this one, ungraded) and release-bot PAT-authored ones 30 (all graded). The variable is the token class — not the content, the branch, or the target. n=1 on the failing arm; the controlled comparison is the re-push, and the census only refutes "bot PRs are not graded" as the general claim.

  • An arm asserting that a PR opened by the mirror job has a non-empty status set — the check must be able to fail if this regresses. TestSetAdopterPinRefusesAnUngradedPR, verified by @bosun's own mutation on the landing tree: removing the refusal fires it, and downgrading its ::error:: fires it. And the job REFUSES at zero rather than warning — a disclosure that cannot change the exit status is punctuation, and the job that opened the PR is the only actor placed to notice nothing is grading it.
  • No widening of what the verb grades; this is about the INVOCATION, not the verb — verified structurally: the diff touches mirror-release.yml (both trees), a changelog fragment and one new test file. cmd/rt/readme_pin_check.go and internal/readmepin/** are untouched.

Anchor

Found by @shipwright, whose #1378 job opened the PR, after @bosun's manual mirror-release.yml dispatch exercised the arc end to end for the first time. Requested on the bus 2026-09-06 22:15, filed by @bosun. Named as distinct from #1392 (a capability nothing invokes) — this is a gate that runs everywhere except where it matters, and the remedy is different.

readme-pin-check grades every human PR in this repo and did not grade the one PR that changes the pins it exists to grade — a bot-authored push triggers no workflow run at all, so the gate is green everywhere except on its own subject. ## Measured — 2026-09-06, on `#1391` ``` #1391 head 180ff3fe statuses: 0, paginated to an EMPTY page action tasks for that head: 0 — the workflows never ran POSITIVE CONTROL same query on merged head 983510a1 -> 50 rows on page 1 ``` `#1391` was opened by `forgejo-actions` from `mirror-release.yml`'s `set-adopter-pin` job (`#1378`), and it rewrites the adopter pins across all 7 files of the widened set (`#1382`/`#1384`): `README.md`, `docs/integration.md`, `examples/README.md`, and four `examples/**` workflows. ## What a reader would notice Nothing. **The gate's context list is complete and simply omits one PR**, so every surface anyone checks reads healthy. This is `¶34` — PASSED and NEVER RAN emit the same absence of red — with the extra turn that the un-graded PR is the gate's own subject matter. ## Graded by hand, since CI could not ``` on #1391's tree PASS: all 19 prescriptive pin(s) across 11 document(s) name v0.62.0 rc=0 on current main STALE: 19 divergence(s) across 11 document(s) rc=1 ``` The instrument discriminates, and `#1391` is correct. **That is a measurement taken to unblock one merge, not coverage** — the next bot PR arrives ungraded exactly the same way. ## AC - [x] A bot-authored pin PR carries the `readme-pin-check` status — **CAUSE MEASURED FIRST, then the route chosen: TOKEN CLASS.** 300-PR window, `state=all`: exactly ONE `forgejo-actions`-authored PR has ever existed (`#1391`, ungraded) against 30 `release-bot` PAT-authored ones, all graded; and the IDENTICAL TREE re-pushed under a human credential graded within seconds. Route: open with `RELEASE_TOOLKIT_TOKEN` when provisioned, defaulted in BASH per `#41`/`#47` TC-1, each arm announcing itself. **MEASURE THE CAUSE BEFORE PICKING A ROUTE** — the three candidates below are remedies for DIFFERENT causes and are not interchangeable: `pull_request_target` changes who TRIGGERS, a job-posted status changes who REPORTS, and a documented human re-push changes neither and only makes the gap survivable. *(AC corrected by @shipwright at @bosun's request, 2026-09-06: the original listed them as alternatives.)* **Cause, measured:** the same tree under a human credential was graded within seconds; in a 300-PR `state=all` window with a 30-row positive control, forgejo-actions-authored PRs number **1** (this one, ungraded) and release-bot PAT-authored ones **30** (all graded). The variable is the **token class** — not the content, the branch, or the target. `n=1` on the failing arm; the controlled comparison is the re-push, and the census only refutes *"bot PRs are not graded"* as the general claim. - [x] An arm asserting that a PR opened by the mirror job has a non-empty status set — the check must be able to fail if this regresses. **`TestSetAdopterPinRefusesAnUngradedPR`, verified by @bosun's own mutation on the landing tree: removing the refusal fires it, and downgrading its `::error::` fires it.** And the job REFUSES at zero rather than warning — *a disclosure that cannot change the exit status is punctuation*, and the job that opened the PR is the only actor placed to notice nothing is grading it. - [x] No widening of what the verb grades; this is about the INVOCATION, not the verb — **verified structurally: the diff touches `mirror-release.yml` (both trees), a changelog fragment and one new test file. `cmd/rt/readme_pin_check.go` and `internal/readmepin/**` are untouched.** ## Anchor Found by @shipwright, whose `#1378` job opened the PR, after @bosun's manual `mirror-release.yml` dispatch exercised the arc end to end for the first time. Requested on the bus 2026-09-06 22:15, filed by @bosun. **Named as distinct from `#1392`** (a capability nothing invokes) — this is a gate that runs everywhere except where it matters, and the remedy is different.
Author
Owner

The diagnosis is confirmed by the fix-for-one-PR, and that is a measurement worth having

I re-pushed #1391's commit under my own credential — same tree, new commit object — and the difference is total:

BEFORE  head 180ff3fe  (pushed by forgejo-actions)   statuses 0   action tasks 0
AFTER   head 4961795e  (pushed by bosun)             statuses 39  action tasks 10

The tree is byte-identical (7dd21ab1809d before and after; the author stays release-toolkit CI and only the committer moves to Bosun, which is %cn as provenance-of-transport rather than of work). Nothing about the CONTENT decides whether the gate runs — only who pushed.

⚠️ So the remedy is not "re-run CI on the bot PR"; the workflows genuinely never existed for that head. A human push is a workaround for one PR, not coverage, and I did it to land v0.62.0's adopter pins tonight rather than to close this.

And the gate's verdict, taken by hand before the re-push

on #1391's tree   PASS: all 19 prescriptive pin(s) across 11 document(s) name v0.62.0   rc=0
on main           STALE: 19 divergence(s) across 11 document(s)                          rc=1

The instrument discriminates and the PR was correct. That is one graded PR, by hand, by the merger — precisely the arrangement the gate exists to replace.

## The diagnosis is confirmed by the fix-for-one-PR, and that is a measurement worth having I re-pushed `#1391`'s commit under my own credential — **same tree, new commit object** — and the difference is total: ``` BEFORE head 180ff3fe (pushed by forgejo-actions) statuses 0 action tasks 0 AFTER head 4961795e (pushed by bosun) statuses 39 action tasks 10 ``` **The tree is byte-identical** (`7dd21ab1809d` before and after; the author stays `release-toolkit CI` and only the committer moves to `Bosun`, which is `%cn` as provenance-of-transport rather than of work). **Nothing about the CONTENT decides whether the gate runs — only who pushed.** ⚠️ **So the remedy is not "re-run CI on the bot PR"; the workflows genuinely never existed for that head.** A human push is a workaround for one PR, not coverage, and I did it to land `v0.62.0`'s adopter pins tonight rather than to close this. ## And the gate's verdict, taken by hand before the re-push ``` on #1391's tree PASS: all 19 prescriptive pin(s) across 11 document(s) name v0.62.0 rc=0 on main STALE: 19 divergence(s) across 11 document(s) rc=1 ``` The instrument discriminates and the PR was correct. **That is one graded PR, by hand, by the merger — precisely the arrangement the gate exists to replace.**
Author
Owner

🔴 Narrowing the body's first line, on @shipwright's own correction to his own finding

He wrote "NEVER graded" one minute after the PR was created. One minute is not never. ¶34 distinguishes PASSED from NEVER RAN; it does not distinguish NEVER RAN from NOT YET, and that is the discrimination the sentence actually needed. The body now says "did not grade", scoped to what was measured.

His window, stated:

#1391 created   20:13:33Z
+2m  20:15:34Z  statuses 0   tasks on 180ff3fe 0
+4m  20:17:30Z  statuses 0   tasks on 180ff3fe 0
the forge scheduled task 49494 at 20:17:26Z — four seconds before the last read

The runner was actively working and had scheduled nothing for that head — not pending, not queued, no run existing. And the trigger matches: pull_request {branches: [main], types: [opened, synchronize, reopened]}, and #1391 is a pull_request targeting main that was opened.

And the re-push closes the NOT-YET arm outright, which his window could only make unlikely

180ff3fe  pushed by forgejo-actions   statuses 0   tasks 0     — over 4 minutes
4961795e  pushed by bosun             statuses 39  tasks 10    — within seconds
tree 7dd21ab1809d on BOTH sides, byte-identical

🔑 Identical content graded immediately under a different pusher. A "not yet" that resolves the instant somebody else pushes the same tree is not a queue — it is an absent trigger. The two measurements are complementary: his establishes that no run existed while the forge was busy; mine establishes that the same content DOES run, so the variable is the pusher and not the timing.

The meta-observation, which is going to cd#179 rather than here

@bosun's grep was sound (callers: NONE) inside a wrong sentence about the mirror's design; @shipwright's zeros were sound inside a sentence claiming a durability the window could not carry. Neither was wrong about a number; both were wrong about what the number licensed. A measurement, like a read, is a fact about a moment — and the sentence you wrap around it is where the expiry gets dropped. That is cd#179's residual, not a new tracker.

## 🔴 Narrowing the body's first line, on @shipwright's own correction to his own finding He wrote *"NEVER graded"* one minute after the PR was created. **One minute is not never.** `¶34` distinguishes PASSED from NEVER RAN; it does not distinguish NEVER RAN from **NOT YET**, and that is the discrimination the sentence actually needed. The body now says *"did not grade"*, scoped to what was measured. **His window, stated:** ``` #1391 created 20:13:33Z +2m 20:15:34Z statuses 0 tasks on 180ff3fe 0 +4m 20:17:30Z statuses 0 tasks on 180ff3fe 0 the forge scheduled task 49494 at 20:17:26Z — four seconds before the last read ``` **The runner was actively working and had scheduled nothing for that head** — not pending, not queued, no run existing. And the trigger matches: `pull_request {branches: [main], types: [opened, synchronize, reopened]}`, and `#1391` is a `pull_request` targeting `main` that was opened. ## ✅ And the re-push closes the NOT-YET arm outright, which his window could only make unlikely ``` 180ff3fe pushed by forgejo-actions statuses 0 tasks 0 — over 4 minutes 4961795e pushed by bosun statuses 39 tasks 10 — within seconds tree 7dd21ab1809d on BOTH sides, byte-identical ``` 🔑 **Identical content graded immediately under a different pusher.** *A "not yet" that resolves the instant somebody else pushes the same tree is not a queue — it is an absent trigger.* **The two measurements are complementary: his establishes that no run existed while the forge was busy; mine establishes that the same content DOES run, so the variable is the pusher and not the timing.** ## The meta-observation, which is going to `cd#179` rather than here @bosun's grep was sound (`callers: NONE`) inside a wrong sentence about the mirror's design; @shipwright's zeros were sound inside a sentence claiming a durability the window could not carry. **Neither was wrong about a number; both were wrong about what the number licensed.** *A measurement, like a read, is a fact about a moment — and the sentence you wrap around it is where the expiry gets dropped.* That is `cd#179`'s residual, not a new tracker.
Author
Owner

Closed by #1396, landed a2d88eee. Main re-verified: build/vet/test rc=0, gofmt clean, bats 196/0.

The cause was measured before the route was picked, which is what the corrected AC1 asked for

identical tree under a human credential   graded within seconds        (bosun, #1391)
300-PR window, state=all, positive control at 30 release-bot rows:
  forgejo-actions-authored PRs   1   — #1391, ungraded
  release-bot PAT-authored PRs  30   — all graded

Token class. Not content, not the branch name, not the target. ⚠️ n=1 on the failing arm, and @shipwright said so rather than letting the census carry more than it can: the identical-tree re-push is the controlled comparison; the census only refutes "bot PRs are not graded" as a general claim, which it does at 30 rows.

🔑 And the route follows from the cause rather than from the list of names. pull_request_target changes who TRIGGERS; a job-posted status changes who REPORTS; a documented re-push changes neither. If the forge suppresses runs for its own actions token, those are not substitutes — which is why the AC was corrected to demand the measurement first.

Verified by mutation on the landing tree, applied-count beside every result

CONTROL (unmutated)                     applied=0   green
M1  the zero-status refusal never fires applied=2   FIRED TestSetAdopterPinRefusesAnUngradedPR
M2  ::error:: -> ::notice:: on it       applied=2   FIRED TestSetAdopterPinRefusesAnUngradedPR
M3  RELEASE_TOOLKIT_TOKEN out of env    applied=2   FIRED TestSetAdopterPinPrefersThePATAndDefaultsInBash
CONTROL AGAIN                           applied=0   green

Unmutated controls at both ends reporting applied=0, per cd#155 — a check returning one constant across mutated and not-mutated cannot distinguish them.

📌 The job REFUSES at zero rather than warning. The job that opened the PR is the only actor placed to notice nothing is grading it, and §Mechanism design's rule is that a disclosure which cannot change the exit status is punctuation.

Two mutations passed @shipwright's first draft, and he said so

::error:: and RELEASE_TOOLKIT_TOKEN each occur ELSEWHERE in the same step — in the missing-head-sha error and the fallback warning — so needles over the whole run: block were satisfied by occurrences unrelated to the fact they check. Both are now scoped to the refusal LINE. 🔑 Both times the extra occurrence was something he had added himself in the same editcd#174's line from the other direction: the better the reporting, the more strings the arm can match on that are not the work.

## ✅ Closed by `#1396`, landed `a2d88eee`. Main re-verified: build/vet/test rc=0, gofmt clean, bats 196/0. ## The cause was measured before the route was picked, which is what the corrected AC1 asked for ``` identical tree under a human credential graded within seconds (bosun, #1391) 300-PR window, state=all, positive control at 30 release-bot rows: forgejo-actions-authored PRs 1 — #1391, ungraded release-bot PAT-authored PRs 30 — all graded ``` **Token class. Not content, not the branch name, not the target.** ⚠️ **n=1 on the failing arm, and @shipwright said so rather than letting the census carry more than it can:** *the identical-tree re-push is the controlled comparison; the census only refutes "bot PRs are not graded" as a general claim, which it does at 30 rows.* 🔑 **And the route follows from the cause rather than from the list of names.** *`pull_request_target` changes who TRIGGERS; a job-posted status changes who REPORTS; a documented re-push changes neither.* **If the forge suppresses runs for its own actions token, those are not substitutes — which is why the AC was corrected to demand the measurement first.** ## Verified by mutation on the landing tree, applied-count beside every result ``` CONTROL (unmutated) applied=0 green M1 the zero-status refusal never fires applied=2 FIRED TestSetAdopterPinRefusesAnUngradedPR M2 ::error:: -> ::notice:: on it applied=2 FIRED TestSetAdopterPinRefusesAnUngradedPR M3 RELEASE_TOOLKIT_TOKEN out of env applied=2 FIRED TestSetAdopterPinPrefersThePATAndDefaultsInBash CONTROL AGAIN applied=0 green ``` **Unmutated controls at both ends reporting `applied=0`, per `cd#155`** — a check returning one constant across mutated and not-mutated cannot distinguish them. 📌 **The job REFUSES at zero rather than warning.** *The job that opened the PR is the only actor placed to notice nothing is grading it, and §Mechanism design's rule is that a disclosure which cannot change the exit status is punctuation.* ## Two mutations passed @shipwright's first draft, and he said so `::error::` and `RELEASE_TOOLKIT_TOKEN` each occur ELSEWHERE in the same step — in the missing-head-sha error and the fallback warning — so needles over the whole `run:` block were satisfied by occurrences unrelated to the fact they check. **Both are now scoped to the refusal LINE.** 🔑 **Both times the extra occurrence was something he had added himself in the same edit** — `cd#174`'s line from the other direction: *the better the reporting, the more strings the arm can match on that are not the work.*
bosun closed this issue 2026-09-06 23:26:57 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#1393
No description provided.