From 94466aae643e574a815ab779f8943c3039f7b095 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:47:02 +0200 Subject: [PATCH] ci: make the verify job actually run verify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI job here is named "verify" and did not call `npm run verify`. It re-inlined the chain by hand, and the hand-written copy had drifted: package.json verify: format:check → lint → test → build ci.yml: format:check → lint → test → build (--if-present ×3) Neither ran a typecheck. `tsc --noEmit` existed nowhere in this repo, so type errors were caught only as a side effect of `next build` — late, slow, and one `typescript.ignoreBuildErrors` away from not being caught at all. Two fixes, both the same principle: 1. Add a real `typecheck` script and put it in `verify`, before test/build so it fails in seconds rather than after a full Next build. Verified: `tsc --noEmit` is already clean (exit 0), so this gate goes green on arrival — it is closing a hole, not papering over a backlog. 2. CI now calls `npm run verify` verbatim, and the `--if-present` flags are gone. Those flags made three gates unable to fail: rename or delete the `lint` script and the step passes green instead of erroring. A gate that cannot go red is not a gate. That is exactly the defect that let evig report "Run Tests ✓" on every PR for three weeks while 25 assertions were failing (fixed in evig#306), and the same reason dotfiles#14 now sweeps the fleet for discarded gates. Full `npm run verify` passes locally: format, lint, typecheck, test, build, exit 0. Green verify locally now genuinely means green CI, because they are the same command. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 24 +++++++++++++----------- package.json | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98eb4eec..09294590 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,14 +29,16 @@ jobs: - run: npm ci - - name: Format check - run: npm run format:check --if-present - - - name: Lint - run: npm run lint --if-present - - - name: Test - run: npm run test --if-present - - - name: Build - run: npm run build + # SSOT: format + lint + typecheck + test + build are bundled in the + # `verify` npm script (package.json), and CI calls it verbatim so the + # gating chain cannot drift from what a developer runs locally. This job + # was already NAMED verify but re-inlined the chain, which is how the + # typecheck gate went missing: `tsc --noEmit` ran nowhere, and type + # errors were caught only as a side effect of `next build`. + # + # The `--if-present` flags are gone on purpose. They made three of these + # gates unable to fail: rename or delete the `lint` script and the step + # passes green instead of erroring. A gate that cannot go red is not a + # gate — the same defect that let evig ship 25 failing tests under a ✓. + - name: Verify (format + lint + typecheck + test + build) + run: npm run verify diff --git a/package.json b/package.json index e9deb26e..06336ee5 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "lint:fix": "eslint . --fix", "format": "prettier --write .", "format:check": "prettier --check .", - "verify": "npm run format:check && npm run lint && npm run test && npm run build", + "typecheck": "tsc --noEmit", + "verify": "npm run format:check && npm run lint && npm run typecheck && npm run test && npm run build", "prepare": "husky install", "lint-staged": "lint-staged" },