chore(mirror): land the adopter pin by the manifest's path-alpha/gamma rule (#1524) #1525
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit!1525
Loading…
Reference in a new issue
No description provided.
Delete branch "claude/1524-pin-direct-push"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs #1524
What changes
mirror-release.yml'sset-adopter-pinjob lands the pin by the same path-alpha / path-gamma rule as the cut's manifest commit (ADR-0007,reusable-release.yml:765), instead of always opening a PR:RELEASE_TOOLKIT_TOKENset,rt preflight-push-whitelist alphapasses / warnsrelease-bot, thenls-remoteread-backrelease-botnot whitelisted)::warning::, no push attempted, PR opened with the PAT, as #1378 didmainmoved, whitelist changed)::warning::, same PRRELEASE_TOOLKIT_TOKEN::error::, exit 1Both paths share the steps before the commit:
readme-pin-check --fix, as before.readme-pin-check, because--fixwrites and reports but does not re-grade, and nothing else grades an alpha commit before it lands.tag-action-digest-check, as before.release-bot.Credentials:
RELEASE_TOKEN_OVERRIDE. Under that nameinternal/credentialsresolves a single-token tier, sort preflight-push-whitelistchecks a user rather than a deploy key. The user it checks comes fromFORGEJO_TOKEN, which the step exports from the same PAT.contents: read.|| code=000,|| statuses=0,|| remote_sha="", and the twojqreads). Underset -e, a failed substitution would otherwise end the step before the message that says what to do (review 7203).Decision tree
main, so an opt-in would protect nothing, and two rules for one fork are easy to confuse. If adopters ever need to choose, the variable belongs on both forks at once.main(#78), and a PR opened with it is never graded (#1393). Without the PAT, neither route can land.recover-pending-cutis that case and is untouched.What alpha gives up: the PR-time required contexts don't run before the pin lands. The
push: mainruns (go-ci,tests,check-self-bootstrap,gitea-twin-check) grade it right after, and the two pin-specific verdicts run inside the job beforehand. #1519, the same kind of mechanical edit, passed all 30 contexts.Carry-forward from the PR-only job (AGENTS.md section 1)
--fix; rc≠0 → warning + exit 0;git diff --quietno-optag-action-digest-checkgate (#1463)-c(#73)-c, nowrelease-bot, matching its other direct pushes (63134a1,aa6f603)contents: readno-changelog:(#1394)::warning::, exit 0::error::, exit 1: the pin did not land, and a green job would hide thatVerification
All of this ran on an LF clone; no workflow ran live.
go test ./...natively on Windows: every packageok.bats tests/workflows.batsinrt-bats:local: 138/138.rt gitea-twin --check: PASS (only themirror-release.ymltwin changed).rt fragment-check(content mode): PASS, no warnings.rt check-self-bootstrap:floating-pin (release-toolkit#456): main matches main by construction, so no re-pin (AGENTS.md section 2.1).TestSetAdopterPinChoosesItsPathByTheManifestRuleruns the real fragment from the workflow under bash, withrtandgitstubbed, in five cases:mirror-release.ymland each turned its targeted arm red. They covered the PAT/fallback, both fallback-to-PR edges, an alpha that falls through to a PR, the preflight being skipped, the token in curl argv, the live-tip detach, the re-grade exit, a forced push, the gamma branch targetingmain, the read-back (two mutants), the zero-status exit, the body being swapped, the four substitution fallbacks, the extraheader unset, and the EXIT trap. The #1463 bats arm also went red with the digestexitremoved.Not verified: the job has not run end to end. It runs only after a
mirror-release.ymldispatch, which publishes to gitea.com, so I did not dispatch it. The next mirror publish is its first real run. Look foradopter-pin path=alpha: pushed …oradopter-pin path=gamma: PR #….Review request: verify at source (AGENTS.md section 6)
Please verify both halves against live state, not this description:
release-botis onmain's push whitelist, andRELEASE_TOOLKIT_TOKENis its PAT with push scope. Also check whetherrt preflight-push-whitelist alphacan read branch protection asrelease-bot. If it can't, it warns and the job proceeds, so the push itself becomes the check. Evidence I used: the 2026-10-02 protection read, #1519 opened byrelease-botwith this secret, and63134a1/aa6f603pushed directly byrelease-bot.no-changelog:body and gets graded. The arms cover this, but nothing has exercised it live since #1519.🤖 Generated with Claude Code
HOLD: reworking this PR per alex's decision in session. Instead of push-only, set-adopter-pin will reuse the manifest commit's path-alpha/path-gamma rule (ADR-0007): push directly when RELEASE_TOOLKIT_TOKEN is set and
rt preflight-push-whitelist alphadoes not refuse; open the PR (with the same PAT) when the preflight refuses or the push is rejected; fail when there is no token. The PR-path pieces removed in96c391dcome back. New commits on this branch, no force-push; I'll post the new head SHA when it is ready for review. Please don't approve96c391d.Reworks the previous commit per review in session: instead of push-only, set-adopter-pin reuses the cut manifest commit's rule (ADR-0007) rather than a switch of its own. path-alpha RELEASE_TOOLKIT_TOKEN set and `rt preflight-push-whitelist alpha` does not refuse -> fast-forward push to main, 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 graded PR (#1393). The PAT is passed as RELEASE_TOKEN_OVERRIDE, the name the credential resolver reads, so the preflight grades the identity that pushes. Both paths re-grade the written tree before the commit. The PR half now authenticates curl from a 0600 header file instead of argv, and a failed PR POST is an error rather than a warning, since the pin did not land. Restores .forgejo/pr-body/adopter-pin.md (plus a path-gamma paragraph), the #1394 body-file arm byte-identical, and the #1393 status-poll arm without its automatic-token fallback check. The path choice is pinned by an arm that executes the real fragment under bash with rt and git stubbed. Refs #1524 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>chore(mirror): set-adopter-pin pushes the pin to main instead of opening a PR (#1524)to chore(mirror): land the adopter pin by the manifest's path-alpha/gamma rule (#1524)HOLD released. Ready for review at head
73fc0bfdb5e2d9ef2d05f70e039546b28efe9f3e(two commits:96c391dpush-only, then73fc0bfreworking it onto the manifest's path-alpha/gamma rule). The description and #1524's acceptance criteria are rewritten for the current design. Local results:go test ./...all ok,tests/workflows.bats138/138, 15/15 workflow mutants caught. Before submitting, pin the review to this head withrt review-submit-check --owner frankenbit --repo release-toolkit --pr 1525 --reviewed-head 73fc0bfdb5e2d9ef2d05f70e039546b28efe9f3e(AGENTS.md section 6.2).HOLD: go-ci / lint fails on
73fc0bf. staticcheck QF1001 (De Morgan) flags three conditions in internal/gates/adopter_pin_push_test.go (lines 178, 196, 285). A lint-only fix follows as a new commit; I'll post the new head. Please don't review73fc0bf.HOLD released. Ready for review at head
ed9db78a15d480add080f875126bd0a2e7f35985. Since73fc0bf, one lint-only commit: the three!(a && b)ordering checks are rewritten asa >= b || b >= c. Local golangci-lint v2.14.0 (CI's flags) reports the same three QF1001 on73fc0bfand 0 issues oned9db78; the gates tests pass and all 15 workflow mutants are still caught. Pin the review withrt review-submit-check --owner frankenbit --repo release-toolkit --pr 1525 --reviewed-head ed9db78a15d480add080f875126bd0a2e7f35985.Verdict: APPROVE at
ed9db78a15d480add080f875126bd0a2e7f35985. This is posted as COMMENT because the forge identity isclaude, the same account that opened the PR, and Forgejo blocks self-approval. @alex has to click Approve. Before submitting,rt review-submit-check --reviewed-head ed9db78a…returned rc=0 (READY).Verify at source (AGENTS.md section 6)
Alpha half: confirmed from live state.
mainbranch protection, read back 2026-10-02:enable_push=trueenable_push_whitelist=truepush_whitelist_usernames=["release-bot"]push_whitelist_deploy_keys=falserelease-bothaswritepermission, so the whitelist entry is not the silent-drop case (section 8, instance 5).release-botfrom this job usingRELEASE_TOOLKIT_TOKEN. So the secret is release-bot's PAT and, being tier 2, the preflight grades the right login.Gamma half: not exercised live. It is covered by the arms: #1394's body file, #1393's zero-status poll (now using the PAT), and the executed path-choice arm (preflight refuses → no push, PR opened; push rejected → PR opened).
What I ran (LF clone at
ed9db78)go test ./internal/gates -run AdopterPin: all 10 arms pass.rt gitea-twin --check: PASS, 11 twins.ed9db78: combined statesuccess, 30/30 contexts.ac-closure-checkregex over the PR body and all three commit messages: no close targets.mirror-release.yml:exit 0removed); gamma branch pushed without the PAT config; preflight called withgamma.trapcleanup. See nit 2.goreleaser.yml's token-config pattern, which runs on the same runner.Non-blocking findings
set -uo→set -euomakes three failure paths exit before the job's own message. In bash, an assignment from a failing command substitution trips errexit. I checked:set -euo pipefail; x="$(false | cat)"exits 1. Each case below still exits non-zero, so the job goes red, but the operator loses the message:code="$(curl … -w '%{http_code}')". On a transport failure (curl exit 7 or 28) the job dies before::error::… The change is on ${branch}; open it by hand. That is the one message that names the branch to recover from.statuses="$(curl … | jq -r 'length')". A single transient failure ends the poll on its first iteration, with no#1393message. Before this PR,[[ =~ ]] || statuses=0absorbed it.remote_sha="$(… git ls-remote … | awk …)"fails, the job exits with no message, after the pin has most likely landed.A
|| true(or|| code=000) on those three substitutions would restore the intended messages.The
git config --local --unset-all "http.${base_url}/.extraheader"line is load-bearing, and no arm pins it. Without it, git sends the automatic token's persisted header alongside the PAT's, and which one the server honours is not established. The precedent ingoreleaser.ymlis unpinned too, so this is a consistency nit, not a regression.The comment on
RELEASE_TOKEN_OVERRIDE(and the PR body) over-credits it. The identity the preflight grades comes fromFORGEJO_TOKEN(GetAuthenticatedUser).RELEASE_TOKEN_OVERRIDEonly selects the credential tier (not split, so the user is checked). Both carry the same PAT, so the behaviour is right. Only the stated reason is off.The read-back can race. If another push lands on
mainbetween the alpha push andls-remote, the job reportsread-back mismatchand exits 1 even though the pin landed. This is rare, and failing loud is the safe direction. An ancestry check after a fetch would remove the false alarm, if you think it's worth it.None of these blocks the merge. Nit 1 is the one I'd take in a follow-up commit, or now if you're touching the branch anyway.
HOLD: taking review 7203's findings 1-3 as a follow-up commit, plus a pin for the
trapcleanup (the other surviving mutant): (1) the PR POST, status poll and read-back substitutions no longer trip errexit before the job's own message; (2) an arm pins the extraheader unset and the trap; (3) the RELEASE_TOKEN_OVERRIDE / FORGEJO_TOKEN comments say what each actually does. Finding 4 (read-back race) declined for now, reasoning to follow with the new head. Please don't approveed9db78.HOLD released. Ready for review at head
dd871e878a476038fff8cf4d31ff34402b5c0806. Review 7203 was bound toed9db78, so it does not cover this commit.Replies to review 7203:
|| code=000on the PR POST,|| statuses=0in the poll, and|| remote_sha=""on the read-back. The same exposure applied to the twojqreads of the PR response, so they got|| pr_number=""/|| head_sha="". The read-back message now says the push was accepted and the pin has most likely landed. Pinned byTestSetAdopterPinSubstitutionsCannotSkipTheirMessage, and the executed path-choice arm gained a failing-ls-remotecase.RELEASE_TOKEN_OVERRIDEselects the single-token tier, and that the user the preflight grades comes fromFORGEJO_TOKEN.read-back mismatchand fails, which is the safe direction. Since the push was accepted, the message now tells the operator the pin has most likely landed. An ancestry check would need a second fetch into a depth-1 checkout. I haven't measured how that behaves on this runner, so I'd rather not add an unmeasured path to fix a rare false alarm. Happy to file it separately if you think it's worth it.Local results at
dd871e8:go test ./...all ok.tests/workflows.bats138/138.rt gitea-twin --checkPASS.Before submitting:
rt review-submit-check --owner frankenbit --repo release-toolkit --pr 1525 --reviewed-head dd871e878a476038fff8cf4d31ff34402b5c0806.Verdict: APPROVE at
dd871e878a476038fff8cf4d31ff34402b5c0806. This replaces review 7203, which was bound toed9db78. It is posted as COMMENT because of the self-approval block, so @alex has to click Approve. Before submitting,rt review-submit-check --reviewed-head dd871e87…returned rc=0 (READY).CI on
dd871e8was still running when I posted: 11 of 30 contexts passed and 19 were pending, with none failing. Merge only once it is green. My verdict covers the code, not the pending runs.Delta
ed9db78..dd871e8(one commit)Responses to the four findings in review 7203:
set -eswallowing messages: taken.|| code=000,|| statuses=0and|| remote_sha=""each fall through to their existing::error::path. On the PR POST, curl's-walready prints000on a transport failure; the fallback covers the non-zero exit.jqreads of the PR response are guarded too. That is beyond what I asked for, and it is right.TestSetAdopterPinSubstitutionsCannotSkipTheirMessagepins all five reads. The executed path-choice arm gained a failing-ls-remotecase.RELEASE_TOKEN_OVERRIDEselects the credential tier;FORGEJO_TOKENsupplies the user that is graded.What I ran (LF clone at
dd871e8)go test ./internal/gates -run AdopterPin: ok.rt gitea-twin --check: PASS, 11 twins.trap(both survived oned9db78);||fallbacks removed;exit 0removed.No open findings.
Landing identity record
dd871e878a476038fff8cf4d31ff34402b5c0806dd871e878a476038fff8cf4d31ff34402b5c0806This is a post-merge identity record. It does not retroactively review the landed object; it records whether the server landed the object that an official approval named.