@@ -342,7 +342,57 @@ const SCAN_ROOTS = ['.claude', 'skills'];
342342 * than re-spelled, so re-scoping a root cannot leave this describing the old one.
343343 */
344344const ROOT_DIR_WATCH_HINTS = [ 'skills/**' ] ;
345- const SCAN_SKIP_DIRS = new Set ( [ 'node_modules' , '.git' , 'dist' , 'references' ] ) ;
345+ /**
346+ * Directories the anti-dormancy walk refuses to descend into: trees that are
347+ * INSTALLED or GENERATED, where nothing hand-written lives.
348+ *
349+ * ## Why `references` is NOT one of them (#15056)
350+ *
351+ * It was, until this edit, carried over from the idiom the repo's prose gates
352+ * share — and that idiom states its reason outright, in
353+ * `check-corpus-claim-drift.mjs`:
354+ *
355+ * > Generated `references/` is skipped: the spec source is the fix site
356+ * > there, so a finding in a generated file names the wrong file.
357+ *
358+ * That sentence is about `content/docs/references/`, which IS generated from
359+ * spec. This gate never walks `content/docs`. Under SCAN_ROOTS the same
360+ * directory NAME holds hand-written operative prose: the PM protocol files
361+ * under `.claude/skills/pm-dispatch/references/` — `decision-analysis.md`
362+ * among them, whose subject matter IS escalation analysis and so is the single
363+ * likeliest place for a third copy to be pasted — and the published reference
364+ * companions that ship verbatim to third parties via `npx skills add`, the
365+ * exact distribution path this gate's header gives as the reason the published
366+ * copy is watched at all. So the reason did not hold here, and the skip hid 32
367+ * of 72 markdown files (44%) from the half of this gate whose entire job is to
368+ * notice a copy of the frame appearing somewhere new. A scan that cannot see
369+ * 44% of its population is the #4690 anti-pattern one directory level down: it
370+ * runs, it passes, and it reports nothing about the files it never opened.
371+ *
372+ * Two sibling gates over this same corpus already carry the corrected verdict.
373+ * `check-skill-identifier-liveness.mjs` keeps `references` OUT of its skip set
374+ * and pins the difference in its self-test ("under `skills/**` those files are
375+ * hand-authored published content, unlike content/docs/references which is
376+ * generated"). `check-doc-authoring.mjs` gave its published-catalog rule a
377+ * SECOND walk rather than reuse the skipping one, because reusing it "would
378+ * have produced a gate that runs, passes, and cannot see a ninth of the
379+ * population it exists to guard".
380+ *
381+ * Hence no narrowed carve-out for the generated pages under `skills/**` either
382+ * (`references/_index.md`, `references/react-blocks.md`). That is the same
383+ * refusal `check-doc-authoring.mjs` records beside them — "an exemption over a
384+ * surface that no longer needs one is where the next regeneration would smuggle
385+ * one back in. A red here is fixed AT THE SPEC SOURCE, never by hand-editing
386+ * the artifact" — and it holds a fortiori here: a fingerprint hit in a
387+ * generated page would mean the GENERATOR is emitting a copy of the frame,
388+ * which is a real finding whose fix site is the generator, not a false one.
389+ *
390+ * The self-test pins this from the WALK's own output rather than as a file
391+ * count: a count has to be re-typed whenever a page is added, and the failure
392+ * it must catch — "the scan reaches no reference page at all" — is not a
393+ * statement a count can make.
394+ */
395+ const SCAN_SKIP_DIRS = new Set ( [ 'node_modules' , '.git' , 'dist' ] ) ;
346396const SCAN_EXTENSIONS = [ '.md' , '.mdx' ] ;
347397
348398function matchAllOf ( text , source ) {
@@ -935,11 +985,48 @@ function selfTest() {
935985 for ( const f of declFailures ) console . error ( ` ✗ dispatch-gates declaration: ${ f } ` ) ;
936986 failed += declFailures . length ;
937987
988+ // ── The scan population (#15056) ──────────────────────────────────────────
989+ //
990+ // What SCAN_SKIP_DIRS leaves out IS the anti-dormancy half's reach, and a
991+ // skip is invisible in the pass line: "40 markdown files scanned" reads
992+ // exactly like 72 to anyone not counting. `references` sat in that set for
993+ // reasons belonging to a corpus this gate does not walk, and hid 44% of the
994+ // population — see the SCAN_SKIP_DIRS docblock.
995+ //
996+ // Pinned from the WALK, on the real tree, by the same call `main()` makes —
997+ // never as a hand-typed file count. A count has to be re-typed whenever a
998+ // page is added, and it cannot state the thing that actually went wrong:
999+ // "the scan reaches no reference page at all".
1000+ const INSTALLED_OR_GENERATED = new Set ( [ 'node_modules' , '.git' , 'dist' ] ) ;
1001+ const popFailures = [ ] ;
1002+ let popCases = 0 ;
1003+ const pop = ( label , ok ) => { popCases += 1 ; if ( ! ok ) popFailures . push ( label ) ; } ;
1004+ const walked = SCAN_ROOTS . flatMap ( ( root ) => walkMarkdown ( root , [ ] ) ) ;
1005+ const inReferences = walked . filter ( ( f ) => / ( ^ | [ \\ / ] ) r e f e r e n c e s [ \\ / ] / . test ( f . file ) ) ;
1006+ pop ( 'the walk reaches the `references/` subtrees under SCAN_ROOTS — hand-written '
1007+ + 'operative prose, not an installed or generated tree. Restoring the skip empties '
1008+ + 'this and every gate stays green, which is the defect this case exists for' ,
1009+ inReferences . length > 0 ) ;
1010+ pop ( 'the walk reaches more than those subtrees, so the case above is judging a real '
1011+ + 'population rather than passing on a coincidence' ,
1012+ walked . length > inReferences . length ) ;
1013+ pop ( 'every SCAN_SKIP_DIRS entry is DECLARED installed-or-generated here — the '
1014+ + 'criterion the three surviving entries meet and `references` never did, so a '
1015+ + 'fourth entry has to be stated rather than appended' ,
1016+ [ ...SCAN_SKIP_DIRS ] . every ( ( d ) => INSTALLED_OR_GENERATED . has ( d ) ) ) ;
1017+ for ( const f of popFailures ) console . error ( ` ✗ scan population: ${ f } ` ) ;
1018+ failed += popFailures . length ;
1019+
9381020 if ( failed > 0 ) {
9391021 console . error ( `\n✗ check-skill-frame-sync self-test failed (${ failed } case(s)).` ) ;
9401022 process . exit ( 1 ) ;
9411023 }
942- console . log ( `✓ check-skill-frame-sync self-test: ${ cases . length } cases pass, plus 5 dispatch-gates declaration cases.` ) ;
1024+ console . log (
1025+ `✓ check-skill-frame-sync self-test: ${ cases . length } cases pass, plus 5 `
1026+ + `dispatch-gates declaration cases and ${ popCases } scan-population cases `
1027+ + `(${ walked . length } markdown files walked under the scan roots, `
1028+ + `${ inReferences . length } of them inside a references/ directory).` ,
1029+ ) ;
9431030 selfTestReachedVerdict = true ;
9441031}
9451032
0 commit comments