Skip to content

Commit e5d530e

Browse files
claude[bot]claude
andauthored
Declare the scripts/ population two gates walk but never named (#14692)
`scripts/pm/dispatch-gates.mjs` builds every dispatch's gate list by scanning a gate's own source for the path literals it operates on, and "looks like a path" there means "carries a separator". Both gates below seed a recursive walk at the bare single-segment word `scripts`, which the extractor cannot see at all, so the only literals recoverable from either file named individual artifacts: the six scripts one gate's ledger cites by name, and two `owner/repo` action slugs plus sandbox filenames in the other. An artifact roster is not a population -- a list of the files that already exist can never contain the one added tomorrow -- so both families walked all of `scripts/` and appeared on no card that edited any of it. Each now declares that population in its own module body under the ROOT_DIR_WATCH_HINTS idiom, pinned in its own --self-test against the LIVE walk in both directions: nothing walked left uncovered, nothing covered left unwalked. One hint per admitted extension rather than the bare subtree, following check-ratchet-remedy-authority.mjs at this same root -- `scripts/**` would name both gates for the 91 JSON, Markdown and text files under the root that neither one opens. An extension the filter admits but the tree does not yet hold is deliberately absent, because a hint reaching nothing tracked is a dead lead the consumer reports as a population; the pins redden the day such a file lands. Measured on this tree with the derivation's own --residue, probe scripts/check-nul-bytes.mjs: 14 matched families -> 16, Silent 148 -> 146. With an outside probe (packages/metadata-protocol/src/protocol.ts) both stay Silent, so the declaration does not over-claim. Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0e01dde commit e5d530e

2 files changed

Lines changed: 212 additions & 1 deletion

File tree

scripts/check-self-test-wired.mjs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,41 @@ const RATCHET_AUTHORITY_MARKER = '⛔ MAINTAINER-ONLY';
116116
/** Extensions whose files can be a `scripts/` entry point. */
117117
const SCRIPT_EXT = /\.(mjs|mts|js|sh)$/;
118118

119+
/**
120+
* POPULATION DECLARATION -- what `scripts/pm/dispatch-gates.mjs` is told this
121+
* gate reads, in the subtree spelling that tool compares in. Provenance ONLY:
122+
* nothing in this file reads this array.
123+
*
124+
* That tool builds every dispatch's gate list by scanning a gate's own source
125+
* for the path literals it operates on, and "looks like a path" there means
126+
* "carries a separator". This gate's corpus root arrives as
127+
* `join(ROOT, 'scripts')` -- a bare single-segment word -- so the only literals
128+
* the extractor could recover from this file were the workflow directory and
129+
* the handful of individual scripts the ledger below cites BY NAME. That is an
130+
* artifact roster, not a population: a list of the files that already exist can
131+
* never contain the one added tomorrow. The measured result was a family that
132+
* walks all of `scripts/` and appeared on no card that edited any of it.
133+
*
134+
* ⛔ NOT the bare subtree. One hint per admitted extension, following
135+
* `check-ratchet-remedy-authority.mjs` at this same root: `scripts/**` would
136+
* name this gate for the JSON, Markdown and text files under the root that
137+
* `walkScripts` never opens. The declared set is SET-EQUAL to that walk --
138+
* nothing walked left uncovered, nothing covered left unwalked -- which is what
139+
* `--self-test` pins, in both directions and against the live tree.
140+
*
141+
* An extension `SCRIPT_EXT` admits but the tree does not yet HOLD is
142+
* deliberately absent: a hint reaching nothing tracked is a dead lead, which
143+
* the consumer reports as a population and is not one. The pin below reddens
144+
* the day such a file lands, which is the coupling that keeps this honest.
145+
*
146+
* Spelled as a LITERAL array, never computed from `SCRIPT_EXT`: the extractor
147+
* reads SOURCE TEXT, so a built spelling keeps this value identical at runtime,
148+
* keeps every assertion about it green, and contributes ZERO hints.
149+
* `check-watch-hint-literal.mjs` holds that rule fleet-wide; the self-test
150+
* below holds the own-source half.
151+
*/
152+
const ROOT_DIR_WATCH_HINTS = ['scripts/**/*.mjs', 'scripts/**/*.mts', 'scripts/**/*.sh'];
153+
119154
/**
120155
* A `scripts/...` path, optionally followed by `--self-test`.
121156
*
@@ -463,14 +498,15 @@ const SELF_TEST_BATTERIES = Object.freeze({
463498
'right boundary': 4,
464499
'alias resolution': 4,
465500
'population verdict': 4,
501+
'population declaration': 7,
466502
'ledger hygiene': 9,
467503
'live ledger': 4,
468504
});
469505

470506
// DELETING an entry silences that battery's floor exactly as effectively as
471507
// zeroing it, so the registry's own size is pinned too. Adding a battery raises
472508
// this number; removing one is the same ⛔ deliberate edit as lowering a count.
473-
const SELF_TEST_BATTERY_FLOOR = 6;
509+
const SELF_TEST_BATTERY_FLOOR = 7;
474510

475511
// The key an assertion is filed under when no battery is open. It is not a
476512
// declared battery, so it reds by the same set difference rather than silently
@@ -597,6 +633,75 @@ function selfTest() {
597633
);
598634
}
599635

636+
// ── POPULATION DECLARATION: what the dispatch derivation is told this gate reads ──
637+
//
638+
// Nothing in this file can ENFORCE the declaration: `ROOT_DIR_WATCH_HINTS` is
639+
// read by another tool entirely (`extractWatchHints` in
640+
// `scripts/pm/dispatch-gates.mjs`), so a stale or wrong one runs green here
641+
// forever and pays itself out as a dev dispatched on a `scripts/` card with
642+
// this gate absent from the brief -- the exact round this declaration was
643+
// added to end. So the pins below hold it against the LIVE WALK rather than
644+
// against a fixture: a sandbox tree would keep them green while the real
645+
// declaration drifted.
646+
battery('population declaration');
647+
{
648+
const walked = walkScripts(join(ROOT, 'scripts'));
649+
const extOf = (path) => path.slice(path.lastIndexOf('.'));
650+
// Written INDEPENDENTLY of `hintCovers` on purpose: a pin that reuses the
651+
// consumer's own matcher cannot catch the consumer changing under it.
652+
const declares = (path) =>
653+
path.startsWith('scripts/') && ROOT_DIR_WATCH_HINTS.includes(`scripts/**/*${extOf(path)}`);
654+
655+
ok(
656+
walked.length > 0,
657+
'the population pin walked NO files — a broken walk proves nothing about the declaration (#4690)',
658+
);
659+
ok(
660+
walked.every(declares),
661+
'a file this gate WALKS is left undeclared — the declaration under-names the population it exists '
662+
+ 'to publish, which is the silence it was added to end',
663+
);
664+
ok(
665+
ROOT_DIR_WATCH_HINTS.every((hint) => walked.some((path) => declares(path) && `scripts/**/*${extOf(path)}` === hint)),
666+
'a declared hint reaches nothing this gate walks — a dead lead, which the consumer reports as a '
667+
+ 'population and is not one',
668+
);
669+
ok(
670+
ROOT_DIR_WATCH_HINTS.every((hint) => SCRIPT_EXT.test(hint)),
671+
'a declared hint names an extension SCRIPT_EXT does not admit — the declaration over-names the walk, '
672+
+ 'and a declaration that can drift from the walk is worse than none',
673+
);
674+
ok(
675+
!ROOT_DIR_WATCH_HINTS.some((hint) => hint === 'scripts' || hint.endsWith('/**') || hint === '.' || hint === '**'),
676+
'the bare subtree or the repo root was declared — it would name this gate for every JSON, Markdown '
677+
+ 'and text file under the root that this gate never opens',
678+
);
679+
ok(
680+
ROOT_DIR_WATCH_HINTS.every((hint) => hint.includes('/')),
681+
'a declared literal carries no separator, so the consumer refuses it as too generic and it reaches nothing',
682+
);
683+
// The literal SPELLING is the whole mechanism: a value built from
684+
// SCRIPT_EXT would keep the runtime value identical, keep every assertion
685+
// above green, and contribute ZERO hints. `check-watch-hint-literal` owns
686+
// that rule fleet-wide; this is the own-source half.
687+
let ownSource = null;
688+
try {
689+
ownSource = readFileSync(join(ROOT, 'scripts/check-self-test-wired.mjs'), 'utf8');
690+
} catch {
691+
ownSource = null;
692+
}
693+
const declSites = ownSource === null
694+
? []
695+
: [...ownSource.matchAll(/\bconst\s+ROOT_DIR_WATCH_HINTS\s*=\s*([^;]*);/g)];
696+
ok(
697+
declSites.length === 1
698+
&& ROOT_DIR_WATCH_HINTS.every((hint) => declSites[0][1].includes(`'${hint}'`))
699+
&& !/[A-Za-z_$][\w$]*\s*\./.test(declSites[0][1]),
700+
'the declaration is not a single literal array of quoted strings — the extractor reads SOURCE TEXT, '
701+
+ 'so a computed spelling contributes nothing while every assertion above stays green',
702+
);
703+
}
704+
600705
// ── Ledger hygiene: every row must still be true, and still be needed ────
601706
battery('ledger hygiene');
602707
{

scripts/check-whole-set-label-write.mjs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,42 @@ export const SCANNED_EXTENSIONS = new Set(['.yml', '.yaml', '.mjs', '.js', '.cjs
156156

157157
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage']);
158158

159+
/**
160+
* POPULATION DECLARATION -- what `scripts/pm/dispatch-gates.mjs` is told this
161+
* gate reads, in the subtree spelling that tool compares in. Provenance ONLY:
162+
* nothing in this file reads this array.
163+
*
164+
* That tool builds every dispatch's gate list by scanning a gate's own source
165+
* for the path literals it operates on, and "looks like a path" there means
166+
* "carries a separator". Two of the three `ROOTS` carry one and are recovered
167+
* for free; the third is the bare single-segment word `scripts`, which the
168+
* extractor cannot see AT ALL. So the only other literals this file offered it
169+
* were two `owner/repo` action slugs and the sandbox filenames the fixtures
170+
* build -- an artifact roster, not a population. The measured result was a gate
171+
* that walks all of `scripts/` and appeared on no card that edited any of it,
172+
* while its two `.github` roots were named correctly the whole time.
173+
*
174+
* ⛔ NOT the bare subtree. One hint per admitted extension, following
175+
* `check-ratchet-remedy-authority.mjs` at this same root: `scripts/**` would
176+
* name this gate for the JSON, Markdown and text files under the root that
177+
* `walk` skips on `SCANNED_EXTENSIONS`. The declared set is SET-EQUAL to what
178+
* that walk admits under this root -- nothing walked left uncovered, nothing
179+
* covered left unwalked -- which is what `--self-test` pins, in both directions
180+
* and against the live tree.
181+
*
182+
* An extension `SCANNED_EXTENSIONS` admits but the tree does not yet HOLD under
183+
* this root is deliberately absent: a hint reaching nothing tracked is a dead
184+
* lead, which the consumer reports as a population and is not one. The pin
185+
* below reddens the day such a file lands.
186+
*
187+
* Spelled as a LITERAL array, never computed from `SCANNED_EXTENSIONS`: the
188+
* extractor reads SOURCE TEXT, so a built spelling keeps this value identical
189+
* at runtime, keeps every assertion about it green, and contributes ZERO hints.
190+
* `check-watch-hint-literal.mjs` holds that rule fleet-wide; the self-test
191+
* below holds the own-source half.
192+
*/
193+
const ROOT_DIR_WATCH_HINTS = ['scripts/**/*.mjs', 'scripts/**/*.ts', 'scripts/**/*.sh'];
194+
159195
/**
160196
* How far apart the method slot and the `/labels` path may sit and still be
161197
* read as one call. A backslash-continued `curl` puts them 1-3 lines apart; a
@@ -898,6 +934,76 @@ export function selfTest() {
898934
failures.push(`WHOLE_SET_ACTIONS['${action}'] has no source-read reason`);
899935
}
900936

937+
// ── POPULATION DECLARATION: what the dispatch derivation is told this gate reads ──
938+
//
939+
// Nothing in this file can ENFORCE the declaration: `ROOT_DIR_WATCH_HINTS` is
940+
// read by another tool entirely (`extractWatchHints` in
941+
// `scripts/pm/dispatch-gates.mjs`), so a stale or wrong one runs green here
942+
// forever and pays itself out as a dev dispatched on a `scripts/` card with
943+
// this gate absent from the brief. Held against the LIVE WALK rather than a
944+
// fixture tree: the fixtures below build sandboxes, and a pin over one of
945+
// those would stay green while the real declaration drifted.
946+
{
947+
const walked = [];
948+
walk(REPO_ROOT, 'scripts', walked);
949+
const extOf = (path) => path.slice(path.lastIndexOf('.'));
950+
// Written INDEPENDENTLY of the consumer's `hintCovers`, deliberately: a pin
951+
// that reuses the consumer's own matcher cannot catch it changing underneath.
952+
const declares = (path) =>
953+
path.startsWith('scripts/') && ROOT_DIR_WATCH_HINTS.includes(`scripts/**/*${extOf(path)}`);
954+
955+
expect('POPULATION the declaration pin walked files at all (#4690)', walked.length > 0, true);
956+
expect(
957+
'POPULATION every file this gate walks under scripts/ is declared',
958+
walked.every(declares),
959+
true,
960+
);
961+
expect(
962+
'POPULATION every declared hint reaches a file this gate walks (no dead lead)',
963+
ROOT_DIR_WATCH_HINTS.every((hint) => walked.some((path) => declares(path) && `scripts/**/*${extOf(path)}` === hint)),
964+
true,
965+
);
966+
expect(
967+
'POPULATION every declared hint names an extension SCANNED_EXTENSIONS admits',
968+
ROOT_DIR_WATCH_HINTS.every((hint) => SCANNED_EXTENSIONS.has(extOf(hint))),
969+
true,
970+
);
971+
expect(
972+
'POPULATION the bare subtree and the repo root are NOT declared',
973+
ROOT_DIR_WATCH_HINTS.some((hint) => hint === 'scripts' || hint.endsWith('/**') || hint === '.' || hint === '**'),
974+
false,
975+
);
976+
expect(
977+
'POPULATION every declared literal carries a separator (a bare word reaches nothing)',
978+
ROOT_DIR_WATCH_HINTS.every((hint) => hint.includes('/')),
979+
true,
980+
);
981+
expect(
982+
'POPULATION the scripts root is the one ROOTS entry the extractor cannot see, and it is the one declared',
983+
ROOTS.filter((root) => !root.includes('/')).join(),
984+
'scripts',
985+
);
986+
// The literal SPELLING is the whole mechanism: a value built from
987+
// SCANNED_EXTENSIONS would keep every assertion above green and contribute
988+
// ZERO hints. `check-watch-hint-literal` owns that rule fleet-wide.
989+
let ownSource = null;
990+
try {
991+
ownSource = readFileSync(join(REPO_ROOT, 'scripts/check-whole-set-label-write.mjs'), 'utf8');
992+
} catch {
993+
ownSource = null;
994+
}
995+
const declSites = ownSource === null
996+
? []
997+
: [...ownSource.matchAll(/\bconst\s+ROOT_DIR_WATCH_HINTS\s*=\s*([^;]*);/g)];
998+
expect(
999+
'POPULATION declared exactly once, as an array of quoted literals the text scan can read',
1000+
declSites.length === 1
1001+
&& ROOT_DIR_WATCH_HINTS.every((hint) => declSites[0][1].includes(`'${hint}'`))
1002+
&& !/[A-Za-z_$][\w$]*\s*\./.test(declSites[0][1]),
1003+
true,
1004+
);
1005+
}
1006+
9011007
// The checked-in allowlist itself passes the reason rule.
9021008
for (const [index, entry] of ALLOWLIST.entries()) {
9031009
if (typeof entry.reason === 'string' && entry.reason.trim().length >= MIN_REASON_LENGTH) continue;

0 commit comments

Comments
 (0)