QuantEcon / QuantEcon/actions

Release gating: require a green canary before v0 moves (adds a v0-next staging tag)

Open
#135 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

high-priority infrastructure
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 @v0setup-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:

  1. move v0-next to the candidate commit;
  2. dispatch the canary's v0-next workflows;
  3. green is a precondition for step 4 — red stops the release;
  4. move v0 to 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 in build-jupyter-cache/action.yml on the tagged tree. Fully self-consistent and testable, but the tagged tree then differs from main, 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-next tag and document its meaning alongside v0
  • Add a v0-next workflow set to QuantEcon/test-actions-lecture-intro
  • Add a release step that moves v0-next, dispatches the canary, and requires green before v0 moves
  • Document the rollback: v0 is movable, so recovery from a bad release is git 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 a workflow_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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.