chore(ci): one PR-gates reusable and fewer toolkit-self jobs, because the runner is down to one slot (#1304's hold condition) #1530
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit#1530
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
The runner queue is the binding constraint again. #1304 closed its consolidation tracker as won't-do and named the condition for reopening it: "a future state where the runner queue is the binding constraint again". That state is now measured, and the operator has confirmed it will last. This tracker covers a smaller adopter workflow surface and fewer toolkit-self jobs. It goes further than #1304 in one respect: the operator has also accepted breaking the reusable-workflow API, because every adopter is a repo under the same control.
Tracker since 2026-10-04. The work is split into the sub-issues under "Acceptance criteria". The corrections found in review are under "Corrections".
Measured
Data comes from the live forge through the public API, on 2026-10-02/03, at origin/main
dd871e8to8086700.Concurrency is 1.
caymans-fedora) also took jobs. That was the operator's workstation booted into Fedora, and it no longer runs one.One toolkit-self PR (runs 19373-19387; median over 25 heads):
mainrtbuildsA small job takes about 5.5 s, and about 87% of that is setup:
rtbuild 2.05 s.The gates themselves took 28 s of the 224 s that small jobs spent. The real work sits in two jobs:
batsat about 34 s andgo-ciat about 30 s.The adopters are five repos plus toolkit-self. Each wires up to six workflow files, at mixed pins (re-read on 2026-10-04; unchanged):
@v0.63.0@v0.63.1@v0.42.0@v0.56.0@v0.42.0@v0.36.0Why #1304's reasons no longer decide this
#1304 comment 109121 gave three reasons. Here is where each stands now.
bats, about 34 s), so wall-clock does not get worse at any slot count.pull_requestgates merge.fork-pr-approval-notice(pull_request_target, the trust boundary) stays separate.release.yml/release-cut.ymlconcurrency split (ADR-0010) andrecover-pending-cut, which stays dispatch-only because nothing may fire it without a person.$RUNNER_TEMP, and nothing enforces that they do not collide. The plan enforces it:Plan (reviewed design, sizes estimated)
PR-A (S,
no-changelog), now #1539. The next free ADR (0013), written as a decision tree. It supersedes the frozen-API statement indocs/architecture/contracts/workflow-api.mdand records why #1304's hold condition is met.PR-B (L,
added, additive), now #1541. Addsreusable-pr-gates.ymland its twin. Its job runs every PR-time gate as a step, with one checkout and onertbuild:onoroff, and the job is red if anyongate fails. There is no in-job "advisory" mode: this forge has no job-log API, so a warning inside a green job is invisible (#1177);ononly for manifest and fragments.Toolkit-self calls it from a local
pr.ymlwith all gates on. The PR also carries:NeedsSecretslists;workflows.batsregistration guard (today a hard-coded list of 9);main, no caller pin is found, and the repository is not toolkit-self. Otherwise a tag-pinned adopter would silently runmain;tests / workflow-schema;The old wrappers keep running.
PR-C (M, additive), now #1540.
tests.yml's four short jobs into onetests / checksjob, porting the build, tree-state and attest guards fromtoolkit-self-gates(#1323).toolkit-self-gatesby the PR halves of check-self-bootstrap and gitea-twin-check, by prep-order-check and by readme-pindigest.readme-pin-check / mirror,base-divergence-check) stay separate jobs.One admin edit: swap the required-context list on
mainjust before PR-D merges, after approving PR-D. (Corrected below: two swaps.)PR-D (M-L,
removed), now #1542. It needs aBREAKING CHANGEline naming the renamed contexts (#1479). It deletes:gitea_twin.gofails on orphans;pre_push.gorows;One cut (the next minor, v0.66.0 or later; v0.65.0 was cut on 2026-10-04). Old tags keep working. Part of #1542.
Adopters, one PR each, one issue each in the adopter's repo:
RELEASE_TOOLKIT_MINISIGN_PUBLIC_KEYfirst for adopters jumping past v0.57.0 (#1479).pr.ymlwith only the gates that adopter already ran, and bumpsrelease.yml's pin.If #1529 ships in the same cut, storey adds itsMoot (see Corrections).release_noteskey in the same PR.Expected result (estimated, one slot):
rtbuildsCorrections (review, 2026-10-04, at origin/main
e5fcf1d; branch protection read live)mainrequires today:tests / workflow-schema,tests / dated-examples,tests / contract-paths,tests / shellcheck,check-self-bootstrap / check,gitea-twin-check / check,prep-order-check / checkandreadme-pin-check / digest. It needs its own swap at merge. #1542 swaps the 14 gate-wrapper contexts.pr.yml.workflow-api.mdsays the parse check's wrapper "must be a sibling workflow, since a malformed workflow cannot run its own observer". A malformedpr.ymlwould drop every gate silently on an adopter with no required checks. #1539 decides this.workflow-api.mdapplies it "Starting at v1.0.0", and the latest cut is v0.65.0. So the ADR amends the v1 target; it does not break a promise in force.e5fcf1d, inside v0.65.0. #1529 shipped with no config key:TestLoad_releaseNotesIsAnUnknownKeypinsrelease_notesas unknown. So storey needs norelease_noteskey, and plan step 7's last bullet is moot.tests/workflows.bats, at lines 254 and 644.manifest-check.yml). An adopter calling frompr.ymlmatches none, so the bake list and the fail-closed step close one hole together (#1541).frankenbitorg, which is what accepting the API break rests on.Acceptance criteria
Each row ticks when its sub-issue's own criteria are done. This tracker is closed by hand once every row is ticked.
Order (revised 2026-10-05, operator decision): #1540, #1539 and #1541 are done.
reusable-pr-gates.ymlalongside the old reusables.pr-gates@v0.66.0as the live probe of the tag-pinned adopter path, and its job log is read.pr / gates (pull_request), and v0.67.0 is cut.Issues that wait on another carry
status/blocked. Found along the way: #1548 (the old reusables never ran the landing-tree replay).workflow-api.md(C3), cites #1304's hold condition, and decides where the parse gate runs. — DONE @ #1544 (44bdba5, reviewed as44bdba5). Decided by the operator on 2026-10-04: D1-D8 as recommended, including thelayout: combined | splitinput (defaultcombined), the caller's status as the one required check, the parse gate insidepr.ymlwith required checks on adopters, and recover-pending-cut left out of v1.tests / checks, the growntoolkit-self-gatesand its own swap of the required checks. — DONE @ #1546 (9968ed4, reviewed as9968ed4). Measured: 22 to 15 jobs and 207 s to 173 s wall clock per toolkit-self PR;mainrequires 18 checks.reusable-pr-gates.yml: — DONE @ #1547 (855b34e, reviewed as855b34e). The caller context ispr / gates (pull_request). Also found there: #1548, where the old reusables never ran the replay.on|off, verdicts namespaced and attested;0de3876, reviewed as0de3876), cut as v0.67.0 (#1553, tag8e59136). Measured: 8 jobs, 114 s summed, 122 s wall clock, 5 required checks per toolkit-self PR, against 22 jobs, 181 s and 207 s before.af77cf0, reviewed asaf77cf0), onpr-gates@v0.66.0.pr / gatesis required on tmux-tell main, and the probe result is in comment 115189.fork-pr-approval-notice,recover-pending-cut, therelease/release-cutsplit, and the scheduled, release and dispatch workflows. — DONE:git diff 44bdba5 8e59136(from ADR-0013's landing to the v0.67.0 tag) is empty forfork-pr-approval-notice.yml,recover-pending-cut.yml,release-cut.yml,release.yml,goreleaser.yml,mirror-release.yml,mirror-drift-check.ymlandprepared-uncut-check.yml.reusable-release.ymlandreusable-recover-pending-cut.ymlchange only their baked-ref marker (maintov0.67.0), which is the cut's own bake. #1540 kept the push and schedule halves of the workflows it folded (review 7222).Out of scope
register-check --git-log. It has been inert sincea06cd67. Turning it on hits 4 historical commits, and its remedy is a force-push, which AGENTS.md section 15 forbids. That is a policy issue of its own.reusable-release.yml's outputs, a generalized caller scan for the release workflow, and retiring the Gitea twins.Anchor
Measurement and design workflows of 2026-10-02/03, during the release-notes work for frankenbit/storey (#1478, #1529). Prior art: #1253 (5 gates into
toolkit-self-gates), #1304 (won't-do while slots were free), #1177 (advisory checks need their own contexts).git add --all -- changelog.d(AGENTS.md section 5a) instead of relying on the verb to create the directory #1537chore(ci): one PR-gates reusable and fewer toolkit-self jobs — the runner is down to one slot, which is #1304's hold conditionto chore(ci): one PR-gates reusable and fewer toolkit-self jobs, because the runner is down to one slot (#1304's hold condition)Plan review of this tracker and its sub-issues, read at origin/main
6b5d554with branch protection read live today.Summary: the plan holds. Its numbers check out: the 25 contexts on
main, the 8 that #1540 removes, the 14 from the gate wrappers, and the adopter wrappers and pins. I found four gaps worth fixing in the issue bodies before the implementers reach them. None of them changes the design.Verified:
mainrequires exactly 25 contexts. #1540's eight (tests / workflow-schema,dated-examples,contract-paths,shellcheck,check-self-bootstrap / check,gitea-twin-check / check,prep-order-check / check,readme-pin-check / digest) are all among them. The gate wrappers account for 14 more (2+2+2+2+3+3), and the remaining three aretests / bats,go-ci / lint + build + testandtoolkit-self-gates.canonicalFileshas 9 entries, and the twotests/workflows.batslists are at 254 and 644, as stated.1. #1540 needs a carry-forward list (AGENTS.md section 1)
Folding jobs into one job drops per-job preconditions unless each one is carried over. These are load-bearing today:
check-self-bootstrap.ymlskipsrelease-prep/heads with a job-levelif:(its comment explains why).toolkit-self-gateshas no such skip. Moved as a plain step, the check starts running on the rolling PR.fetch-depth: 0is load-bearing fordated-examples(tests.yml:299-304says a shallow clone gets an empty population and refuses), forcontract-paths(tests.yml:625) and forcheck-self-bootstrap.workflow-schemaandshellcheckuse depth 1. The mergedtests / checksjob needs the deepest of them.prep-order-checkneedspull-requests: readandFORGEJO_TOKEN: secrets.GITHUB_TOKEN.readme-pin-check / digestdeliberately runs with no token (readme-pin-check.yml:105). In one job the permissions become the union, so keep the token-less property at step level and say so.toolkit-self-gatesfires onedited; the four checks moving into it did not. After the move they re-run on every PR-body edit. That is harmless, but it costs a slot. Also,readme-pin-checkhasworkflow_dispatch, anddigestloses it if it moves.tests / checksonly if the job id ischeckswith noname:(orname: checks).2. #1541 undercounts the hand-kept lists in
tests/workflows.batsBesides lines 254 and 644:
if len(scripts) != 18(about line 1320) is 9 reusables times 2 providers. A tenth reusable makes it 20.wrapper_namesmaps (about 851, 1323, 1601) andwrapper_names.get(reusable, reusable.removeprefix("reusable-"))(1394, 1661) derive the caller file aspr-gates.ymlforreusable-pr-gates.yml. ADR-0013 D4 scans every workflow file instead, so this resolve-ref harness needs rewriting for the new reusable, not just a new entry.gates=(...)array at 2111 ("all 4 PR-CI gates consume the shared bootstrap artifact").3. #1542's "at most 6 required checks" depends on ADR-0013 D2
A reusable called from a wrapper posts two contexts: the caller job and the reusable's job. The 6 counts
tests / bats,tests / checks,go-ci,toolkit-self-gatesand the twoprcontexts. That holds only with the parse gate insidepr.yml, which is D2's recommendation. A sibling parse workflow adds two more. Step 1 of #1542 ("requires theprcheck") also needs to name which of the two contexts, or both.4. The adopter issues do not carry D2's required-status step
If D2 is accepted as recommended, the safety net for a
pr.ymlthat fails to parse is a required status on each adopter'smain. None of tmux-tell#963, storey#69, ember#166, cellblock#181 or purser#63 mentions it. Each needs an AC for the admin edit: enable status checks and require the PR-gates context, but only after that context has posted on the adopter's rolling release PR (#809's shape). Eachpr.ymlalso needseditedin itspull_requesttypes for the body-reading gates, and nopaths:filter (#644). If D2 goes the other way, the adopter issues instead need the sibling parse workflow.My review of the ADR PR itself is on #1544 (review 7220).
Plan review 114846 is now folded into the issue bodies. I verified each point at origin/main
6b5d554before editing.#1540: a new "Carry-forward" section and an acceptance criterion for the PR's carry-forward table. The implementer has the list.
#1541: the hand-kept lists in
tests/workflows.batsare now all named: lines 254 and 644,len(scripts) != 18at 1389 and 1656,wrapper_namesat 1322 and 1600, theremoveprefixderivation at 1394 and 1661, the pairs at 855-860 and the gates array at 2111. A new criterion covers the resolve-ref harness rewrite.#1542: the job and check counts now depend on ADR-0013 D2. The swap must name the exact PR-gates context or contexts, since a wrapper call posts two (caller and reusable job).
tmux-tell#963, storey#69, ember#166, cellblock#181 and purser#63: each now has two new criteria.
pr.ymlwiring rule:editedin the types, and nopaths:filter;Each also states its repo's current required-status setting.
The ADR's own findings from review 7220 are with its author for a follow-up commit on #1544.
The adopter probe passed. frankenbit/tmux-tell#964 landed as
af77cf0, exactly the reviewed head (review 7241); Alex merged it on 2026-10-05 at 21:20. tmux-tell is the first adopter onreusable-pr-gates.yml@v0.66.0.From the job logs, read in the built-in browser:
pr / gates):resolved toolkit ref: v0.66.0 (source=baked), with the per-gate script taken from.release-toolkit/;bootstrap rt: FETCH published asset @ v0.66.0, thenminisign signature OK,signed for v0.66.0 (trusted comment matches)andchecksum OK (189ebf7f…);reusable-release.yml@v0.66.0): resolvedv0.66.0the same way, then FETCHed and verifiedrt. The v0.64.0 PAT scope check passed as release-bot, and the run opened rolling PR tmux-tell#965 (v0.40.0).rtcache. That is now #1550, and later runs take about 7 s.For the other adopters: storey, ember, cellblock and purser can migrate at
v0.66.0. Copy tmux-tell'spr.yml, setting the gates each one already ran, and bumprelease.yml. ember, cellblock and purser needRELEASE_TOOLKIT_MINISIGN_PUBLIC_KEYfirst, because they jump past v0.57.0. It is now also set at the org scope; check that per repo.Still open on tmux-tell#963:
pr / gatesmust post on rolling PR #965. Then the operator makes it required on tmux-tell's main (ADR-0013 D4).