From 1c3ebbedd66f1e906b9a7578c107d7f3282e3705 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 21:26:42 +0000 Subject: [PATCH] ci(devx): run the dev-prereqs gate's self-test half in lint.yml (#8170) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `check:dev-prereqs` is `--self-test && the scan`, and the scan half is CI-hostile: on an unbuilt tree it exits 1 with "The workspace is not built — 67 of 67 workspace packages declare an entry point under dist/ that is not on disk", and on a tree that just built it is a tautological green. Its subject is a human's long-lived worktree before `pnpm dev`, not CI. So it correctly stays out — and it was taking the hermetic self-test half with it, leaving the gate in the gate list reading as coverage while nothing kept its scan honest. This wires the self-test half only, invoked as `node` rather than through the npm script so the scan half cannot come along. Same shape as PR #8171 landed for the family's first member. Also corrects the script header, which claimed "CI IS UNAFFECTED … never into a workflow" — true of the scan, and this change makes it false of the file. --- .github/workflows/lint.yml | 44 +++++++++++++++++++++++++++++++++++ scripts/check-dev-prereqs.mjs | 16 ++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index eb1848be9d..3d140f64bf 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -931,6 +931,50 @@ jobs: - name: Stall-guard self-test run: pnpm check:stall-guard + # The dev-preflight gate's self-test — and deliberately only HALF of that + # gate (#8170, the second and only other member of #8110's split-gate + # family). Same neighbour as the step above for the same reason: both are + # self-tests of a guard whose real path CI does not exercise. + # + # The npm script of the same name is `--self-test && the scan`, and the + # SCAN half is genuinely CI-hostile. Its question is "is THIS WORKING TREE + # built, and is the one staleness-lying artifact current" — a precondition + # for `pnpm dev` on a human's long-lived tree. Re-measured on a fresh + # worktree before any install or build: exit 1, "The workspace is not built + # — 67 of 67 workspace packages declare an entry point under dist/ that is + # not on disk". In a job that has not built, that is a hard false red about + # a precondition CI does not have; in a job that just built, it is a + # tautological green. It correctly stays out, and it was taking the half + # that CAN run with it — the gate sat in the gate list reading as coverage + # while its scan ran on every human's `pnpm dev` and nothing kept that scan + # honest. + # + # The `--self-test` half is a different animal. Its fixtures are SYNTHETIC: + # it materializes throwaway workspaces in a temp dir and drives the gate's + # own `inspect`/`report`/`stamp` to all 16 verdicts. No network, no git, no + # node_modules, no workspace state at all — measured green in ~0.11s in a + # `--depth 1` clone carrying no remote-tracking ref for main. + # + # WHAT REDDENS IT, and where this differs from its sibling. #8110's step + # cannot go red alone (its specimen is derived from the frame documents, so + # a structural change reddens the sync gate above it first). This one CAN, + # and that is fine: its only input is this one script's own code, so the + # only PR that reaches it red is a PR that edits that script. Measured + # adversarially — rewording the stale-dist red message from "LIES" reds this + # step alone with `stale/says-it-lies: expected true, got false`, naming the + # assertion in the very file the PR is editing, so no sibling gate needs to + # diagnose it. The other direction was measured too: a legitimate change + # elsewhere cannot redden it — dropping `--stamp` from packages/spec's build + # script (which DOES red the scan half) leaves this step green, because the + # fixtures are synthetic. That is also this step's honest boundary: it + # vouches for the gate's verdict paths, never for this workspace. + # + # Invoked as `node`, NOT through the npm script, precisely because that + # script would drag the scan half in with it — the same shape as the `dev` + # chain calling this file directly (see the script's header). + - name: Self-test the dev-prereqs gate (self-test half only, never the scan) + run: node scripts/check-dev-prereqs.mjs --self-test + - name: Type check (@objectstack/spec) run: pnpm --filter @objectstack/spec exec tsc --noEmit diff --git a/scripts/check-dev-prereqs.mjs b/scripts/check-dev-prereqs.mjs index 4105c7d9fc..fa721327d6 100644 --- a/scripts/check-dev-prereqs.mjs +++ b/scripts/check-dev-prereqs.mjs @@ -163,11 +163,21 @@ * `pnpm build` filters it out) and the examples (their entry is a .ts * source file, not a build artifact). * - * CI IS UNAFFECTED. This is wired into the root `dev` / `dev:*` scripts only — - * never into a workflow. CI builds before it runs anything that could trip - * this, so the condition cannot occur there; #5726 and #5217 are both + * WHERE THE SCAN RUNS. The scan is wired into the root `dev` / `dev:*` scripts + * only — never into a workflow, and it must stay that way. CI builds before it + * runs anything that could trip this, so in a job that just built the scan is a + * tautological green, and in a job that has not built it is a hard false red + * about a precondition CI does not have; #5726 and #5217 are both * local/worktree-only shapes. * + * WHERE THE SELF-TEST RUNS — since #8170, a different answer to a different + * question. `--self-test` is hermetic (synthetic workspaces in a temp dir, no + * network, no git, no node_modules), so lint.yml runs THAT half, on its own, by + * invoking this file with `node`. The `check:dev-prereqs` npm script keeps the + * conventional self-test-then-scan shape and is still for humans only: running + * it in CI would drag the scan half back in, which is the whole thing being + * avoided. The split is deliberate on both sides — see the step's comment. + * * No env escape hatch, deliberately (check:console-sha has none either). To * boot a deliberately half-built workspace, call the underlying command * directly: `pnpm --filter @objectstack/example-showcase dev`.