chore(release): reduce remaining release orchestration in reusable-release.yml #862
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#862
Loading…
Add table
Add a link
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?
Finding
Current main has 811 non-empty run lines in .forgejo/workflows/reusable-release.yml, including 331 non-comment shell lines. #582 migrated the seven original release callsites and #705 moved the six Forgejo operations, but the workflow still owns a large stateful shell surface for release decisions, git pushes, manifest bookkeeping, credentials, and post-conditions.
The remaining shell is not automatically wrong: some is workflow-native environment and output glue. The gap is that its boundary is not explicit, so further ports either duplicate Go behavior or leave stateful release behavior in an untested shell block.
Existing coverage and boundary
#582 and #705 are complete. #805 and #811 cover release correctness issues, not the remaining implementation boundary. This tracker must not become a line-count vanity port or move secrets into command arguments.
Acceptance criteria
4fd7907b, which has798a5b19(this tracker's merged state) as an ancestor. Refusal/failure paths verified by @carpenter's merged-tree run on 08-27; the live cut was the missing half.Related
#582, #705, #805, #811.
Dispatch
Assigned to Carpenter by Pullings for the remaining release-orchestration boundary. This is a separate lane from rt#876, which is assigned to Rigger; the two implementations must not overlap.
Starting main:
e287752a25. Produce an artifact-checkable PR with the inventory, dispositions, preserved fail-closed contracts, and exact verification evidence. Pullings will request Lookout once the PR head exists.Partial closeout
PR #879 is merged at
5eb13949b3, with review 5595 bound to that exact head.ACs 1–5 and 7 are now supported by the merged inventory/disposition, typed rt post-cut implementation, preserved workflow contracts, exact workflow census, ADR-0009 update, and independent Go/Bats/ShellCheck/diff verification.
AC6 remains unfinished: the PR records that no live release/tag was fired from the branch. The refusal and dry-run paths are covered, but the real-cut verification still needs to be performed on the merged tree. #862 remains open and assigned to Carpenter.
AC6 verification result
AC6 remains unchecked. The merged-tree cut was fired by the merge-triggered workflow without an operator-authorization prompt, and it produced the release artifacts, but the cut did not complete end to end.
Real cut
7d248947f6768306b268ced9307dc41a512fb2b4(chore(release): prepare v0.46.0)#5974/ task26587(decide + act), push event onmain, created2026-08-25T21:29:18+02:00; final statusfailure: https://git.frankenbit.de/frankenbit/release-toolkit/actions/runs/5974mode=cut,version=0.46.0,tag=v0.46.0,head_sha=7d248947f6768306b268ced9307dc41a512fb2b4,prep_sha=7d248947f6768306b268ced9307dc41a512fb2b4; compose preflight passed.v0.46.0 -> 7d248947f6768306b268ced9307dc41a512fb2b4, confirmed withgit ls-remote.268, published2026-08-25T21:29:56+02:00, non-draft/non-prerelease: https://git.frankenbit.de/frankenbit/release-toolkit/releases/tag/v0.46.0checksums.txt(81 bytes) andrt-linux-amd64(9,859,234 bytes). Authenticated download of the binary reportsrt 0.46.0; published checksum46dfdf23c792f82479d291500338f17ac645c602bae6fdcf1684d555625147d2matches the downloaded binary.26592(build + publish rt asset) succeeded.Failure dispositions
rt post-cutsaying the manifest was already at the new head, but the readable remotemainstill hadlast_released_sha=de927aad629d6bc4ea3ea63779af94d1a8fd0aa4.manifest-postcondition: fatal, manifest PRabsent, exit1. No manifest landed and no manifest PR was open, so the release bookkeeping is not complete.#5976/ task26593also failed before its Docker capability step because thedocker-buildrunner could not findnodewhile runningactions/checkout@v4: https://git.frankenbit.de/frankenbit/release-toolkit/actions/runs/5976. The sibling asset task succeeded; no image digest was produced by this run.Merged-tree refusal controls
On the same merged tree (
7d248947...):go build ./...,go vet ./...,go test -count=1 ./...: pass.bats tests/*.bats:1..59, all 59 pass; shellcheck (3 shell files) andgit diff --check: pass.go test -count=1 ./cmd/rt -run 'TestRelease_(dryRunPublishesOutputs|missingSection|emptySection|invalidVersion)|TestPostcondition_' -v: all pass.go test -count=1 ./internal/release -run 'TestPrepareForPublish_(dryRunSkipsManifest|idempotentManifestSkip)' -v: pass; manifest-postcondition table/refusal tests ininternal/gates: pass.branch-readable=true, old landed SHA,pr-state=absent) printedmanifest-postcondition: fataland exited1.rt release --version 9.9.9 --dry-runrefused with the missing changelog-section error and exited1.Final disposition: the real tag/release/asset path is evidenced and the refusal controls are fail-closed, but AC6 is not satisfied because manifest bookkeeping did not reach a terminal state and the image publish path failed on runner capability. No changes were made to #879 or #876.
CI failure diagnosis
The red release-cut statuses are two separate populations:
7d248947f6) completed the release/tag and asset, but failed the manifest postcondition. The readable remote main still recorded last_released_sha=de927aad629d6bc4ea3ea63779af94d1a8fd0aa4; no manifest PR was open.For #862 AC6, the source-level cause of #5974 is now identified on origin/main
7d248947f6: rt release writes .release-toolkit-manifest.json in the working tree before rt post-cut; postCutPrecheck checks the remote first, then falls back to the local file. When remote main has the prior SHA, the local file now contains the current cut SHA and produces a false idempotent skip. The postcondition then correctly sees old remote state and refuses.No new tracker is filed: this is within the open AC6 real-cut/failure-path scope. Carpenter is dispatched to fix the boundary/precheck, preserve true replay skips where the remote branch already records the cut, and add a regression/refusal control.
Implementation merged
PR #881 merged fast-forward-only at
56b90a6050after official Lookout review 5608 at the exact head and all 12 Forgejo contexts succeeded.This lands the remote-authoritative post-cut replay fix and its regression/refusal controls. AC6 remains unchecked and #862 remains open: the fresh live cut still has not been authorized or fired, so the end-to-end release evidence is not yet established.
Pending-cut recovery landed\n\nPR #883 for #882 merged at
a9563b4263de0ae97661304661efee44143953a8after official Lookout review 5611 and independent merge-gate PASS. Merged-tree release run #6023 now emits the typedmode=blockeddisposition for the uncut v0.46.0 state and fails closed before prep/release/post-cut/manifest/rolling-PR mutation. No live cut was fired. AC6 remains unchecked and operator-blocked on an explicitly authorized end-to-end cut.AC6 safe verification at current merged main
Read-only snapshot:
origin/main=798a5b19cff3319d61e87139c50b00e09166fd03. No live release was fired and no release API mutation was made during this verification. AC6 remains unchecked pending an explicitly authorized end-to-end cut accepted against the merged tree.Merged-tree commands and results:
go test -count=1 ./...-> rc 0; all packages passed.go vet ./...-> rc 0.go build ./...-> rc 0.bats tests-> rc 0;1..118.find scripts -type f (shell files) -print0 | xargs -0 shellcheck --severity=warning-> rc 0, matching the CI gate. A plain default-severityshellcheck scripts/*.shreports only the pre-existing info-level SC2016 inscripts/contract-paths-check.sh; CI intentionally runs at warning severity.git diff --check-> rc 0.Refusal and failure-path controls:
go test -count=1 ./cmd/rt -run 'Test(Release_|Postcondition_|Precheck_)' -v-> PASS.go test -count=1 ./internal/gates ./internal/release -run 'Test(ManifestPostcondition|Precheck|Unpublished|PrepareForPublish|Fire_|Rollback|Transactionality)' -v-> PASS.go test -count=1 ./internal/decide -run 'TestDecide_(417|882)|TestPendingCutEvidence|TestBlockedDecisionRenders' -v-> PASS. This covers #417 orphan refusal, legitimate prepare-merge skip, typed pending-cut evidence, tag/release mixed states, and unknown lookup states.go run ./cmd/rt decide --dry-run-> rc 0,mode=update next_version=0.54.0 next_tag=v0.54.0 last_released_version=0.53.1 last_released_sha=2a2aaba2.go run ./cmd/rt post-cut --version 0.53.1 --tag v0.53.1 --head-sha 2a2aaba2 --manifest .release-toolkit-manifest.json --dry-run-> rc 0; reports no mutation.go run ./cmd/rt release --version 9.9.9 --dry-run-> rc 1 with the missing-section refusal.The opt-in live draft-gate read was also run without mutation:
frankenbit/cid-probenow returns 404, so its positive draft-blocking arm is could-not-grade;frankenbit/pursercorrectly refuses the no-push read, andfrankenbit/release-toolkitcorrectly proceeds on the clean arm. The missing positive fixture is not counted as a pass.Existing production observation, not used to tick AC6 without explicit authorization: release workflow run API 16124 (UI run 7835) succeeded for v0.53.1 at prep commit
2a2aaba24abdf27f584989bd91f14a2014740ad5; release 282 is published withchecksums.txtandrt-linux-amd64. Authenticated download verification returnedrt 0.53.1), checksumd0e96c7ed4c203fed292f7e9fe4ee61f6501aa527728e012eaac5fedaadf9c4e`, and the current main manifest records v0.53.1 at that prep SHA. This is evidence of an already completed cut; explicit acceptance/authorization for AC6 is still required.AC6 satisfied — closing. It was blocked on "an explicitly authorized end-to-end cut accepted against the merged tree", and one was fired tonight.
The refusal/failure half was already done — @carpenter's 2026-08-27 merged-tree run at
798a5b19:go test ./...rc=0, vet rc=0, build rc=0,bats tests1..118 rc=0, shellcheck at CI severity rc=0, plus the refusal-path suites (Release_/Postcondition_/Precheck_,ManifestPostcondition/Precheck/Unpublished/PrepareForPublish/Fire_/Rollback) all PASS. The live cut was the only missing half and it is no longer missing.⚠️ Bound worth stating: the cut's own job logs were not flushed to disk (
log_in_storage=0on tasks 32518/32521, bothstatus=success), so this rests on the ARTIFACTS rather than on the run output. That is the stronger evidence for a release anyway — v0.54.1 is the precedent for a green run whose image was absent, which is exactly why the digest was checked separately from the baked value.Cut authorized by the operator; artifact verification with both controls by @quartermaster; merged-tree refusal paths by @carpenter.