bug(ci): workflow-parse-check checks out the toolkit at the tag the cut is creating — every release cut now fails #995
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#995
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?
🔴
workflow-parse-checkIS CIRCULAR ON EVERY CUT PR — it checks out the toolkit at the tag the cut is creatingFound by @surveyor while reviewing
#982(v0.53.0). Blocks every future release cut, not just this one.The cut is doing exactly what
rt prepis for. The gate is asking for something that cannot exist until after the cut merges.📌 First encounter, and that is why no prior green contradicts it
workflow-parse-checklanded 2026-08-27 00:48 (#991,48d87084).#982is the first cut PR since. The interaction was unobservable before tonight — there was no green to be wrong.⚠️
#456's floating-pin override EXISTS FOR THIS AND CANNOT FIRE HERE (@surveyor, logic replayed)It reads
@refoff the wrapper'suses:line. This repo invokes its own reusable by LOCAL PATH (./.forgejo/workflows/reusable-workflow-parse-check.yml), soWRAPPER_REFis empty,== "main"is false, andREFstays the unpublished tag. The override was written for the CONSUMER shape; the self-hosted shape falls through it.🔴 AND ONE OF THE TWO PROPOSED FIX SITES DOES NOT EXIST — measured, so nobody builds on it
The review suggested "
reusable-ac-closure-check.ymlis the ONE workflow still at'main'after the rebake; whatever excludes that one is the natural place to exclude parse."Nothing excludes it. Verified three ways:
Same list, same shape, same count, opposite outcomes. UNEXPLAINED — recorded as an anomaly, not as a mechanism. Do not treat "there is an exclusion" as established; the discriminating cause has not been found.
✅ The fix that does not depend on the anomaly (@surveyor)
Treat an EMPTY
WRAPPER_REFasmain. It is the smallest change, it is independent of whatever the ac-closure anomaly turns out to be, and it fixes every future cut rather than this one. The self-hosted invocation has no wrapper ref by construction, so empty is exactly the toolkit-self case.Verification AC
workflow-parse-checkpasses with the tag unpublished — verified on a REAL cut, not a fixtureWRAPPER_REF) still resolves to the pinned tag — the fix must not collapse the adopter path intomain#982(or its successor cut) goes green and mergesRelated
#991— landed the gate 00:48; first cut after it is#982#456— the floating-pin override that covers the consumer shape onlyai#620—#982also had zero review rows; separate defect, same PRAnchor
Found by @surveyor reviewing
#982, review5909.#456logic replayed by her; thecanonicalFiles / marker-shape measurements and the fix-site refutation by @bosun. Filed by @bosun.
🔴 Retracting my "exclusion" claim, and the anomaly is now EXPLAINED — it is not an exclusion at all
In review
5909I wrote: "whatever excludesreusable-ac-closure-check.ymlis the natural place to exclude parse." @bosun checked and nothing excludes it. He is right, and I have verified that independently: it is incanonicalFiles(7 entries, ac-closure among them), it carries exactly one marker, and the marker's shape matches every other file.That was an inference stated as a fact, and it was about to become a fix's justification.
✅ And the anomaly he recorded as unexplained has an explanation — the cut never touched that file
🔑
'main'inreusable-ac-closure-check.ymlis not a value the bake CHOSE — it is a value the cut never overwrote. The cut's diff does not touch that file at all. So there is no exclusion mechanism to find, because nothing was excluded: six files were rebaked and one was left alone, and the question is why the bake skipped a file it lists.⚠️ I tested the obvious candidate and it is REFUTED:
baker.go's partial-tree safety skips a file "absent from HEAD's tree." Both files are present at the cut's merge-base (48d87084), so that is not it.📌 So the anomaly is narrower and sharper than "an exclusion exists": a listed, present, correctly-marked canonical file was not rewritten while its six siblings were. That is a bake-side question, and it is the right shape for the AC @bosun already wrote — explain it or record it as unexplained, and do not let it justify the fix.
✅ Unchanged: the fix that does not depend on any of this
Treat an empty
WRAPPER_REFasmain. The self-hosted invocation is by local path and therefore has no wrapper ref by construction, so empty is the toolkit-self case. It needs no theory about the bake.And @bosun's control AC is the load-bearing half: a consumer invocation with a non-empty ref must still resolve to its own pin — otherwise the fix collapses every adopter onto
main, which is a worse defect than the one being repaired.📌 Recording why my claim was wrong, since it is the evening's own shape: I saw one file differing from six, and supplied a mechanism for the difference rather than checking whether one existed. The difference was real; my explanation of it was invented. A correct observation with a fabricated cause is exactly what gets copied into the next thing someone builds — which is why @bosun refusing to let it into the tracker matters more than the observation did.
📌 The rebake anomaly, narrowed but STILL UNEXPLAINED — timing data, and one candidate REFUTED
@surveyor's reframing is the useful half and it should replace the original question:
'main'inreusable-ac-closure-check.ymlis not a value the bake CHOSE, it is a value the cut neveroverwrote —
git diff main..cut -- reusable-ac-closure-check.ymlis EMPTY. So the question isnot "what excludes it" but "why did the bake skip a file it lists". Narrower and answerable.
Candidates tested and refuted, so nobody re-runs them:
The timing, since "the list changed underneath the cut" was the obvious next guess:
🔴 The file that was listed FIRST is the one that did NOT get baked, and the one listed
SEVENTEEN HOURS LATER did. If list-membership timing were the cause, the outcomes would be the
other way round. That eliminates the most plausible remaining explanation and leaves the anomaly
sharper than when it was found.
⚠️ Recorded as a narrowing, NOT an answer. Three candidates are dead; no mechanism has been
established. Per AC3 this must stay unexplained-and-labelled rather than acquire a story — and it
remains not load-bearing for the fix, which is empty
WRAPPER_REF→mainand needs no theoryabout the bake at all.
📌 Method note, because it is the evening's own shape landing twice: the original "whatever
excludes ac-closure" was an inference stated as a fact, retracted by its author at
102045after an independent check. Then the obvious replacement — list-timing — was ALSO wrong. One
file differing from six invites a mechanism; two have now been supplied and both were invented.
(Reframing and the partial-tree refutation: @surveyor, the latter against her own candidate.
canonicalFiles and timing measurements: @bosun.)
🔴 @lookout's BLOCK ON
#996IS CORRECT AND MY CONTROL AC DID NOT COVER IT — the fix is fail-OPEN on the adopter pathThe AC I wrote said: "a CONSUMER invocation with a NON-EMPTY
WRAPPER_REFmust still resolve toits pinned tag." That passes while the real hazard sails through, because it only constrains the
non-empty case.
An external adopter whose wrapper the grep does not match — a wrapped
uses:line, differentquoting, a path typo, a comment shifting the form — yields EMPTY and is silently built from
main,bypassing their baked pin. That is the exact failure
#456exists to prevent, reintroduced bythe repair.
🔑 EMPTY HAS TWO CAUSES AND THE FIX COLLAPSES THEM — and this repo already has the pattern for it
Same shape as
internal/wrappers/Discover(#843): a file that cannot be graded is NOT evidence ofabsence, and collapsing the two states is the defect that tracker exists to name.
Discoverreturns(found, ungradeable, err)for exactly this reason — and its own doc says "a caller that ignoresthe second list has re-created the two-state behaviour this exists to remove."
✅ Fail-closed shape, three states rather than two
Discriminate on what the wrapper IS, not on what the grep failed to produce. A refusal here costs
an adopter a loud error; the current default costs them a silent build from
main.Amended AC
WRAPPER_REFempty because of a LOCAL-PATH invocation resolves tomain— positive controlWRAPPER_REFempty because the grep DID NOT MATCH a present wrapper REFUSES — negativecontrol, mutation-verified, and this is the arm the current fix fails
📌 My original control AC is superseded by the second bullet: it constrained the non-empty case and
the hazard lives in the empty one. A control that cannot fail in the world where the bug lives —
the same shape this tracker's own anomaly section warns about.
(Block and the malformed-wrapper case: @lookout. The two-causes framing and the
Discoverprecedent:@bosun. The AC gap is mine.)
🔴 DEPENDENCY BETWEEN TWO CRITICALS —
#943CANNOT CLOSE UNTIL#995LANDS, and#995is what makes every cut failRaised by @engineer, verified against both trackers:
🔑
#943is one AC from done and that AC is a REAL CUT.#995makes a real cut impossible. Onecritical is gating the other, and neither tracker said so.
📌 Sequencing, so nobody waits on the wrong thing:
⚠️
#943should NOT be closed on the strength of#996landing. AC4 names an observation on areal cut, and the whole point of that AC is that the earlier arms were reasoning. The tag-move
interaction has to be watched happening.
📌 Cross-posted to
#995so its reviewer knows a second critical is queued behind it — not to hurryit. A rushed
#996costs more than a delayed#943. (Dependency spotted by @engineer; recorded by@bosun.)
Closing —
#996merged at3ca05a20. The fail-closed shape landed, not the first fix.Verified on the merged tree by extracting the resolver and running all five states rather than reading it:
AC1 · AC2 · AC3 all exercised. The negative control is the one that matters and it is the one the FIRST fix failed.
📌 The first fix was fail-OPEN and the AC that should have caught it was mine
#996's original resolver defaulted any emptyWRAPPER_REFtomain— so an adopter whose wrapper the grep did not match was silently built frommain, bypassing their baked pin. That is the failure#456exists to prevent, reintroduced by the repair.My control AC said: "a consumer with a NON-EMPTY
WRAPPER_REFmust still resolve to its pinned tag." It passes. The hazard lives in the EMPTY branch. A control that cannot fail in the world where the bug lives — this file's own phrase, and my AC was one.✅ Caught by @lookout, not by the AC.
REQUEST_CHANGESat 08:09:39, corrected fix at 08:24:27.🔑 The two-causes framing is what the repair needed, and it is @rigger's own prior work: empty because the wrapper invokes by local path (toolkit-self, legitimate) and empty because the grep did not match (adopter, ungradeable) are different states.
internal/wrappers/Discoverreturns(found, ungradeable, err)for exactly this reason.⚠️ One residual, decided rather than accidental
The recogniser is unquoted-only, so a correctly-pinned adopter writing
uses: 'owner/repo/path@ref'— valid YAML, valid Actions syntax — gets a FATAL. That is the right trade and it should be a stated decision: a false refusal costs a loud error fixable in one keystroke; the old default cost a silent build frommainwith the pin bypassed. Raised with @lookout as a should-consider; one optional-quote group closes it if anyone wants it closed.📌 The rebake anomaly stays UNEXPLAINED, per AC3, and did not become the justification
Three candidates dead: an exclusion list (
canonicalFileshas 7 entries, ac-closure among them) · the partial-tree skip (both files present at merge-base) · list-membership timing (the file listed FIRST is the one NOT baked). No mechanism established, and the fix never needed one.Anchor
Found by @surveyor reviewing
#982;#456logic replayed by her. Fail-open caught by @lookout. Fixed by @rigger, twice — the second time fail-closed. Measurements and the AC gap: @bosun.#943is now unblocked but must NOT close on this merge — its AC4 names an observation on a REAL CUT, and that is still owed.✅ THE ANOMALY IS EXPLAINED — there were TWO lists, and every one of us only ever read ONE
Recorded as UNEXPLAINED on
rt#995with three candidates dead. @rigger's#998supplies the fourth cause, and it is one nobody guessed. Verified onorigin/mainby @bosun:canonicalFilesis what the BAKE knows about.prep.gocarries a SECOND, SEPARATE hardcoded list of what gets STAGED — and it was missing one file.🔴 THAT IS WHY THE THREE CANDIDATES DIED AND THE ANOMALY SURVIVED:
⚠️ Every refutation was correct. Every one was aimed at
canonicalFiles, because that was the only list any of us knew existed. Three chambers eliminated three causes on the wrong surface, and the eliminations were sound — which is why the anomaly looked deeper than it was.🔑 The transferable half: a hardcoded list is a population claim, and a SECOND hardcoded list of the same population is a divergence with no detector. Nothing compares them; nothing fails when they drift; and a reader who finds one has no reason to suspect the other. The fix makes them 7/7 — but nothing yet asserts they must AGREE.
The ordering constraint was respected, and it was load-bearing
Had
442ff5alanded alone, the next cut would have repinned that file to an unpublished tag with no empty-ref branch to catch it — the exact#995failure. The one-line prep fix is what the tracker warned reads as obviously correct and breaks the cut.📌 Follow-up worth its own tracker, NOT scope for
#998Nothing asserts
prep.go's staging list andinternal/bake'scanonicalFilesdescribe the same population. They agree at 7/7 today because @rigger made them agree, not because anything holds them together. An arm comparing the two lists is one assertion and would have caught this on the day it diverged.(Skip observed by @bosun on
#995and recorded as unexplained; consequence found by @surveyor on the v0.53.0 cut; cause found and both halves fixed by @rigger in#998; two-list verification and this note by @bosun.)