Skip to content

Commit 9d2fb82

Browse files
committed
ci: push-to-main runs the full gate (== local full gate), not only nightly
Smell-audited: 2: ADR-0107 revision. CI mirrored the local batched-full-gate cadence too literally — fast CORE (no e2e) on push-to-main, the full e2e suite only on the nightly schedule. So a shared-code change that broke an e2e step other than the changed step's own smoke sat on main until the next nightly: the D-316 def-meta case failed four nightlies before it surfaced, making CI the late detector and leaving "the local full gate" and "CI" inconsistent. Now every event except pull_request sets CLJW_CI_FULL=1 (the full e2e gate = test/run_all.sh --serial-e2e, exactly the local full gate); PR stays fast CORE for iteration, and its merge fires a push event that runs the full gate before it lands. The heavier tree_walk parity sweep (a second ReleaseSafe rebuild, not part of the local full gate) split onto its own CLJW_CI_PARITY flag, nightly/dispatch-only, so it is an extra backstop rather than push-vs-local drift. concurrency: cancel-in-progress bounds a rapid push burst to one completing full run. Devil's-advocate not forked: this reduces (never expands) the CI trust gap and introduces no new F-NNN-touching surface.
1 parent 952eecd commit 9d2fb82

3 files changed

Lines changed: 79 additions & 26 deletions

File tree

‎.dev/decisions/0107_pipeline_gate_smoke_authorized.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,30 @@ background machinery. Adopted.
186186
the two-tier policy is required for consistency but is **permission-blocked for
187187
the autonomous loop**. Surfaced to the user. The scripts + this ADR carry the
188188
enforceable policy in the meantime.
189+
190+
## Revision history
191+
192+
### 2026-07-21 — CI runs the full gate on push-to-main (not just nightly)
193+
194+
The batched-full-gate cadence above is a *local* discipline. CI had mirrored it
195+
too literally: `.github/workflows/ci.yml` ran the fast CORE tier (`--smoke`, **no
196+
e2e**) on both PR *and* push-to-main, and the full e2e suite only on the nightly
197+
schedule. So a shared-code change that broke an e2e step (not the changed step's
198+
own smoke) passed the push CI and sat on `main` until the next nightly failed —
199+
observed as the D-316 `def`-meta change breaking `phase14_var_metadata.sh`, which
200+
failed four consecutive nightlies (2026-07-18..21) before being caught. That
201+
made CI the *late* detector and left "the local full gate" and "CI" inconsistent.
202+
203+
Fix: **push-to-main now runs the FULL gate** (`CLJW_CI_FULL=1` for every event
204+
except `pull_request`), so every landed commit is e2e-verified by exactly the
205+
local full gate (`test/run_all.sh --serial-e2e`). PR stays fast CORE for
206+
iteration (its merge fires a push event that runs the full gate before it
207+
lands). The heavier non-default-backend (tree_walk) parity sweep — a second
208+
ReleaseSafe rebuild, **not** part of the local full gate — moved to its own
209+
`CLJW_CI_PARITY` flag and stays nightly/dispatch-only, so it is an extra backstop
210+
rather than a source of push-vs-local drift. `concurrency: cancel-in-progress`
211+
already collapses a rapid push burst to one completing full run, so the added
212+
per-push cost is bounded.
213+
214+
Affected: `.github/workflows/ci.yml` (event→flag mapping), `scripts/ci_gate.sh`
215+
(the parity sweep split onto `CLJW_CI_PARITY`).

‎.github/workflows/ci.yml‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
# (preinstalled on GitHub runners); `zig build -Dwasm` fetches the embedded
88
# zwasm engine + the zlinter dev-dep from git over the network.
99
#
10-
# Two-tier, matching the local ADR-0107 (smoke per commit, full batched) +
11-
# ADR-0049 (the Linux full gate is batched, not per-commit) disciplines so CI is
12-
# not wasteful:
13-
# push to main / PR → fast CORE (units + F-012 diff oracle, no e2e shell suite).
14-
# nightly schedule / manual dispatch → FULL gate (+ every e2e step).
10+
# Tiered (ADR-0107, revised 2026-07-21) so CI verifies exactly what the local
11+
# full gate does on every landed commit — the fast tier is PR-only:
12+
# PR → fast CORE (units + F-012 diff oracle, no e2e shell suite) for iteration.
13+
# push to main → FULL gate (+ every e2e step) == the local full gate, so an
14+
# e2e regression from a shared-code change fails on the push, not a day later.
15+
# nightly schedule / manual dispatch → FULL gate + the non-default-backend
16+
# (tree_walk) sweep (an extra backstop that is not part of the local gate).
1517
# The Zig toolchain, the fetched deps (zwasm/zlinter), and the build cache are
1618
# all preserved across runs (actions/cache), so a warm run rebuilds only what
1719
# changed instead of three cold ReleaseSafe builds — the dominant cost.
@@ -149,6 +151,14 @@ jobs:
149151
- name: ci_gate.sh
150152
shell: bash
151153
env:
152-
# nightly schedule + manual dispatch → full gate; push/PR → fast core.
153-
CLJW_CI_FULL: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '1' || '0' }}
154+
# push-to-main + nightly + dispatch → FULL gate (== the local full
155+
# gate: every e2e step), so a shared-code change that breaks an e2e
156+
# step fails on the push, not a day later on the nightly (ADR-0107
157+
# revision 2026-07-21). PR → fast core (its merge fires a push event
158+
# that runs the full gate before it lands).
159+
CLJW_CI_FULL: ${{ github.event_name != 'pull_request' && '1' || '0' }}
160+
# The heavier non-default-backend (tree_walk) sweep — a second
161+
# ReleaseSafe rebuild, NOT part of the local full gate — stays
162+
# nightly/dispatch-only as an extra backstop.
163+
CLJW_CI_PARITY: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '1' || '0' }}
154164
run: bash scripts/ci_gate.sh

‎scripts/ci_gate.sh‎

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,26 @@
55
# per-host gate. It checks the CURRENT host only; multi-host fan-out is the
66
# caller's job (the CI matrix / scripts/run_remote_ubuntu.sh's SSH leg).
77
#
8-
# Two tiers, mirroring the local ADR-0107 discipline (smoke per commit, full
9-
# batched) so CI is not wasteful on routine events:
10-
# CLJW_CI_FULL=1 → FULL gate: `zig build test` x2 (the F-012 dual-backend diff
11-
# oracle + every unit), zlinter, a ReleaseSafe build_cljw,
12-
# zone_check, corpus_regression, AND every e2e step
13-
# (test/run_all.sh --serial-e2e). Set on nightly schedule + dispatch.
8+
# Tiers, mirroring the local ADR-0107 discipline (smoke per commit locally, the
9+
# full gate as the authoritative landing gate) so CI verifies what the
10+
# maintainer's full gate does — never LESS:
11+
# CLJW_CI_FULL=1 → FULL gate == the LOCAL full gate: `zig build test` x2 (the
12+
# F-012 dual-backend diff oracle + every unit), zlinter, a
13+
# ReleaseSafe build_cljw, zone_check, corpus_regression, AND
14+
# every e2e step (test/run_all.sh --serial-e2e). Set on
15+
# push-to-main + nightly schedule + dispatch — every landed
16+
# commit is fully e2e-verified, so a shared-code change that
17+
# breaks an e2e step surfaces on the push, not a day later on
18+
# the nightly (ADR-0107 revision 2026-07-21).
1419
# CLJW_CI_FULL=0 → fast CORE: the same correctness core WITHOUT the ~248 e2e
15-
# shell steps (test/run_all.sh --smoke). Set on push-to-main / PR.
16-
# Both tiers run `zig fmt --check src/` first.
20+
# shell steps (test/run_all.sh --smoke). Set on PR only, for
21+
# fast iteration feedback; the PR's merge fires a push-to-main
22+
# event that runs the FULL gate before it lands.
23+
# CLJW_CI_PARITY=1 → additionally the non-default-backend (tree_walk) sweep
24+
# below. Heavier (a second ReleaseSafe rebuild), NOT part of
25+
# the local full gate, so it stays nightly/dispatch-only — an
26+
# EXTRA backstop, not a source of push-vs-local drift.
27+
# Every tier runs `zig fmt --check src/` first.
1728
#
1829
# --serial-e2e (not the -P8 parallel default) is deliberate: the parallel path
1930
# can flake the D-418/D-258 agent send/await load-race under scheduler pressure,
@@ -27,8 +38,9 @@
2738
# warm run rebuilds only what changed rather than three cold ReleaseSafe builds.
2839
#
2940
# Usage:
30-
# bash scripts/ci_gate.sh # fast core (CLJW_CI_FULL unset → 0)
31-
# CLJW_CI_FULL=1 bash scripts/ci_gate.sh # full gate
41+
# bash scripts/ci_gate.sh # fast core (PR)
42+
# CLJW_CI_FULL=1 bash scripts/ci_gate.sh # full gate (push)
43+
# CLJW_CI_FULL=1 CLJW_CI_PARITY=1 bash scripts/ci_gate.sh # full + backend sweep (nightly)
3244
set -euo pipefail
3345
cd "$(dirname "$0")/.."
3446

@@ -38,18 +50,22 @@ echo "[ci_gate] (1/2) zig fmt --check src/"
3850
zig fmt --check src/
3951

4052
if [ "${CLJW_CI_FULL:-0}" = "1" ]; then
41-
echo "[ci_gate] (2/3) FULL gate: test/run_all.sh --serial-e2e"
53+
echo "[ci_gate] FULL gate: test/run_all.sh --serial-e2e"
4254
bash test/run_all.sh --serial-e2e
43-
# D-555: the NON-default-backend sweep — corpus + every e2e on the
44-
# tree_walk (F-012 oracle) build, so an oracle-only regression (GC
45-
# rooting, backend-divergent eval defects) cannot hide behind the
46-
# vm-default gate. Nightly/dispatch-only: it rebuilds the binary twice
47-
# (tree_walk, then the default restore) — too heavy per-push.
48-
echo "[ci_gate] (3/3) non-default-backend sweep: scripts/check_vm_parity.sh"
49-
bash scripts/check_vm_parity.sh
5055
else
51-
echo "[ci_gate] (2/2) fast CORE: test/run_all.sh --smoke"
56+
echo "[ci_gate] fast CORE: test/run_all.sh --smoke"
5257
bash test/run_all.sh --smoke
5358
fi
5459

60+
# D-555: the NON-default-backend sweep — corpus + every e2e on the tree_walk
61+
# (F-012 oracle) build, so an oracle-only regression (GC rooting,
62+
# backend-divergent eval defects) cannot hide behind the vm-default gate. It
63+
# rebuilds the binary twice (tree_walk, then the default restore) and is NOT
64+
# part of the local full gate, so it stays nightly/dispatch-only (CLJW_CI_PARITY)
65+
# — an extra backstop, not a per-push cost.
66+
if [ "${CLJW_CI_PARITY:-0}" = "1" ]; then
67+
echo "[ci_gate] non-default-backend sweep: scripts/check_vm_parity.sh"
68+
bash scripts/check_vm_parity.sh
69+
fi
70+
5571
echo "[ci_gate] OK ($(uname -s), full=${CLJW_CI_FULL:-0})"

0 commit comments

Comments
 (0)