Files
54139504d4 feat(release): add one-dispatch stable rollback (#8507)
* feat(release): add one-dispatch stable rollback

A stable release publishes its artifacts to an immutable
stable/versions/<version> prefix and then copies the pointer objects into
stable/latest. latest is therefore derived state, and retracting a release is
copying an older version prefix over it again — but until now nothing in the
repo could do that, so a bad stable release had no stop button.

Add `tools-release rollback-latest` plus a release-rollback workflow that
dispatches it. The CLI refuses unless the live pointer still serves the version
the operator typed as from_version, the target published completely (not
partial, not a dry run), every ready platform's manifest and updater feed exist
in the target prefix, and the current installer floor is satisfiable by the
target. It re-resolves control.launcher.version from live channel policy rather
than restoring the snapshot's, records rollback provenance, writes metadata.json
last so latest never advertises a version whose feeds it does not yet serve, and
verifies every object through both the origin and the public URL.

The workflow shares release-stable's publish concurrency group so a rollback
cannot overlap a publish, moves only the GitHub Release "Latest" badge (never a
release body), and ends with a checklist of what a rollback does not reach: the
download page's version-pinned links, the What's New card, the group
announcement, and above all the installs already on the retracted version —
the updater never offers a version that is not strictly newer than the running
one, so this stops a rollout and cannot recall one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(release): group the rollback summary fallback redirects

shellcheck SC2129 — CI runs actionlint with shellcheck wired in, which my
local actionlint could not see without it installed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(release): prove the restored tag has a release before writing

The badge move runs after the R2 write, so a missing GitHub Release for the
restored version would have failed the run with stable/latest already moved.
Check it up front instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(release): pass the stable installer-floor policy into rollback

resolveLauncherVersionFloor() reads process env, and repository variables are
not process env. Without the handoff a rollback resolved no floor at all: it
would strip an active control.launcher.version from the restored metadata and
stop refusing targets below the current minimum — the safeguard the workflow
documents as enforced. Latent today only because no stable floor is set.

release-stable.yml passes the same pair to metadata publish and verify; mirror
it, with a topology assertion so a later edit cannot drop it silently. Also
stamp RELEASE_BRANCH/RELEASE_COMMIT so the rollback provenance is not blank.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(release): require the restored release to be badge-eligible

gh release view succeeds for a draft or a prerelease, but GitHub will not put
the Latest badge on either — so the preflight passed and the edit failed with
the R2 rollback already done, which is the half-complete state the preflight
exists to prevent. Check isDraft and isPrerelease too, and cover the
eligibility guard in the workflow test alongside its ordering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 03:21:12 +00:00
..

Tools Release

tools-release owns release metadata, storage publishing, release reports, and notification-facing data contracts. Packaged artifact build, cache, installer, payload, and smoke work stays in tools-pack; release workflows compose the two tools through CLI and file contracts.

Channel Architecture Snapshot

This matrix is a quick check before changing or validating the stable lane.

Channel Workflow / lane Role Build source Publish gate GitHub Release surface R2 / storage surface Linux policy Dry-run focus
beta release-beta / beta Daily R&D and fast validation Build the current beta lane artifacts directly No stable promotion gate Beta artifacts according to beta policy Beta channel metadata, feeds, platform manifests, payloads, and reports Default-disabled in the current validation round Fast build, cache, updater, and metadata validation
prerelease release-prerelease / prerelease Stable delivery validation Build prerelease artifacts directly The only stable promotion gate No final stable GitHub Release semantics Prerelease metadata, feeds, platform manifests, payloads, and reports Follows stable platform policy, including optional Linux enablement Complete materials that a future stable release can promote
stable release-stable / stable Formal public release Promote from a validated vX.Y.Z prerelease, with prepublish validation when requested Must detect available prerelease artifacts for the exact vX.Y.Z target Installers/packages and matching SHA files only All other platform artifacts, launcher payloads, manifests, metadata, feeds, and reports Follows stable platform policy, including optional Linux enablement metadata validates promotion inputs and plan; prepublish covers build, smoke, reports, and final publish plan without side effects

Stable Quick Check

  • Stable must verify an available, usable vX.Y.Z prerelease artifact set before publication.
  • Non-stable channels publish counted versions with a -<channel>.N suffix, for example -beta.N and -prerelease.N; stable promotion checks must match the prerelease suffix while deriving the stable vX.Y.Z target.
  • Stable promotion depends on prerelease only.
  • --dry-run=metadata should validate prerelease metadata, materials, and the stable publish plan without triggering build or smoke work.
  • --dry-run=prepublish should run all pre-publication build, smoke, payload or installer validation, report, artifact selection, and publish-plan work, while stopping before final external publishing side effects.
  • Stable GitHub Release assets should contain only user-facing installers or packages and matching SHA files.
  • R2 remains the complete release/update storage surface for payloads, manifests, metadata, feeds, platform reports, and non-GitHub artifacts.
  • The stable workflow should stay isomorphic with the other release-* lanes. Stable-specific policy belongs behind scripts, CLI options, and file contracts, not as leaked tool internals in workflow YAML.