Found 2026-09-03 while adding the release chain to audiocomponents (the audioif/audiocomponents split, audiocomponents#2).
reusable-tag-on-release-merge.yml (publishing-v6 and v8 alike; the v6..v8 diff is checkout@v4→v7 only) does not validate what VERSION says. It runs gh release create "v$V" on the file's contents verbatim. Any string that is a legal git ref becomes a tag and a GitHub Release — and git check-ref-format tags/v0.0.0-PLACEHOLDER-BRAD-NAMES-THIS succeeds, so a placeholder that a human has not yet replaced would have been tagged and Released the moment VERSION landed on main. audioif's tag-release.yml, which calls the reusable unguarded, would have done exactly that.
audiocomponents mitigated it locally with a validate job its tag job needs, using the same grammar the publish chain accepts:
V=$(tr -d '[:space:]' < VERSION)
[[ "$V" =~ ^[0-9]+\.[0-9]+\.[0-9]+((a|b|rc)[0-9]+|\.dev[0-9]+)?$ ]] || { echo "VERSION is '$V', not a release version; refusing to tag." >&2; exit 1; }
Proven against the guard: refuses 0.0.0-PLACEHOLDER-BRAD-NAMES-THIS, v0.2.0, 0.2, `` and 0.2.0-rc1; accepts `0.2.0`, `0.2.0rc1`, `0.2.0.dev1`, `1.10.3b2`.
The reusable should carry the check itself so every consumer gets it without a local job — a consumer that forgets the guard today gets a burned tag and a Release to delete. Publishing-v9 material, alongside #26's skew hardening; versions are human-named everywhere in the org, so a placeholder in VERSION between releases is the normal state, not an edge case.
Found 2026-09-03 while adding the release chain to audiocomponents (the audioif/audiocomponents split, audiocomponents#2).
reusable-tag-on-release-merge.yml(publishing-v6 and v8 alike; the v6..v8 diff is checkout@v4→v7 only) does not validate whatVERSIONsays. It runsgh release create "v$V"on the file's contents verbatim. Any string that is a legal git ref becomes a tag and a GitHub Release — andgit check-ref-format tags/v0.0.0-PLACEHOLDER-BRAD-NAMES-THISsucceeds, so a placeholder that a human has not yet replaced would have been tagged and Released the momentVERSIONlanded on main. audioif'stag-release.yml, which calls the reusable unguarded, would have done exactly that.audiocomponents mitigated it locally with a
validatejob itstagjob needs, using the same grammar the publish chain accepts:Proven against the guard: refuses
0.0.0-PLACEHOLDER-BRAD-NAMES-THIS,v0.2.0,0.2, `` and0.2.0-rc1; accepts `0.2.0`, `0.2.0rc1`, `0.2.0.dev1`, `1.10.3b2`.The reusable should carry the check itself so every consumer gets it without a local job — a consumer that forgets the guard today gets a burned tag and a Release to delete. Publishing-v9 material, alongside #26's skew hardening; versions are human-named everywhere in the org, so a placeholder in
VERSIONbetween releases is the normal state, not an edge case.