ADR-073: The platform release is one command that reads its progress from git and GitHub
Status: Accepted (2026-10-31)
Builds on: ADR-069: One versions table pins everything, and every release proves the upgrade from the previous one (every release proves the upgrade), spec 079 (cplat pr wait), spec 121 (release-check.py, the release guard), spec 123 (the platform repo has no release role), spec 129, issue #296
Context
Section titled “Context”The platform was released by hand along CONTRIBUTING.md, Releases: ten steps, two of them irreversible (the merge of the
release PR into main and the tag vX.Y.Z). Releasing 5.4.0 took an attended hour and two steps went wrong: the upgrade
e2e run on the merge commit was looked up before it had started, and a step crashed under the system Python. The known
traps (a cancelled duplicate CI run that shows red, a missing DCO sign-off, a tag without a green upgrade e2e) recur on
every release. /component:release cannot do it: the platform repo has no release agent (spec 123).
Decision
Section titled “Decision”- The platform is released with
cplat platform release(and/platform:release, which only relays it and asks at its stops). CONTRIBUTING.md, Releases names it as the procedure; the manual steps remain documented as what it does. - The command keeps no state file. The release branch
release/X.Y.Z, the open PR from it, the squash commitchore: release X.Y.Z (#N)onmain, the tag on origin, the GitHub release and the release-guard run are its state; every run derives the phase from them, prints it, and continues at the first step not done. - It stops for the maintainer before the two irreversible steps (exit 3,
CONFIRM: merge|tag);--confirm mergeand--confirm tagare honoured only in the phase that stop belongs to. - It reuses the platform’s own checks instead of copying them:
cplat pr waitfor the PR’s CI (newest run per workflow),scripts/release-check.py rows | needs-major | gateof the checkout being released,devbox run validate. - It never bumps a plugin or marketplace version (it only checks they agree), never closes issues, and never deletes, moves or force-pushes a tag.
Consequences
Section titled “Consequences”- A release is one command and two confirmations; an interruption (a red check, a closed laptop, a declined confirmation) is resumed by running it again, on any machine with the checkout.
- The release-commit subject and the
release/X.Y.Zbranch name become a convention the command depends on; a hand-made release that uses other names is not recognised as in flight (the command then sees the newest tag as released). - The upgrade e2e and the release guard are awaited by the command, so the maintainer no longer looks up runs by hand.
Not included
Section titled “Not included”Changelog fragments (#321), closing linked issues, version bumps of plugins, and any action on a tag whose release guard is red: that stays a maintainer decision.