Tracker: #6257, opened 2026-10-26 on Val's decision (#6111) to own Phases 2–4. It replaces the retired Bead cave-7kix8.8. Frontend build stays the required check until Val changes branch protection; agents don't.
Parent design: docs/superpowers/specs/2026-08-05-release-candidate-ci-design.md (approved; spec commit 11ee417d9)
Task-level breakdown: docs/superpowers/plans/2026-08-17-release-candidate-ci.md
Status: Program — Phase 1 (signed promotion, full validation, parallel PR checks context) is shipped on main, and Phase 5 promotion has run on real cuts (v0.5.13-rc.1 → v0.5.13, v0.5.14-rc.4 → v0.5.14 at cd4965abc). Phase 2 (the branch-protection switch), Phase 3 (retire routine fanout) and Phase 4 (verify the lean gate) remain. Last checked against main on 2026-10-20 (#6111).
This is the rollout plan for the approved Option C model: one non-skippable
Linux PR checks gate on pull requests, no automatic main CI, and the full
cross-platform/Rust/Playwright/sidecar suite at the signed release-candidate
boundary, with exact-SHA signed final-tag authorization before any release
artifact is created.
The repository has moved past the written spec. What is already true on
origin/main (f0905ce15):
| Piece | State |
|---|---|
PR checks job in .github/workflows/ci.yml |
Shipped (#4753, refined #5088) — always-reporting Ubuntu job: install, lint, typecheck, test wiring, app/API/mobile tests |
| Classic branch protection | Still requires Frontend build — PR checks reports in parallel but is not yet the required context |
.github/workflows/full-validation.yml (reusable) |
Shipped — frontend, rust, e2e (8 shards), e2e-agentic, runtime (Ubuntu/Windows/macOS), windows-native, fail-closed Release candidate validated rollup |
.github/workflows/release-candidate.yml |
Shipped — signed RC-tag trigger + provenance gate calling full-validation |
.github/workflows/release.yml authorization |
Shipped — Authorize release promotion job gates all publishing paths |
scripts/release-promotion.mjs + tests |
Shipped and wired — tag parsing, tag verification, main ancestry, RC-run discovery, legacy recovery boundary |
scripts/ci-recovery.mjs + tests |
Shipped and wired — since #5936, requests a full rerun of the latest cancelled first-attempt pull_request run after a one-hour cooldown, rechecking exact head, live base, merge parents and workflow guards; it never dispatches, because workflow_dispatch checks cannot satisfy branch protection (#5922) |
ci.yml push: branches: [main] trigger |
Still present — post-merge full reruns still happen |
ci.yml legacy path-aware fanout |
Still present — paths, ios, frontend-validation, windows-conformance (added 2026-09-14), frontend-bundle, frontend-e2e, frontend-e2e-agentic, build |
| Required-check documentation | Still names Frontend build — CLAUDE.md, .agents/skills/branch-to-merge/SKILL.md, docs/cross-environment.md |
So the remaining work is: switch branch protection to PR checks, then remove
the queue-heavy routine CI and update the required-check documentation.
pull_request: branches [main] (workflow_dispatch: exact-SHA diagnostic recovery)
│
▼
┌─────────────────────┐
│ PR checks │ ubuntu-latest · never skipped · one job
│ install --frozen │ concurrency: ci-pr-<PR#>, cancel-in-progress
│ lint │ (dispatch queued, never cancels the run it rescues)
│ typecheck │
│ check:tests-wired │
│ test:app │
│ test:api │
│ test:mobile │
└─────────────────────┘
Deliberately absent from the PR gate: pnpm build, Cargo, Playwright,
cross-environment conformance, packaged-sidecar runtime, and macOS. Those
move to candidate validation.
push: tags vMAJOR.MINOR.PATCH-rc.N (workflow_dispatch: diagnostics only)
│
▼
provenance — tag pattern ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[1-9][0-9]*$
— annotated tag object, verification.verified == true
— peeled commit == github.sha
— commit reachable from origin/main
│ commit (exact SHA)
▼
full-validation (reusable) ─────────────────────────────────────────┐
├─ frontend ubuntu · every PR-checks command + build (retry) │
├─ rust ubuntu · cargo check --locked + mobile-token test│
├─ e2e 8 shards · Playwright Chromium + WebKit │
├─ e2e-agentic flag-enabled Playwright journeys │
├─ runtime ubuntu / windows / macOS · conformance + sidecar │
├─ windows-native windows · Cargo lifecycle regression tests │
└───────────────────────────────────────────────────────────────────┘
▼
release-candidate-validated if: always() · every leg must be success
push: tags vMAJOR.MINOR.PATCH (prerelease tags explicitly excluded)
│
▼
authorize-release-promotion — verified signed annotated final tag
│ — peels to github.sha, reachable from main
│ — one successful push-event RC run with:
│ head_sha == final peeled commit
│ tag vMAJOR.MINOR.PATCH-rc.N (same base)
│ successful Release candidate validated
│ — current RC ref still verified signed
▼
rollback-readiness → daemon-package → build/checksums/updater-manifest/homebrew
No job that creates a release, uploads an asset, signs an updater artifact, or changes release metadata may run when authorization fails.
Already merged on main:
- #4753 established
PR checks,full-validation.yml,release-candidate.yml, release authorization, andscripts/release-promotion.mjs+ tests. - #5000 sharded candidate E2E; #5006 isolated release E2E server workers.
- #5088 made
PR checkssuites report independently instead of short-circuiting (cave-t8p1a).
No further work in this phase unless a corrective PR is required.
Operator step, done through the GitHub UI / API against classic branch
protection. Preserve every unrelated setting — strict: false,
enforce_admins: false, current conversation/signature policies. The
disabled ruleset (19123333) is not enabled or edited.
- Confirm
PR checksreports successfully on a PR created after thePR checkscontext landed. - Change the required status check from
Frontend buildtoPR checks. - Confirm
Frontend buildis no longer required and a new PR shows onlyPR checks.
At no point is main left without a required pull-request check. The old
jobs may keep consuming runners between phases, but they stop being merge
gates after this step.
Branch: ci/cave-7kix8-8-lean-pr-gate. One focused PR, merged through the
new single required context.
Workflow change (.github/workflows/ci.yml):
- Delete
push: branches: [main]. - Delete the
paths,frontend-validation,windows-conformance,frontend-bundle,frontend-e2e,frontend-e2e-agentic, andbuildjobs. Keepiosuntil candidate validation runs the iOS XCTest suite:full-validation.ymlhas no iOS leg, and native iOS coverage must be preserved when duplicate lanes retire. - Retain
pr-checksunchanged. - Retain
workflow_dispatchwith the exact-SHA guard, PR-number concurrency, and read-only permissions. - Update comments so none describe removed path selection, fanout, or
maincancellation.
Resulting job map must be exactly one job: pr-checks named PR checks.
Recovery contract (scripts/ci-recovery.mjs,
scripts/ci-recovery.test.mjs, scripts/ci-recovery-workflow.test.mjs):
- Narrow the workflow-guard contract (today
paths,build, and its carried-forward-evidence step) topr-checksonly. - Keep the #5936 recovery shape: a full rerun of an eligible cancelled
pull_requestrun, exact head, live base, merge parents, attempt freshness, and the one-hour cooldown. Never recover required checks byworkflow_dispatch.
Documentation and contract pins:
CLAUDE.mdand.agents/skills/branch-to-merge/SKILL.md: required check becomesPR checks; describe it as one Ubuntu job running install/lint/typecheck/test-wiring/app/API/mobile; state that build, Rust, Playwright, conformance, sidecar, Windows-native, and macOS run at the signed RC boundary.docs/cross-environment.md:PR checksbecomes the PR baseline and the RC workflow the cross-platform authority; no text describes candidate validation as a branch-protection check.- Extend
scripts/ci-recovery-workflow.test.mjsto pin both docs namingPR checksand not naming the retiredFrontend buildcontext.
New tests: none beyond the narrowed contract tests already wired into
scripts/run-tests.mjs (no new files, so no registration needed).
After Phase 3 merges:
- Open a PR; confirm
PR checksis the only required context and a new commit cancels its prior run. - Merge; confirm no automatic
ci.ymlrun starts onmain(gh run list --workflow ci.yml --commit <merge-sha> --event push→ empty). - Run the focused contract suites locally:
release-promotion,release-promotion-workflow,ci-recovery,ci-recovery-workflow,ios-build-ci,stamp-release,release-macos-signing,src-tauri/release-runtime.
Phase 5 — exercise promotion at a release cut (done: v0.5.13-rc.1 → v0.5.13, v0.5.14-rc.4 → v0.5.14)
- Stamp a release commit; create
git tag -s vX.Y.Z-rc.N <commit>. - Confirm the
Release candidateworkflow runs and theRelease candidate validatedrollup succeeds at that exact commit; theReleaseworkflow must NOT start for the RC tag. - Negative-check final authorization with the fixture tests (unsigned, lightweight, off-main, failed, skipped, manual, wrong-version, wrong-SHA). Never push deliberately invalid final tags — final tags are immutable and publication-capable.
- Create
git tag -s vX.Y.Z <same-commit>; confirm authorization succeeds before packaging/TestFlight jobs start and the summary names the candidate tag, candidate run URL, and exact promoted SHA. - Confirm RC tags never start the release publisher and prerelease tags are excluded by the final tag matcher.
| Cache | Mechanism | Scope |
|---|---|---|
| pnpm store | actions/setup-node with cache: pnpm (keyed by pnpm-lock.yaml) |
shared across PR checks and every full-validation leg |
| Rust target | Swatinem/rust-cache with workspaces: src-tauri -> target |
rust leg, runtime legs, windows-native leg |
| Playwright browsers | installed per job with --with-deps chromium webkit (version pinned by lockfile; not cached) |
e2e, e2e-agentic |
.next build |
not cached; Turbopack flake retry (scripts/build-with-retry.mjs: 3 attempts, clean .next between; a build-gate failure, exit 78, is not retried) |
frontend leg only |
Caching exists to cut wall-clock, never to relax correctness: every leg runs
pnpm install --frozen-lockfile against the exact lockfile and --locked
Cargo invocations, so a warm cache cannot hide dependency drift.
- Signature authority is GitHub's tag-object
verificationresult (verification.verified == true), read through the REST API. Local%G?output is never used (no reliable allowed-signers config in this checkout). - Exact-SHA pinning: the candidate workflow peels the annotated tag to a
commit and validates only that commit; the release gate requires a
successful push-event candidate run whose
head_shaequals the final tag's peeled commit, for the same base version, with a successfulRelease candidate validatedrollup. - Fail-closed: missing, cancelled, failed, or skipped evidence always denies. No fallback to the latest candidate, no silent acceptance of a branch run or manual dispatch.
- Observability: each workflow summary records the PR-check duration, the candidate tag/peeled commit/verification reason and per-platform results, or the final tag/chosen candidate/candidate run URL/exact promoted SHA.
- Contract tests pin behavior, not text:
release-promotion(tag parsing + provenance negatives),release-promotion-workflow(YAML DAG: PR gate exclusions, rollup fail-closed, release jobs downstream of authorization),ci-recovery-workflow(today theFrontend buildaggregate andmainpush concurrency; after Phase 3, one required context and nomainpush trigger), plusdocs-indexkeeping this document indexed.
- [done] Phase 1 shipped on
main(#4753, #5000, #5006, #5088). - [operator] Switch classic branch protection from
Frontend buildtoPR checks(Phase 2). - [PR] Phase 3: remove
push: main+ legacy fanout, narrow recovery contract, update required-check docs and pins. Merge throughPR checks. - [verify] Phase 4: new PR shows only
PR checks; merge starts nomainCI; contract suites pass. - [done] Phase 5: signed RC → full validation → signed final tag →
authorized publication (v0.5.14-rc.4 → v0.5.14 at
cd4965abc).
- A PR reports only
PR checksas required, and a new commit cancels its prior run. - Merging starts no automatic
ci.ymlrun onmain. - An unsigned, lightweight, off-main, failed, skipped, or manually dispatched candidate cannot authorize a final release.
- A signed successful candidate authorizes only a signed final tag for the same base version and exact commit.
- No release artifact or release metadata is created before promotion authorization succeeds.
- Candidate validation covers Ubuntu, Windows, macOS, Rust, Playwright, conformance, sidecar runtime, all PR-check commands, and the production frontend build.