:next is meant to track the next major, but any pre-release claimed it —
so 2.4.3-beta.1 took it from 3.0.0-alpha.1, and anyone pulling :next for
a 3.0 alpha was silently moved back onto a 2.x image. A downgrade across
a major line, from a rehearsal beta that had no business touching it.
:beta had the mirror of the same flaw, claimable by a pre-release on any
line.
Both are now gated on LATEST_MAJOR, exactly as :latest already was:
:beta is the stable line's pre-release, :next the newer major's. With
LATEST_MAJOR=2, 2.4.3-beta.x takes :beta only and 3.0.0-alpha.x takes
:next only.
:next has been repointed to 3.0.0-alpha.1 on Docker Hub, by copying the
manifest list so both architectures were preserved.
Most of docker.yaml was doing work that was duplicated or hazardous now
that releases are cut from a tag.
The tests were duplicated exactly: release.yaml and docker.yaml called
the same reusable tests.yaml, on the same commit — once before drafting,
again after publishing. No new information, and the image build queued
behind it. Pint likewise: check.yaml already style-checked the commit
when it landed. Both jobs go; coverage is unchanged.
manual_docker_build goes too, and it was worse than redundant. Its
checkout took no ref, so it built the dispatched branch while tagging
with whatever string was typed — an image could be labelled 2.4.2 while
containing 2.x HEAD — and `tag` defaulted to "latest", so a careless
dispatch republished the moving stable tag from a branch. #710 had to add
a guard purely to stop the registration dispatch doing that by accident.
It was last used in September 2025 to push the legacy and alpha tags
during the Docker distribution work; that is finished, and releases
produce images now. A patched base image is better served by a patch
release than by silently changing what a pinned tag contains.
That cascade removes the tag input and the #710 guard as well, leaving
register_tag as the only dispatch input and two jobs in the file.
Losing the test gate would leave the image build ungated, so the release
build now refuses a release with no InvoiceShelf.zip. A release either
came from the tested pipeline or it gets no images — the updater is
already protected this way, since registration downloads that same asset.
GitHub offers no way to forbid hand-made releases; this is the closest
thing, which is to make them inert.
"Docker" no longer describes a workflow that registers on the updater and
publishes images, so it becomes publish.yaml — matching its trigger and
pairing with release.yaml, which prepares what this distributes. The
recovery instructions in AGENTS.md name this workflow and would have
broken silently, so they move with it.