You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
npm run test:ci is documented as the local gate, and ci.yml is what actually blocks a merge. They have
drifted: 22 of the 42 drift/wiring checks reachable from test:ci run in no workflow at all.
They are therefore enforced only when a human happens to run the full local aggregate. On a repo whose gate
auto-merges on green CI, a check nothing in CI runs is not a gate — it is a convention.
Measured against main by walking npm run references transitively from test:ci and matching each
resulting *:check / *drift-check / *:audit script against every file in .github/workflows/:
db:migrations:immutable:check guards the invariant that a shipped migrations/*.sql is never
edited. Violating it breaks boot for every upgraded Orb — the highest-blast-radius rule in the repo,
and CI does not check it.
test-wiring:check and typecheck-coverage:check exist to stop coverage and typecheck silently
narrowing. Narrowing them is invisible precisely because these do not run.
This is not hypothetical: #10237 is the same class having already fired twice on one artifact, both times
discovered by a human running the local gate rather than by CI.
The shape of the fix is a decision, not an obvious default
Wiring all 22 is not simply "add 22 steps". Each needs a needs.changes.outputs.* gate chosen from its real
inputs — the mistake #10268 had to fix for its own check was that no filter watched packages/loopover-contract/** at all, so the naive gating would have skipped the exact PRs that matter.
CI runtime is also an explicit constraint here (it is what moved selfhost.yml post-merge).
Roughly:
A. Wire all 22 into ci.yml, individually gated. Highest fidelity, most review effort, some CI cost.
Most are sub-second node scripts, so the cost is likely dominated by job setup rather than the checks.
B. One drift-checks job that runs the whole set ungated on every PR. Cheapest to write and to keep
correct — no per-check gating to get wrong, and no filter can be missing. Pays one job's setup cost per PR.
Loses per-check path gating.
C. Wire only the load-bearing subset (db:migrations:immutable, checkers-wired, test-wiring, typecheck-coverage, dead-exports, publishable-deps), leave the rest local-only, and document that test:ci is deliberately a superset.
D. Add a mechanical guard that fails when a check reachable from test:ci is absent from every
workflow, with a reasoned allowlist — the ALLOWED_UNWIRED idiom check-checkers-wired.ts already uses.
Composes with any of A–C and is what stops the gap reopening.
Recommendation: B + D. B removes the per-check gating surface entirely, which is where the fidelity bugs
actually live, and one extra job on a PR is cheap next to the existing fan-out. D is what makes it stay
true. A is the "right" answer on paper but its 22 hand-chosen gates are precisely the hand-maintained list #9860 exists to reject, and C leaves the meta-guard unguarded.
Deliverables
Decide between A–D above.
Every check reachable from test:ci either runs in a workflow or is explicitly recorded as
local-only with a reason.
A mechanical guard so a newly added test:ci check cannot silently be CI-invisible.
How this was found
Walking npm run references transitively out of test:ci and diffing against every workflow file, while
fixing the single instance in #10237. The single-instance fix is #10268; this is the class.
Problem
npm run test:ciis documented as the local gate, andci.ymlis what actually blocks a merge. They havedrifted: 22 of the 42 drift/wiring checks reachable from
test:cirun in no workflow at all.They are therefore enforced only when a human happens to run the full local aggregate. On a repo whose gate
auto-merges on green CI, a check nothing in CI runs is not a gate — it is a convention.
Measured against
mainby walkingnpm runreferences transitively fromtest:ciand matching eachresulting
*:check/*drift-check/*:auditscript against every file in.github/workflows/:db:migrations:immutable:checkscripts/check-released-migrations-immutable.tsturbo-inputs:checkscripts/check-turbo-typecheck-inputs.tsworkspace-dep-ranges:checkscripts/check-workspace-dep-ranges.tscontrol-plane:contract:checkcontrol-plane:openapi:checkcoverage-boltons:checkscripts/check-coverage-bolt-on-filenames.tsui-derived-types:checkscripts/check-ui-derived-types.tsserver-manifest:checkscripts/check-server-manifest.tsdead-exports:checkscripts/check-dead-exports.tspublishable-deps:checkscripts/check-publishable-deps.tsfixture-clock-races:checkscripts/check-fixture-clock-races.tstypecheck-coverage:checkscripts/check-typecheck-coverage.tstest-wiring:checkscripts/check-test-coverage-wiring.tscheckers-wired:checkscripts/check-checkers-wired.tsfocus-manifest-fields:checkscripts/check-focus-manifest-fields.tsrelease-commit-parsing:checkscripts/check-release-commit-parsing.tsreleasable-commit-types:checkscripts/check-releasable-commit-types.tsmaintainer-associations:checkscripts/check-maintainer-association-copies.tsrelease-linked-versions:checkscripts/check-release-linked-versions.tsmcp:tool-reference:checkmcp:client-config:checkcontract:api-schemas:checkWhy this is worth acting on rather than noting
Three of these are load-bearing in a way that makes the gap self-concealing:
checkers-wired:checkis the meta-guard from Generate or guard the remaining hand-maintained lists — three shipped red-main incidents traced to one class #9860 that fails when ascripts/check-*.tsrunsnowhere. It itself runs nowhere in CI. So the mechanism that detects unwired checkers is unwired.
db:migrations:immutable:checkguards the invariant that a shippedmigrations/*.sqlis neveredited. Violating it breaks boot for every upgraded Orb — the highest-blast-radius rule in the repo,
and CI does not check it.
test-wiring:checkandtypecheck-coverage:checkexist to stop coverage and typecheck silentlynarrowing. Narrowing them is invisible precisely because these do not run.
This is not hypothetical: #10237 is the same class having already fired twice on one artifact, both times
discovered by a human running the local gate rather than by CI.
The shape of the fix is a decision, not an obvious default
Wiring all 22 is not simply "add 22 steps". Each needs a
needs.changes.outputs.*gate chosen from its realinputs — the mistake #10268 had to fix for its own check was that no filter watched
packages/loopover-contract/**at all, so the naive gating would have skipped the exact PRs that matter.CI runtime is also an explicit constraint here (it is what moved
selfhost.ymlpost-merge).Roughly:
A. Wire all 22 into
ci.yml, individually gated. Highest fidelity, most review effort, some CI cost.Most are sub-second node scripts, so the cost is likely dominated by job setup rather than the checks.
B. One
drift-checksjob that runs the whole set ungated on every PR. Cheapest to write and to keepcorrect — no per-check gating to get wrong, and no filter can be missing. Pays one job's setup cost per PR.
Loses per-check path gating.
C. Wire only the load-bearing subset (
db:migrations:immutable,checkers-wired,test-wiring,typecheck-coverage,dead-exports,publishable-deps), leave the rest local-only, and document thattest:ciis deliberately a superset.D. Add a mechanical guard that fails when a check reachable from
test:ciis absent from everyworkflow, with a reasoned allowlist — the
ALLOWED_UNWIREDidiomcheck-checkers-wired.tsalready uses.Composes with any of A–C and is what stops the gap reopening.
Recommendation: B + D. B removes the per-check gating surface entirely, which is where the fidelity bugs
actually live, and one extra job on a PR is cheap next to the existing fan-out. D is what makes it stay
true. A is the "right" answer on paper but its 22 hand-chosen gates are precisely the hand-maintained list
#9860 exists to reject, and C leaves the meta-guard unguarded.
Deliverables
test:cieither runs in a workflow or is explicitly recorded aslocal-only with a reason.
test:cicheck cannot silently be CI-invisible.How this was found
Walking
npm runreferences transitively out oftest:ciand diffing against every workflow file, whilefixing the single instance in #10237. The single-instance fix is #10268; this is the class.