Release gating: require a green canary before v0 moves (adds a v0-next staging tag)
Nobody has claimed this yet.
- Dominant language
- Shell
- Stars
- 0
- Forks
- 1
- Avg merge
- 32m
- Merged PRs (30d)
- 3
Description
Decision from the consumer side: a red canary should block a release. The lecture family wants quantecon/actions verified green — within the bounds of current testing — before a release is published, and is sequencing its move to floating @v0 behind that. Consumer-side context in QuantEcon/workspace-lectures#33; this issue is the implementation.
This is stage 4 of #100, plus the thing #100 did not specify: how a gate is supposed to work when the canary is pinned to the very tag the release moves.
The blocker: the canary cannot gate a release as built
Every one of the canary's action references is @v0:
| Ref | Count in test-actions-lecture-intro |
|---|---|
build-lectures@v0 |
6 |
build-jupyter-cache@v0 |
1 |
restore-jupyter-cache@v0 |
1 |
preview-netlify@v0 |
1 |
publish-gh-pages@v0 |
1 |
v0 is what a release moves. So the canary always exercises the previous release, never the candidate — it is a post-release regression detector, and no amount of scheduling changes that. "Green before we release" is not expressible against it today.
Two further facts shape any fix. GitHub does not allow expressions in uses:, so a ref can never be parameterised. And build-jupyter-cache reaches its siblings at a hardcoded @v0 — setup-environment@v0 at action.yml:149 and build-lectures@v0 at :164, :174, :184.
Proposal: a v0-next staging tag
Add a second floating tag that always points at the release candidate, and give the canary a workflow set pinned to it. The release then becomes:
- move
v0-nextto the candidate commit; - dispatch the canary's
v0-nextworkflows; - green is a precondition for step 4 — red stops the release;
- move
v0to the same commit.
This works precisely because it does not need parameterised refs: @v0-next is as static as @v0. It costs a duplicate workflow set in the canary, which is the price of the uses: constraint rather than a design smell.
What it gates, and what it cannot
Gated — every action the canary calls directly, which is the whole publish path and the highest-consequence surface:
publish-gh-pages, preview-netlify, build-lectures, restore-jupyter-cache, setup-environment.
Not gated — the build-jupyter-cache sibling chain. A build-jupyter-cache@v0-next run still executes setup-environment@v0 and build-lectures@v0, i.e. the previous release's siblings. The candidate's cache action is tested against stale siblings, so the pairing that consumers will actually run remains untested until after v0 moves. That is the structural limit named in #100 and this proposal does not remove it; it narrows the ungated surface to one action's internal chain.
Worth stating plainly so the gate is not over-trusted: "100% before release" will mean "everything except the build-jupyter-cache sibling chain", unless that chain is addressed separately (see below).
Options for the residual, not recommended unilaterally
- Rewrite sibling refs at release time. A release step rewrites
@v0→ the candidate SHA inbuild-jupyter-cache/action.ymlon the tagged tree. Fully self-consistent and testable, but the tagged tree then differs frommain, which is a real cost to reasoning about the repo. - Accept it, and lean on cadence. The cache path already has the longest post-release lead of any consumer path — the canary's weekly build runs Sunday 03:00 UTC against lecture repos' Monday 02:00/03:00 UTC. Combined with rollback, exposure is bounded to roughly a day.
Work
Gate mechanism
- Introduce the
v0-nexttag and document its meaning alongsidev0 - Add a
v0-nextworkflow set toQuantEcon/test-actions-lecture-intro - Add a release step that moves
v0-next, dispatches the canary, and requires green beforev0moves - Document the rollback:
v0is movable, so recovery from a bad release isgit tag -f v0 <previous>. Write it down before it is needed, with who is expected to act
Close the ungated consumer paths (these matter whether or not the gate lands, since they are also the post-release safety net)
- Schedule the canary's
ci.yml— the preview path currently runs only when a human opens a PR in the canary (last: 2026-08-05, before that 2026-07-15) - Schedule the canary's
publish.yml, and give it aworkflow_dispatch— it currently has neither, so it cannot even be run by hand - Keep any canary schedule strictly ahead of consumers' Monday 02:00/03:00 UTC cache builds
Prove the gate is real
A gate that has never been exercised is not a gate, and this repo has precedent on both halves: the container smoke tests could not fail for three independent reasons and still reported green (#108), and the canary failed eight consecutive weeks (2026-02-22 → 2026-04-12) filing zero issues (#83).
- Self-test the gate — stage a deliberately broken candidate, confirm the canary goes red and the release is actually stopped, then revert
- Confirm a red canary reaches a human, separately from the above. The eight silent weeks were failures that alerted nobody
Open questions
| Question | Why it matters |
|---|---|
| Is the release blocked automatically, or by convention in the runbook? | There is no release automation here today — v0 is moved by hand — so "blocked" may initially mean a documented step rather than an enforced one |
Does v0-next also get a scheduled canary run, or only on release? |
Only-on-release is cheaper; scheduled would catch drift on main between releases |
Is the build-jupyter-cache residual acceptable? |
It decides whether "100%" is qualified or whether the ref-rewrite option is worth its cost |
Related
- #100 — testing; this is its stage 4 and the gating design it left open
- #83 — the two-month silent alerting failure behind the "prove it reaches a human" item
- #108 — the smoke tests that could not fail; precedent for self-testing the gate
- QuantEcon/workspace-lectures#33 — the consumer-side plan, blocked on this
- QuantEcon/workspace-lectures#31 — the lecture-family CI migration this unblocks
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the release process in this repository and the canary workflows in QuantEcon/test-actions-lecture-intro, especially ci.yml and publish.yml. Inspect the sibling references in build-jupyter-cache/action.yml at lines 149, 164, 174, and 184. Done means v0-next stages the candidate, the canary result blocks moving v0, rollback and ownership are documented, and the gate plus human alerting have been deliberately self-tested.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions, shell
- Domain
- ci-cd, devops, release
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100