bug(release): a rebuild deletes assets then fails verify on every tag before v0.56.1 #1197
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#1197
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?
Rebuilding any tag older than v0.56.1 deletes its assets, uploads 2, then fails verifying that 3 exist — so the rebuild feature breaks on exactly the tags it exists for.
Found by @engineer on 2026-09-05 by running the #980 rebuild against v0.49.0. It is not a flaky run; it is a contract gap between two halves of the pipeline.
The two halves disagree about what a release contains
The signature landed at v0.56.1. Every tag at or below v0.56.0 therefore produces 2 and is graded against 3.
Measured on v0.49.0
The delete half WORKS — assets were genuinely replaced under
allow_asset_replacement=true, and the new pair is internally coherent (sha256sum -cpasses). The break is downstream, in the asset-set contract.🔴 The failure is destructive-then-fatal: it deletes first and fails after, so a failed rebuild leaves the release holding a failed run's output rather than its original assets. v0.49.0 was restored from a pre-run capture — a rebuild without one would have destroyed the originals with no way back.
Why this inverts #980's status
The older the tag, the more certain the failure. A feature that works only on tags recent enough not to need rebuilding is not a feature, so AC1's delete-authorization path is not undemonstrated — it is broken, and the demonstration is what found it.
AC
rt release-assets expectedreads the SOURCE config and emits the set; both call sites consume it. Exercised across the whole corpus: 45 of 45 config-carrying tags derive, ZERO refusals, exactly two distinct sets — 38×checksums.txt rt-linux-amd64and 7× withchecksums.txt.minisig. That 38/7 split matches the independently-computedsigns:partition, so the derivation agrees with a differently-derived number rather than only with itself.ExpectedNamesrefuses on any config shape it has not been shown rather than guessing a name, and the refusal reaches the caller before anything is removed. ⚠️ This also repaired a latent hazard in#1194's own guard, which refused after the delete.An arm on a pre-v0.56.1 tag, asserting the refusal fires and the original assets are untouched— AMENDED: THE REMEDY THIS AC ASSUMED IS NOT THE ONE THAT SHIPPED. It presumes the fix makes old tags refuse. It does not — deriving the set from the tag's own config makes them succeed: a pre-signature tag now expects two assets, deletes two, builds two and verifies two. Refusal is reserved for a config shape the derivation cannot read, which is a different and rarer case. Replaced by:v0.49.0andv0.56.1configs as fixturesgoreleaser releaseagainst an old tag, which is a write to a published release and#980's acceptance rather than this fix's.— DEFERRED → #980. This was never a criterion for#980does not close until a pre-v0.56.1 rebuild either succeeds or refuses cleanly#1197; it is a gate on a different tracker, recorded here because this fix is what unblocks it. ⚠️ Its wording also predates the fix: a pre-signature rebuild should now succeed rather than refuse, since the set is derived from what the tag produces.Related
Anchor
@engineer, 2026-09-05, running #980 against v0.49.0. Filed by @bosun per rule ②.
🔴 CORRECTION 2026-09-05 — THE PREDICATE IN THIS BODY IS VERSION-ORDERED AND THAT IS WRONG. It says "every tag before v0.56.1". The boundary is CHRONOLOGICAL, not version-ordered, and one tag disproves the version reading outright:
v1.0.0-alpha.0sorts ABOVE everyv0.5xtag and carries nosigns:block at all, because it was cut on 2026-07-25 — five weeks before the signature landed. A fix keyed on version >= v0.56.1 would skip it.⚠️ And it is the worst tag to skip: it is the one someone reaches for when preparing v1.0.0, and
git tag --sort=-v:refnameputs it at the top of the list.✅ The correct discriminator does not involve version comparison at all — ask the tag's own tree:
git cat-file -e <tag>:.goreleaser.yamlfor presence, then countsigns:blocks for the expected asset set. Derive the required set from what the tag PRODUCES, which is what AC1 already says.(Chronological-not-version-ordered measured by @engineer while writing #980's adopter doc; the erroneous predicate was @bosun's, written into this body an hour after making the same version-ordered claim on the bus.)