You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Spec: #33 (Baseline release). Section numbers refer to its Implementation Decisions.
What to build
Cutting a release is one workflow_dispatch: the workflow validates, records the pins in versions.json, tags, publishes the GitHub Release with the pins line, the Release preamble and generated notes, and pushes the multi-arch image with provenance and SBOM attestations. Spec sections 14 and 15; ADR-0008; ticket #19, #31, #32.
Acceptance criteria
workflow_dispatch with a version input (vX.Y.Z or vX.Y.Z-rc.N) and a dry-run input; validates semver, greater than the last tag, required checks green on main's head, the pins check and protocol zero diff.
Appends one row (wand version, Target Chrome, Protocol roll, Companion Chromium, read through the pins printer) to the root versions.json, commits on main through the App token, tags that commit, and creates the Release: title = tag, first line the three pins, then docs/releases/<tag>.md when present at the tagged commit (with {{rc}} and {{date}} placeholders substituted), then GitHub's label-grouped notes from .github/release.yml (roll in its own section, plus bug, enhancement, documentation and dependencies categories); -rc.N marked pre-release.
Builds and pushes ghcr.io/headlesslab/wand as one multi-arch manifest per tag (vX.Y.Z or vX.Y.Z-rc.N, plus latest for a non-rc) and :dev; records a build-provenance attestation and an SPDX SBOM attestation for every pushed manifest digest with push-to-registry; gh attestation verify oci://ghcr.io/headlesslab/wand:<tag> -R headlesslab/wand succeeds, demonstrated on a throwaway image tag (deleted afterwards; git tags are never created in a dry run).
versions.json seeded as an empty array; the release-blocking label exists; the bad-release procedure (retract directive, fixed patch, Release marked withdrawn) is documented.
Merging a Roll never tags; no version constant exists in code.
The last half of criterion 3, done now that the workflow is on main: run 34127182928, -f version=v0.1.0-rc.1 -f dry-run=true, green in four jobs (Validate, Push linux/amd64, Push linux/arm64, Publish).
Both architectures built natively and pushed under no tag; the two digests became one manifest per tag; each manifest took its two attestations with push-to-registry, and each was verified before the run went on. Read back from outside the run, the two published manifests carry exactly what the criterion asks for:
Each is signed by the Public Good Sigstore instance and in Rekor (log indices 2751062361, 2751062721, 2751064265, …).
The throwaway tags are gone, and GITHUB_TOKEN turned out to be allowed to delete a package version after all — the warning path the workflow keeps for the case it is not never ran:
dry run: ghcr.io/headlesslab/wand:dry-run-34127182928-v0.1.0-rc.1 was pushed, attested and verified, and is deleted below.
No commit, no tag and no Release were created.
deleted ghcr.io/headlesslab/wand:dry-run-34127182928-v0.1.0-rc.1
deleted ghcr.io/headlesslab/wand:dry-run-34127182928-dev
And nothing was left behind, checked from here afterwards: git ls-remote --tags origin is empty, gh release list is empty, and main is still e7d4a99, the merge commit.
The first dispatch was refused, correctly
The dispatch two minutes after the merge stopped at the first job: the Gate's own push run on e7d4a99 had barely started, so ten of the fourteen required checks were not green yet and nothing was built or pushed.
##[error]e7d4a99… is not green: Generate (zero diff), Image linux/amd64, Image linux/arm64, Tier 1 darwin/arm64 (Go stable), …
Then Tier 1 darwin/arm64 (Go stable) went red on that push run — TestScrollScreenshot at 61.79 s against about two seconds normally, the flake of #99, green when the job alone was re-run. So the first real use of this gate found the thing it exists to find and also the thing #99 is about; the cost of those flakes is now a blocked release rather than a red run, which is noted there.
What is left for a human
Making ghcr.io/headlesslab/wand public and linking it to the repository, in the package's settings. GHCR created it private on this first push and exposes no REST endpoint for the setting, so the notes carry it under "What stays human" rather than the workflow.
Activity
Address review: the .dockerignore reads at every depth, the build scr…
Maintainer notes: the automation identity, step by step (#58) (#102)
The release workflow (#60)
The release workflow (#60) (#104)
Fly-Playgroud commentedon Sep 7, 2026
The last half of criterion 3, done now that the workflow is on
main: run 34127182928,-f version=v0.1.0-rc.1 -f dry-run=true, green in four jobs (Validate, Push linux/amd64, Push linux/arm64, Publish).Both architectures built natively and pushed under no tag; the two digests became one manifest per tag; each manifest took its two attestations with
push-to-registry, and each was verified before the run went on. Read back from outside the run, the two published manifests carry exactly what the criterion asks for:Each is signed by the Public Good Sigstore instance and in Rekor (log indices 2751062361, 2751062721, 2751064265, …).
The throwaway tags are gone, and
GITHUB_TOKENturned out to be allowed to delete a package version after all — the warning path the workflow keeps for the case it is not never ran:And nothing was left behind, checked from here afterwards:
git ls-remote --tags originis empty,gh release listis empty, andmainis stille7d4a99, the merge commit.The first dispatch was refused, correctly
The dispatch two minutes after the merge stopped at the first job: the Gate's own push run on
e7d4a99had barely started, so ten of the fourteen required checks were not green yet and nothing was built or pushed.Then
Tier 1 darwin/arm64 (Go stable)went red on that push run —TestScrollScreenshotat 61.79 s against about two seconds normally, the flake of #99, green when the job alone was re-run. So the first real use of this gate found the thing it exists to find and also the thing #99 is about; the cost of those flakes is now a blocked release rather than a red run, which is noted there.What is left for a human
Making
ghcr.io/headlesslab/wandpublic and linking it to the repository, in the package's settings. GHCR created it private on this first push and exposes no REST endpoint for the setting, so the notes carry it under "What stays human" rather than the workflow.