@@ -131,6 +131,66 @@ const SCRIPTS = HERE;
131131/** The one module allowed to reach the TypeScript parser directly. */
132132const PARSER_HOME = join ( SCRIPTS , 'ts-parse.mjs' ) ;
133133
134+ /**
135+ * The SCAN SURFACE, written in the syntax `scripts/pm/dispatch-gates.mjs` can
136+ * read.
137+ *
138+ * ── The defect this repairs (#10784) ────────────────────────────────────────
139+ *
140+ * That derivation scans a gate's module body for path-ish string literals. The
141+ * only literal describing this gate's population was the bare single-segment
142+ * word `scripts` (from the `'scripts/'` prefix test in `packsScripts`, whose
143+ * trailing slash the extractor trims), and `hintCovers` refuses a
144+ * separator-less literal as too generic. So this gate scored, for EVERY card in
145+ * the tree:
146+ *
147+ * pnpm check:parse-guard [lint.yml] dead: 'scripts' — the tree HAS it; the
148+ * covering rule refuses the literal as too generic (no path separator)
149+ *
150+ * A gate in that state is named by no dispatch brief — including a brief for
151+ * the one edit most likely to break it. It is the sibling half of the same
152+ * blind spot #10784 records for `check-entry-guard`: both gates walk this
153+ * directory, and both were invisible to the derivation, by two different
154+ * routes — one declaring a population too generic to match, the other declaring
155+ * a roster of the files it already has. Anyone adding a script got neither.
156+ *
157+ * The row this discharges lived in that tool's `ESCAPABLE_LITERAL_LEDGER`,
158+ * which is SHRINK-ONLY and fails a discharged row as STALE by name. Declaring
159+ * the subtree here is the sanctioned remedy; deleting the row is the other half
160+ * of the same step, and both land together.
161+ *
162+ * ── Why the subtree spelling, and not a wider extractor ─────────────────────
163+ *
164+ * The refusal is measured, not incidental, and it is not this file's to relax:
165+ * `hintCovers`' docblock prices teaching the extractor to accept bare top-level
166+ * directory words at +139084 fabricated (gate, file) pairs, precisely because
167+ * `packages`, `apps` and `examples` are path COMPONENTS in dozens of gates that
168+ * never read those roots. A declared subtree is a different claim — an author
169+ * stating what this gate reads — and the glob collapse reduces it back to this
170+ * root and to nothing else.
171+ *
172+ * ── Why the OUTSIDE walk is deliberately NOT declared ───────────────────────
173+ *
174+ * `walkOutside(REPO_ROOT)` really does read the whole repo, but it CENSUSES;
175+ * it cannot fail this gate. The failing population is the scanned root alone,
176+ * which is what the green line claims and what `TIERS` exists to keep straight.
177+ * Declaring the repo root would name this gate for every card in the tree to
178+ * reach the one directory whose edits can turn it red — the "22 leads is the
179+ * same as none" failure the derivation's own header prices a fabricated lead
180+ * against. The refusal is pinned in the self-test rather than left in this
181+ * paragraph.
182+ *
183+ * ── Provenance, never a lookup key ──────────────────────────────────────────
184+ *
185+ * Nothing in this gate reads this array; `walk(SCRIPTS)` does the walking, and
186+ * the glob form handed to `walk` would name a directory that does not exist.
187+ * The self-test derives both directions from SCRIPTS rather than re-spelling
188+ * the root. The literal has to be written out — assembling it at runtime would
189+ * put it out of reach of the very extractor it exists for, which is a silent
190+ * way to keep the defect while looking fixed.
191+ */
192+ const ROOT_DIR_WATCH_HINTS = [ 'scripts/**' ] ;
193+
134194/**
135195 * The three parser entry points, each with the checked call that replaces it.
136196 *
@@ -644,6 +704,34 @@ export function selfTest() {
644704 codeOnly ( '// gone\nconst a = 1;\n' ) . split ( '\n' ) . length === 3
645705 && ! codeOnly ( '// gone\nconst a = 1;\n' ) . includes ( 'gone' ) ) ;
646706
707+ // -- the dispatch-gates scan surface (#10784) -----------------------------
708+ //
709+ // Enforcement cannot hold any of these: ROOT_DIR_WATCH_HINTS is read by
710+ // another tool entirely, so a wrong or stale one runs green here forever and
711+ // pays itself out as a dev dispatched on a scripts/ card with this gate
712+ // missing from the brief. Both directions are derived from the walked root
713+ // rather than re-spelled.
714+ const walkedRoot = relative ( REPO_ROOT , SCRIPTS ) ;
715+ const declaredRoots = ROOT_DIR_WATCH_HINTS . map ( ( h ) => h . replace ( / \/ \* + $ / , '' ) ) ;
716+ t ( 'the scan surface is declared for the root this gate actually walks' ,
717+ ROOT_DIR_WATCH_HINTS . includes ( `${ walkedRoot } /**` ) ,
718+ JSON . stringify ( { walkedRoot, ROOT_DIR_WATCH_HINTS } ) ) ;
719+ t ( 'and it declares no root this gate does not walk (a declaration that can drift from the scan is worse '
720+ + 'than none -- it replaces a silent gate with a lying one)' ,
721+ declaredRoots . every ( ( r ) => r === walkedRoot ) ,
722+ JSON . stringify ( declaredRoots ) ) ;
723+ t ( 'the declared literal carries a path separator, which is the whole reason it is written this way -- a '
724+ + 'bare root word is refused as too generic and reaches nothing' ,
725+ ROOT_DIR_WATCH_HINTS . every ( ( h ) => h . includes ( '/' ) ) ) ;
726+ // The census side stays undeclared: walkOutside reads the whole repo but
727+ // cannot fail this gate, and naming the repo root would put this gate in
728+ // every card's brief to reach the one directory whose edits turn it red.
729+ t ( 'the repo root is NOT declared -- the outside walk is a census, not the failing population' ,
730+ ! declaredRoots . some ( ( r ) => r === '' || r === '.' || relative ( REPO_ROOT , join ( REPO_ROOT , r ) ) === '' ) ) ;
731+ // Provenance, never a lookup key: the glob form appearing where the walk root
732+ // is read would send readdirSync at a directory that does not exist.
733+ t ( 'the declared form is NOT the walk root itself' , ! ROOT_DIR_WATCH_HINTS . includes ( walkedRoot ) ) ;
734+
647735 const failed = cases . filter ( ( c ) => ! c . ok ) ;
648736 for ( const c of failed ) console . error ( ` x ${ c . name } ${ c . detail ? ` — ${ c . detail } ` : '' } ` ) ;
649737 if ( failed . length ) {
@@ -654,7 +742,8 @@ export function selfTest() {
654742 `✓ check:parse-guard self-test: ${ cases . length } cases pass (every spelling of all three parser entry `
655743 + `points is caught, their checked replacements are not, prose and payloads are not, only ts-parse.mjs `
656744 + `is exempt, and the out-of-tree census counts what this gate does not govern — TIERED by a read of `
657- + `the owning package.json, so no row is printed under a reason that is false of it).` ,
745+ + `the owning package.json, so no row is printed under a reason that is false of it) -- plus the `
746+ + `dispatch-gates scan surface, derived from the walked root, with the census side held out of it.` ,
658747 ) ;
659748 return 0 ;
660749}
0 commit comments