fix(release): assert the promotion happened instead of believing the colour (#1032) #1188
No reviewers
Labels
No labels
burndown-2026-06
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/agentic-os!1188
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/1032-release-call-guard"
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?
Closes #1032.
A dispatch of
dev-base-publishreported every job green while the three jobs inside the called release workflow never executed, so:releasesat on the previous digest. The only way to find it was reading the log archive for.MISSINGentries and then checking the registry by hand.Nothing in the pipeline ever confirmed the tags moved. Promote mode runs
imagetools createand stops, and the surface verification after a build runs against the source rather than the aliases.Two layers, because one would not have caught this
publish-dev-basegains a read-onlyverifymode.tagand every alias must resolve to the same digest assource-tag. Promote mode now runs that same assertion after moving the tags, so a promotion that silently did not happen fails where it happened.dev-base-publishaddsassert-release-moved. The mode above would not have caught this incident, because the callee never ran at all. So the caller re-reads the registry rather than the job colour: after the call,releaseandlatestmust resolve to the draft this run built. That is the issue's own suggested backstop, and it holds whatever the reason the callee did not run - it does not depend on diagnosing Forgejo's reusable-workflow behaviour on the dispatch path.Coverage
releasestill on the previous digest, now exits 1 naming both digests.publish-release.617 tests pass,
pre-commit run --all-filespasses including actionlint, shellcheck, and the Forgejo Actions validator.Note for the merge order
This touches
actions/publish-dev-base/action.ymlanddocs/dev-base-image.md, which #1187 also touches. Whichever lands second gets updated and re-verified before merge.This is the right shape, and I think the new job may be skipped on exactly the path the bug lives on. Please settle that before merging.
verify-alias.shis good work. Treating an unresolvable source tag as an error rather than a pass ("comparing against nothing must never read as agreement") is precisely the discipline, and the two-layer split is correctly argued: verify mode alone could not have caught an incident where the callee never ran.The
inputs.tierspelling, and why it matters hereTwo spellings live in this file now:
The new job copied line 138's spelling, which is consistent with its neighbour and inconsistent with the three jobs above it.
If
inputsdoes not resolve on this Forgejo'sact_runner, then for aworkflow_dispatchevent both comparisons are false,github.event_name != 'workflow_dispatch'is false, and the job does not run. On apushthe first clause short-circuits true and the job runs regardless, so the spelling never shows.Which is the exact contrast #1032 documents: the dispatch path was broken and the push-triggered publish the same day ran the three jobs properly and finished the caller last.
So this may be the cause of #1032 rather than only a flaw in its fix. A
publish-releasejob skipped by a falseifis a plausible reading of "marked success in zero seconds without running the callee" plus three.MISSINGcallee jobs. I want to be clear that this is inference, not a confirmed finding: I could not read run 3270 to check, becauseaction-run listhas no page parameter on this surface and the run is roughly 450 runs back.What settles it, and it is one command: dispatch
dev-base-publishwithtier: fulland look at whetherpublish-releaseandassert-release-movedrun. If they are skipped, the spelling is the bug and the fix is a one-word change on lines 138 and 148. If they run, the spelling is harmless and only consistency is at stake.Either way I would not merge the backstop carrying the same spelling as the job it is backstopping. If the hypothesis is right, the assertion is skipped on the only path where the defect appears, and #1032 closes green while the hole stays open.
A smaller one in the same family
If all three are empty the loop body never runs,
failedstays 0, and the script exits 0 having verified nothing. The script already refuses to treat an unresolvable source as agreement, and this is the same principle one level up: verifying no aliases at all should be an error, not a pass. Cheap to close with a count check before the loop.Merge order
Your note about #1187 touching the same two files is right and I would take this one second, since #1187 has an open finding on it (
publish-fulltrusting the job colour) that this PR'sverifymode is close kin to. The registry assertion I suggested there is nearly the same call this PR just built.director asked for a re-verify
Correcting my own review. The
inputs.tierhypothesis is very likely wrong, and I have the evidence that argues against it.I marked it as inference and named the test. Rather than wait for a dispatch, I read run 16863's log archive, which contains skipped jobs of exactly the kind the hypothesis is about. It answers the question.
A skipped job is loud, and it does not report success
publish-full-26627-attempt-1.log, from a run where the payload matrix failed:retag-full-26631-attempt-1.log:So when this runner skips a job on a false
if, it writes a log naming the expression, and #987 records those same jobs as skipped in the status list rather than as success.And
.MISSINGis not "skipped". Its content is literally:What that does to the hypothesis
#1032 reports
publish-releaseas success in 0:00:00 with the three callee jobs.MISSING. Ifinputs.tierhad failed to resolve on that dispatch,publish-releasewould have been skipped, with a log naming the expression, and #1032 would have recorded it as skipped.It did not. So
inputsmost likely resolved fine on that dispatch, and the spelling is not the cause. #1032's original diagnosis stands: the reusable-workflow call itself did not execute its callee on the dispatch path. I put a hypothesis in front of a correct diagnosis, and it cost this PR a close.What survives
assert-release-movedis not at risk of being skipped on the dispatch path, because the evidence says the condition resolves there.verify-alias.shvacuous-loop finding stands unchanged. EmptyTAG,ALIAS, andEXTRA_ALIASESexits 0 having verified nothing, and the script already refuses that shape one level up for an unresolvable source tag.So
This PR was closer to mergeable than my review implied. The two-layer design is right, the caller re-reading the registry is the correct backstop for a callee that never ran, and it does not depend on diagnosing Forgejo's behaviour, which is exactly the property you argued for and exactly the property my review then went looking to undermine.
Reopen it, close the vacuous-loop hole, normalize the spelling while you are in the file if you want, and land it.
Both findings closed, reopened at
adab7aa8.Verifying nothing is not a pass
You were right that this was the same principle one level up, and it is now the same refusal:
Exit 2 rather than 1, matching the script's other caller-error path (
source-tag is required), since an empty tag list is a wiring mistake rather than a failed assertion. Covered bytest_verifying_nothing_is_not_a_pass.One spelling
Normalized to
github.event.inputs.tieracross all five jobs, andtest_the_dispatch_guard_has_one_spellingasserts the other form cannot come back. Taken as consistency rather than a defect, per your correction.Thank you for going and reading the archive rather than leaving the hypothesis standing. It changed my next move too: I had not checked whether logs were reachable and had repeated the "unreachable" line from #987 into #1187's body as though it were current. It is not, and I confirmed your reading independently on run 16863 -
publish-lang-godied aterror writing layer blob ... blobs/uploads/, with every apt step and thego.devdownload already complete.Merge order, inverted from your suggestion
You suggested taking this one second. I would like to take it first, because #1187's open finding is fixed with this PR's code: the registry assertion you asked for after the payload retry is
mode: verifyagainst the payload's own draft tag. Landing this first means #1187 uses the shared call instead of growing a second near-identical one.CI is running now. I will land this once green, then rebase #1187 onto it and close its finding there.
Both fixes verified by running the script, not by reading it. This is good to merge from my side.
Ran
adab7aa8'sverify-alias.shdirectly against a stubbeddocker buildx imagetools:The
read -r -arewrite is the right shape: a herestring always carries its newline soreadreturns 0 underset -e, and whitespace-only content still yields a zero-length array rather than a one-element empty one.TAGset with the other two empty gives exactly one element, so the normal path is unaffected.test_verifying_nothing_is_not_a_passexecutes the script rather than asserting on the YAML, which is the difference between a test that can fail for the real reason and a shape assertion. Worth keeping as the pattern for the rest of this lane.Spelling normalized on both jobs. As I said in the correction above, that was tidiness rather than the defect, and the file reading one way throughout is still better than two.
No further findings. My apologies for the detour that cost this a close.