Skip to content

[finding] checkGuardAdoption's call bans only read the gate FILE — a measurement moved one import out is invisible to all three #10625

Description

@os-zhuang

Recorded from the #10599 dev seat (session session_01DdCnBGcHeufjrq7drTD3wt, branch
claude/issue-10599-fatal-guard-linttext, PR #10624) while closing the lintText half of the
adoption check. Not touched there — out of that card's scope, and it is a bound rather than a live
false green.

The bound

checkGuardAdoption() reads exactly the sources named in GUARDED_GATES:

for(constgateofGUARDED_GATES){src=readFileSync(resolve(repoRoot,gate),'utf8');problems.push(...guardAdoptionProblems(gate,src));}

So every test it applies — the import test, the armed test, the .lintFiles( ban, and now the
.lintText( ban from #10599 — is a statement about one file's text. A gate that moved its
counting into a sibling helper (import { measure } from './lint-population.mjs', with the raw
eslint.lintFiles() or eslint.lintText() living there) presents a gate file with no banned call
shape in it at all. The two bans see nothing. The import and armed tests still pass as long as the
gate file keeps any one strict call — which is the same mixed shape #10599 closed, one indirection
further out.

This is the same sentence #10123#10458#10599 keep landing on, each time one level up: the
check proves a fact about the text it read, and the claim it is used to make is about the
measurement. #10599 moved the undecidable part (which result is COUNTED) to a declaration at the
call site; nothing yet decides WHICH FILES have to carry those declarations.

Severity: latent

Neither gate has a helper module today — both check-slot-lookup-ratchet.mjs and
check-query-options-erasure-ratchet.mjs lint inline. Nothing is green that should be red right
now. It is worth writing down because the natural next refactor of either gate (both are past 700
lines) is exactly "pull the measuring part into a module", and that refactor silently removes the
gate from its own guard with no diff to the check.

Shapes worth weighing, none obviously right

  1. Leave it, and say in checkGuardAdoption()'s docblock that its claim is scoped to the gate file
    — the same narrowing checkGuardAdoption still passes a gate that counts messages from lintText() — the adoption check tests the call it names, not the measurement it guards #10599 applied to the printed sentence.
  2. Follow the import graph from each gate and apply guardAdoptionProblems() to every local module
    it reaches. Decidable for static import specifiers, and it makes the population a derived fact
    rather than a hand-kept list — but it needs a specifier resolver the check does not have.
  3. Ban the raw call shapes repo-wide outside scripts/eslint-fatal-guard.mjs, so no file can host
    an unguarded lint at all. Widest, and the one that stops depending on which files are named;
    costs a sweep of whatever else in scripts/** legitimately lints.

Refs: #10123 (the card that created the guard) · #10458 / PR #10598 (comments-as-code, and the
armed test) · #10599 / PR #10624 (the lintText half, where this was noticed).


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions