Filed unassigned, found while implementing #10030. Nothing is red today; this is about what the ratchets claim to measure versus what they do measure when a file will not parse.
Measured
scripts/check-slot-lookup-ratchet.mjs and scripts/check-query-options-erasure-ratchet.mjs both run ESLint through its Node API (import { ESLint } from 'eslint') over packages/**/*.{ts,tsx,mts,cts} with the grandfathering ignores lifted, then count the messages matching their own rule text.
ESLint's Node API does not throw when a file fails to parse — it returns the failure as an ordinary message with ruleId: null and fatal: true. Measured in a worktree at origin/main @ 1800ffac2, against the file from #10030:
lintFiles returned normally (did NOT throw)
registry.ts messages: [{"fatal":true,"ruleId":null,"message":"Parsing error: Maximum call stack size exceeded"}]
errorCount=1
Both ratchets filter by their own rule's message text, so a ruleId: null fatal matches nothing and is discarded. The file contributes zero sites, the ratchet reports ✓ … holds, and exits 0 — indistinguishable from "this file was measured and is clean".
Confirmed both gates are green at origin/main today:
✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new.
✓ query-options-erasure ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new.
Why it matters
Both scripts carry docblocks arguing precisely that a green pnpm lint proves nothing for the files they cover, and that they exist to close that hole — check-slot-lookup-ratchet.mjs: "pnpm lint passing proves NOTHING for a file in the baseline". The parse-failure path reopens the same kind of hole one level up: an unparseable file is silently unmeasured by the very gate whose job is to measure it, and the failure mode is quiet green rather than red.
This is not currently biting: over a wide scope (thousands of files) registry.ts does parse, and both ratchets run over all of packages/**. It bites when a file becomes unparseable for any reason — the #10030 signature, a syntax error in a generated artifact, a future parser upgrade.
⚠️ Note the asymmetry with pnpm lint: the root lint script fails loudly on a fatal parse error (that is what made #10030 visible at all). These two gates fail silently on the same input. Whatever the disposition, they should not disagree about that.
Direction (not a decision)
The cheap, self-contained version: after lintFiles, assert that no result carries a fatal message, and fail with the file name and the parse error if one does. That is a handful of lines in each script, it turns a silent skip into a named red, and it changes no ratchet count — verifiable by running both gates before and after and seeing identical site counts.
Whether the two scripts should also get the stack headroom the root lint script now has (they run as plain node scripts/… at V8's default 984 KB stack) is a separate question and depends on the above landing first — with a fatal assertion in place, the stack question announces itself instead of hiding.
Refs: #10030 (where this surfaced) · #4251 (slot-lookup ratchet) · #4918 (query-options ratchet).
Filed unassigned, found while implementing #10030. Nothing is red today; this is about what the ratchets claim to measure versus what they do measure when a file will not parse.
Measured
scripts/check-slot-lookup-ratchet.mjsandscripts/check-query-options-erasure-ratchet.mjsboth run ESLint through its Node API (import { ESLint } from 'eslint') overpackages/**/*.{ts,tsx,mts,cts}with the grandfatheringignoreslifted, then count the messages matching their own rule text.ESLint's Node API does not throw when a file fails to parse — it returns the failure as an ordinary message with
ruleId: nullandfatal: true. Measured in a worktree atorigin/main@1800ffac2, against the file from #10030:Both ratchets filter by their own rule's message text, so a
ruleId: nullfatal matches nothing and is discarded. The file contributes zero sites, the ratchet reports✓ … holds, and exits 0 — indistinguishable from "this file was measured and is clean".Confirmed both gates are green at
origin/maintoday:Why it matters
Both scripts carry docblocks arguing precisely that a green
pnpm lintproves nothing for the files they cover, and that they exist to close that hole —check-slot-lookup-ratchet.mjs: "pnpm lintpassing proves NOTHING for a file in the baseline". The parse-failure path reopens the same kind of hole one level up: an unparseable file is silently unmeasured by the very gate whose job is to measure it, and the failure mode is quiet green rather than red.This is not currently biting: over a wide scope (thousands of files)
registry.tsdoes parse, and both ratchets run over all ofpackages/**. It bites when a file becomes unparseable for any reason — the #10030 signature, a syntax error in a generated artifact, a future parser upgrade.pnpm lint: the rootlintscript fails loudly on a fatal parse error (that is what made #10030 visible at all). These two gates fail silently on the same input. Whatever the disposition, they should not disagree about that.Direction (not a decision)
The cheap, self-contained version: after
lintFiles, assert that no result carries afatalmessage, and fail with the file name and the parse error if one does. That is a handful of lines in each script, it turns a silent skip into a named red, and it changes no ratchet count — verifiable by running both gates before and after and seeing identical site counts.Whether the two scripts should also get the stack headroom the root
lintscript now has (they run as plainnode scripts/…at V8's default 984 KB stack) is a separate question and depends on the above landing first — with a fatal assertion in place, the stack question announces itself instead of hiding.Refs: #10030 (where this surfaced) · #4251 (slot-lookup ratchet) · #4918 (query-options ratchet).