diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1c4b487f33..b09e86486f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -239,8 +239,9 @@ jobs: - name: Raw control-byte guard run: pnpm check:nul-bytes - # The three shared modules the two `scripts/**` routing gates below - # DELEGATE their design arguments to (#10608). Both of those gates are + # The first three of these are the shared modules the two `scripts/**` + # routing gates below DELEGATE their design arguments to (#10608). Both of + # those gates are # SPELLING gates by deliberate design — `check:entry-guard` enforces that # only `scripts/invoked-as.mjs` may read `process.argv[1]`, and # `check:parse-guard` that every TypeScript parse goes through @@ -261,23 +262,59 @@ jobs: # own documentation — loud — while a mask that starts OVER-masking makes # both go quietly green over text they never read. # - # What runs here is the self-tests, not the modules' callers: the callers - # are the two gate steps below, which already run on every PR. + # `import-prerequisite.mjs` is the FOURTH module here, from a different + # family and for the same reason (#14007). It is not one of the #10608 + # delegates — it is the shared `PREREQUISITE NOT MET` frame that 45 gates + # IMPORT (42 under `scripts/**`, 3 under `packages/lint/scripts/**`), and + # its `--self-test` is the ONE place the inherited advisory is pinned. The + # module says so itself: *"Pinned HERE and nowhere else, because this is + # the one copy 45 importers print."* Since #13983 the same self-test also + # pins the exit-code CLASS those 45 gates return for an unmet prerequisite + # (`EXIT_PREREQUISITE_NOT_MET`, distinct from a gate's own findings exit). + # Nothing ran it: `git grep -n "import-prerequisite" -- .github/workflows + # package.json` was EMPTY, so 60 cases executed only when a human or an + # agent typed the command by hand. + # + # ⛔ NOT a hole in `check:self-test-wired`, whose green is correct by its + # own definition: its population is the scripts CI RUNS, and this file is a + # MODULE no workflow can invoke, so it sat in that gate's remainder by + # construction. The blast radius and the population rule point in opposite + # directions here: the file with the largest inheritance surface in the + # farm is the one shape the wiring gate cannot see. Naming it in this step + # is what moves it INTO that population (161 -> 162 scripts CI runs that + # ship a `--self-test`), and it is the smaller of the two remedies — the + # other being to widen the gate to a transitive closure, which #14007 + # explicitly does not ask for. ⛔ It is also not a licence to sweep the + # rest of that remainder from here; that is a separate card. + # + # What runs here is the self-tests, not the modules' callers: for the first + # three the callers are the two gate steps below, which already run on + # every PR; for `import-prerequisite` the callers are the 45 importing + # gates, whose ordinary runs take the branch where the dependency IS + # present and therefore observe none of what the self-test pins. # Invoked as `node` rather than through `pnpm check:*`: see the GATE # INVOCATION IDIOM note at the top of this file. # `ts-parse` spawns ~20 real node children (~10 s measured, and the spawns # are the point — they pin that a caller's try/catch cannot swallow the - # refusal); the other two are in-process fixtures, ~0.5 s combined. + # refusal); the other three are in-process fixtures, ~0.6 s combined. # # ⭐ Collected rather than sequenced, for the reason spelled out at the # `Shallow-history guard self-tests` step below (#10814): under `bash -e` a # bare sequence stops at the first failure, so a red `ts-parse` would leave - # the entry-predicate and comment-mask self-tests UNRUN while the log shows - # only the one failure. `ts-parse` is both the slowest of the three and the - # one that spawns real children, so it is the likeliest to be red — - # precisely the masking direction. The three modules are independent of one - # another, so collecting loses nothing. - - name: scripts/ shared-module self-tests (parse · entry predicate · comment mask) + # the entry-predicate, comment-mask and prerequisite-frame self-tests UNRUN + # while the log shows only the one failure. `ts-parse` is both the slowest + # of the four and the one that spawns real children, so it is the likeliest + # to be red — precisely the masking direction. The four modules are + # independent of one another, so collecting loses nothing. + # + # ⭐ Collected is also why the fourth leg is HERE rather than in a step of + # its own: Actions skips a job's remaining steps once a step fails, so a + # standalone step further down this job would be masked by every gate above + # it — the same defect one level up, and the split `check-step-collectors` + # explicitly refuses as a remedy. Inside this block the frame's self-test + # prints its own verdict whatever the other three do, and it does so early + # in the job. + - name: scripts/ shared-module self-tests (parse · entry predicate · comment mask · prerequisite frame) run: | # Tolerate-and-collect (#10814) — see the note above this step. Each # self-test runs unconditionally and prints its own verdict; the step @@ -300,13 +337,14 @@ jobs: run_self_test node scripts/ts-parse.mjs --self-test run_self_test node scripts/invoked-as.mjs --self-test run_self_test node scripts/js-comment-mask.mjs --self-test + run_self_test node scripts/import-prerequisite.mjs --self-test if [ -n "$failed" ]; then echo "" echo "scripts/ shared-module self-tests — the following FAILED:" printf "%s" "$failed" exit 1 fi - echo "scripts/ shared-module self-tests — all three ran and passed" + echo "scripts/ shared-module self-tests — all four ran and passed" # Every `scripts/**` entry guard goes through ONE predicate (#10086). # The hand-typed forms of "did node run me, or did someone import me?"