chore(mirror): set-adopter-pin pushes the pin to main instead of opening a PR that needs a human merge #1524

Closed
opened 2026-10-02 22:21:13 +02:00 by claude · 1 comment
Member

Measured

After the v0.64.0 mirror publish, mirror-release.yml's set-adopter-pin job did its job: rt readme-pin-check --fix moved every adopter pin @v0.63.0 → @v0.64.0 and opened #1519 as release-bot at 2026-10-02 20:49. Every one of its 30 status contexts was green.

It then sat until a human merged it by hand (alex, 22:08, zero review rows — an admin bypass, since apply_to_admins: false). For those 79 minutes readme-pin-check / mirror failed on every other PR graded in that window, for a reason none of them caused. That cost is written into readme-pin-check.yml's own header as consequence 1.

Live main branch protection, read 2026-10-02:

field value
required_approvals 1
enable_push_whitelist / push_whitelist_usernames true / ["release-bot"]
apply_to_admins false
required contexts include readme-pin-check / digest; not readme-pin-check / mirror

release-bot already pushes to main directly under this rule: 63134a1 and aa6f603 (v0.64.0 post-cut bookkeeping) are both authored and committed by release-bot, with no PR. So the credential and the whitelist entry exist and work; the pin job just does not use them.

Why it matters

  • The PR adds a human step with no judgement in it. The value is computed by the same code that grades it, and the digest gate runs before anything is published. The reviewer can only confirm what the job already checked.
  • Until someone merges it, readme-pin-check / mirror is red on every unrelated PR. That teaches people to ignore that context, and it is the only context that tells them the README names something a stranger cannot fetch.
  • release-bot cannot approve its own PR (Forgejo blocks self-approval globally, AGENTS.md section 8), so the PR path can never finish without a person.

Proposal

Revised 2026-10-02 (agreed with alex in session; first draft below the rule). The pin lands by the same path-alpha / path-gamma rule as the cut's manifest commit (ADR-0007), not by a switch of its own.

  • path-alpha: RELEASE_TOOLKIT_TOKEN is set and rt preflight-push-whitelist alpha does not refuse → fast-forward push to the default branch as release-bot, then read it back.
  • path-gamma: the preflight refuses, or the push is rejected → open the PR with the same PAT, as #1378 did.
  • neither: no PAT → refuse. The automatic token can neither push to protected main (#78) nor open a PR that gets graded (#1393), so there is no working route to fall back to.

One difference from the manifest: a refused alpha falls back to gamma instead of failing. The cut must not leave a release half-done, but a pin that lands later by PR is still correct.

Why not a repo variable (opt-in, PR by default)? Considered and declined. The manifest fork has no variable; its switch is the credential. In this repo the same PAT already pushes the post-cut commits to main, so an opt-in would protect nothing, and two rules for the same fork are easy to confuse.

Both paths re-grade the rewritten tree (readme-pin-check without --fix, and tag-action-digest-check) before the commit. On alpha, the push: main runs grade the commit after it lands.


First draft (superseded): push-only, retiring the PR path.

Acceptance criteria

  • set-adopter-pin takes the PAT as RELEASE_TOKEN_OVERRIDE (the name the credential resolver reads), and no step reads secrets.GITHUB_TOKEN. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • Without RELEASE_TOOLKIT_TOKEN the job fails with an ::error:: that names the secret and the manual remedy. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • The commit is built on the live default-branch tip, not on the dispatch ref. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • Before the commit, the job re-grades the written documents with readme-pin-check (no --fix) and tag-action-digest-check; either one failing stops the job. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • Path choice: rt preflight-push-whitelist alpha passing → push; refusing → PR with no push attempt; push rejected → PR. Pinned by an arm that runs the real fragment. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • Alpha: the push is non-force, the token never appears in argv (git and curl both read 0600 files), and the job reads the branch back and refuses on a mismatch. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • Gamma keeps #1394's body file and #1393's zero-status refusal, both still graded. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • readme-pin-check.yml's consequence-1 comment describes both windows. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
  • .gitea twin regenerated; rt gitea-twin --check passes. — DONE @ #1525 (dd871e8, reviewed as dd871e8)
## Measured After the v0.64.0 mirror publish, `mirror-release.yml`'s `set-adopter-pin` job did its job: `rt readme-pin-check --fix` moved every adopter pin `@v0.63.0` → `@v0.64.0` and opened #1519 as `release-bot` at 2026-10-02 20:49. Every one of its 30 status contexts was green. It then sat until a human merged it by hand (alex, 22:08, zero review rows — an admin bypass, since `apply_to_admins: false`). For those 79 minutes `readme-pin-check / mirror` failed on every other PR graded in that window, for a reason none of them caused. That cost is written into `readme-pin-check.yml`'s own header as consequence 1. Live `main` branch protection, read 2026-10-02: | field | value | |---|---| | `required_approvals` | `1` | | `enable_push_whitelist` / `push_whitelist_usernames` | `true` / `["release-bot"]` | | `apply_to_admins` | `false` | | required contexts | include `readme-pin-check / digest`; **not** `readme-pin-check / mirror` | `release-bot` already pushes to `main` directly under this rule: `63134a1` and `aa6f603` (v0.64.0 post-cut bookkeeping) are both authored and committed by `release-bot`, with no PR. So the credential and the whitelist entry exist and work; the pin job just does not use them. ## Why it matters - The PR adds a human step with no judgement in it. The value is computed by the same code that grades it, and the digest gate runs before anything is published. The reviewer can only confirm what the job already checked. - Until someone merges it, `readme-pin-check / mirror` is red on every unrelated PR. That teaches people to ignore that context, and it is the only context that tells them the README names something a stranger cannot fetch. - `release-bot` cannot approve its own PR (Forgejo blocks self-approval globally, AGENTS.md section 8), so the PR path can never finish without a person. ## Proposal > **Revised 2026-10-02** (agreed with alex in session; first draft below the rule). The pin lands by the **same path-alpha / path-gamma rule as the cut's manifest commit** (ADR-0007), not by a switch of its own. - **path-alpha:** `RELEASE_TOOLKIT_TOKEN` is set and `rt preflight-push-whitelist alpha` does not refuse → fast-forward push to the default branch as `release-bot`, then read it back. - **path-gamma:** the preflight refuses, or the push is rejected → open the PR with the same PAT, as #1378 did. - **neither:** no PAT → refuse. The automatic token can neither push to protected `main` (#78) nor open a PR that gets graded (#1393), so there is no working route to fall back to. One difference from the manifest: a refused alpha **falls back** to gamma instead of failing. The cut must not leave a release half-done, but a pin that lands later by PR is still correct. Why not a repo variable (opt-in, PR by default)? Considered and declined. The manifest fork has no variable; its switch is the credential. In this repo the same PAT already pushes the post-cut commits to `main`, so an opt-in would protect nothing, and two rules for the same fork are easy to confuse. Both paths re-grade the rewritten tree (`readme-pin-check` without `--fix`, and `tag-action-digest-check`) before the commit. On alpha, the `push: main` runs grade the commit after it lands. --- *First draft (superseded):* push-only, retiring the PR path. ## Acceptance criteria - [x] `set-adopter-pin` takes the PAT as `RELEASE_TOKEN_OVERRIDE` (the name the credential resolver reads), and no step reads `secrets.GITHUB_TOKEN`. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] Without `RELEASE_TOOLKIT_TOKEN` the job fails with an `::error::` that names the secret and the manual remedy. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] The commit is built on the live default-branch tip, not on the dispatch ref. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] Before the commit, the job re-grades the written documents with `readme-pin-check` (no `--fix`) and `tag-action-digest-check`; either one failing stops the job. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] Path choice: `rt preflight-push-whitelist alpha` passing → push; refusing → PR with no push attempt; push rejected → PR. Pinned by an arm that runs the real fragment. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] Alpha: the push is non-force, the token never appears in argv (git and curl both read 0600 files), and the job reads the branch back and refuses on a mismatch. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] Gamma keeps #1394's body file and #1393's zero-status refusal, both still graded. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] `readme-pin-check.yml`'s consequence-1 comment describes both windows. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`) - [x] `.gitea` twin regenerated; `rt gitea-twin --check` passes. — DONE @ #1525 (`dd871e8`, reviewed as `dd871e8`)
Author
Member

Landed via #1525: fast-forward merge pinned to the approved head, so main = dd871e878a476038fff8cf4d31ff34402b5c0806 = the reviewed head (no merge commit).

  • Approval: alex, review 7205, on dd871e8. The code review is in review 7204 (bound to dd871e8, no open findings); finding 4 of review 7203 was declined and the decline accepted.
  • Checks: combined success, 30/30 contexts on dd871e8 before merge. ac-closure-check and a scan of every PR comment, review body and the title found no closing keyword.
  • All 9 acceptance criteria are ticked above.

Not yet exercised live: set-adopter-pin runs only after a mirror-release.yml dispatch. The next mirror publish is its first real run. Look for adopter-pin path=alpha: pushed … (expected here, since release-bot is on main's push whitelist) or adopter-pin path=gamma: PR #….

Closing by hand.

Landed via #1525: fast-forward merge pinned to the approved head, so `main` = `dd871e878a476038fff8cf4d31ff34402b5c0806` = the reviewed head (no merge commit). - Approval: alex, review 7205, on `dd871e8`. The code review is in review 7204 (bound to `dd871e8`, no open findings); finding 4 of review 7203 was declined and the decline accepted. - Checks: combined `success`, 30/30 contexts on `dd871e8` before merge. `ac-closure-check` and a scan of every PR comment, review body and the title found no closing keyword. - All 9 acceptance criteria are ticked above. Not yet exercised live: `set-adopter-pin` runs only after a `mirror-release.yml` dispatch. The next mirror publish is its first real run. Look for `adopter-pin path=alpha: pushed …` (expected here, since release-bot is on main's push whitelist) or `adopter-pin path=gamma: PR #…`. Closing by hand.
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#1524
No description provided.