One question this file answers: I changed some files. Which command do I run?
The why behind the gate design (tiers, the merge-gate/release-gate split, what is still unbuilt) lives in gate-plan.md. This file is the routing table.
0. Last verified
Every gate below was executed end to end againstmain on 2026-08-12 (macOS, M-series, v2.6.0). A green row means somebody watched it go green, not that it is supposed to be green.
pnpm bench was broken and nobody knew. suite-rre.mjs recorded four flows that asserted no observable consequence and then demanded a pass verdict from reticle_flow_verify — which correctly grades an assertion-free suite unverifiable. The product got more honest about false greens; the benchmark measuring it did not follow, so the whole run aborted at script 9 of 10 and replay-determinism never ran at all. Each flow now carries a success oracle. This is the failure mode bench/ is most exposed to: nothing in CI runs it, so it can only rot silently.
pnpm gate:install (~15 min) and the Windows / Rust jobs were not run in this sweep — they are CI-only or network-bound. They are green on main per the last CI run, which is a weaker claim than every row above, and is stated that way on purpose.
1. The routing table
Find the row that matches what you changed. Run its commands. That is the whole rule.
Why routing exists. The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless — routing costs you a slower red, never a missed one.
2. Every gate, and what each one can actually see
Each gate exists because the ones above it are blind to something. That blindness is the column that matters.
The single required status check is
gate. It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to ci.yml is half the work; adding it to gate’s needs: list is the other half — a job missing from that list runs, reports, and is structurally incapable of blocking a merge.
Guards that self-test
Four checks prove they can still fail before they are trusted. A guard that has never refused anything is not a guard, so each has a negative control that CI runs first:3. Gates that are not automatic
These are real and they work; they are not on the PR path, so they only run when somebody runs them.4. bench/ is not a gate
bench/ is measurement and research, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read bench/README.md before touching it — it says which scripts are live and which are one-off studies kept as evidence for a published claim.
The one exception worth knowing: pnpm bench + pnpm bench:gate is a working regression gate for the replay numbers, and it is run by hand before a release.
5. When a gate fails and you think it is the gate’s fault
Sometimes it is. The specific failures worth recognising:EADDRINUSE/ “died during boot” — a previous run left something on:8787,:4310, or:3100.run-ci.shfrees these on exit; if it was killed, free them by hand.- Killing port 4400 with
lsof -ti tcp:4400 | xargs kill -9— this SIGKILLs thereticle mcpproxy too, because the proxy holds a client socket on the bridge port. Always add-sTCP:LISTEN. This is the root cause of most “the MCP went down” reports. - A timing assertion — if a test asserts
Date.now() - t < N, that is a bug in the test, not a flake to re-run. Assert the bound (output size, a truncation flag), or use a generous per-test timeout. Seeharness-rules.md. - An
INCONCLUSIVEverdict — the harness is telling you the transport did not stay up, so it is claiming nothing about the product. That is the harness working, not the product failing.
apps/e2e/harness-rules.md.