Skip to content

Commit bcf4319

Browse files
committed
test(pm): pin the clause-② test exception in dispatch-gates' self-test
The lit case (a test-only spec diff) raises no suspicion and --tier prints no SUSPECT line; a *.zod.ts diff still does; each of the four ruled test shapes is excepted; a mixed diff keeps its contract file; qa/testing.zod.ts stays a suspect; the exception is the imported predicate and never drops a tracked *.zod.ts on this tree. Claude-Session: https://claude.ai/code/session_01A22sUB3mUWs6M36VgfijBq Co-authored-by: Claude <noreply@anthropic.com>
1 parent 639f172 commit bcf4319

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

‎scripts/pm/dispatch-gates.mjs‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25619,6 +25619,51 @@ function selfTest() {
2561925619
t('a mandated surface still prints its suspect paths — the enqueue gate reads diffs, not dispatch tiers', mandatedAndSuspect.mandatory && mandatedAndSuspect.suspects.length === 1 && tierLines(mandatedAndSuspect).join('\n').includes('SUSPECT'));
2562025620
t('a verdict built without a suspects field still renders (suspicion defaults empty)', tierLines({ mandatory: false, tier: null, hits: [], declared: 1 }).length === 3);
2562125621

25622+
// ── Test files under the contract surface are EXCEPTED (#19936) ──────────
25623+
//
25624+
// The path limb must read what the review rule reads — non-test contract
25625+
// files — or a test-only spec PR from an off-tier seat can never enqueue.
25626+
// Ruling 5805897677 (letter A) names the pins: a test-only spec diff shows
25627+
// no SUSPECT line, a `*.zod.ts` diff still does, and PR #19932's one file is
25628+
// the lit case. The shapes are the four the ruling lists; the paths are
25629+
// judged by the pure function, so a hypothetical one decides as well as a
25630+
// tracked one does.
25631+
const LIT_TEST_ONLY = 'packages/spec/src/type-alias-convention.pin.test.ts';
25632+
const litTestOnly = fableOf([LIT_TEST_ONLY]);
25633+
const litRendered = tierLines(litTestOnly).join('\n');
25634+
t('⭐ the lit case — a test-only spec diff, PR #19932\'s one file — raises NO suspicion', litTestOnly.suspects.length === 0 && !litTestOnly.mandatory, litTestOnly.suspects);
25635+
t('…renders no SUSPECT line, and still prints the clause-② note, so the silence is not a clearance', !litRendered.includes('SUSPECT') && litRendered.includes('Clause ② is NOT reachable from paths'), litRendered);
25636+
const zodStill = fableOf(['packages/spec/src/ui/view.zod.ts']);
25637+
t('⭐ a *.zod.ts contract schema is STILL a suspect and still renders the SUSPECT line', zodStill.suspects.length === 1 && tierLines(zodStill).join('\n').includes('SUSPECT'));
25638+
for (const [shape, path] of [
25639+
['a plain *.test.ts', 'packages/spec/src/stack.test.ts'],
25640+
['a *.pin.test.ts', LIT_TEST_ONLY],
25641+
['a helper under __tests__/', 'packages/spec/src/data/__tests__/filter-helpers.ts'],
25642+
['a fixture under a __tests__/ directory', 'packages/spec/src/data/__tests__/fixtures/filter.fixture.json'],
25643+
['a fixture under a test/ directory', 'packages/spec/src/ui/test/fixtures/view-fixture.ts'],
25644+
]) {
25645+
t(`${shape} on the contract surface is excepted — no suspicion`, fableOf([path]).suspects.length === 0, path);
25646+
}
25647+
const mixedDiff = fableOf([LIT_TEST_ONLY, 'packages/spec/src/ui/view.zod.ts']);
25648+
t('a MIXED diff keeps its contract file\'s suspicion — the exception is per path, never per diff', mixedDiff.suspects.length === 1 && mixedDiff.suspects[0].path === 'packages/spec/src/ui/view.zod.ts', mixedDiff.suspects);
25649+
t('⛔ a contract domain whose NAME reads test-flavoured is no test: qa/testing.zod.ts stays a suspect', fableOf(['packages/spec/src/qa/testing.zod.ts']).suspects.length === 1);
25650+
t('…while the test file beside it is excepted', fableOf(['packages/spec/src/qa/testing.test.ts']).suspects.length === 0);
25651+
t('an input that CONTAINS the contract surface is still a suspect — the exception narrows files, never the reverse match', fableOf(['packages/spec']).suspects.length === 1);
25652+
t('a mandate is never excepted: a test-named file under a mandated root keeps its mandate', fableOf(['skills/objectstack-data/x.test.ts']).mandatory === true);
25653+
const contractEntry = SUSPECT_TIER_GLOBS.find((g) => g.glob === 'packages/spec/src/**');
25654+
t('the exception is the IMPORTED test-file predicate — the repo\'s own, never a respelling here', contractEntry?.except === isTestPath);
25655+
t('…whose home is a gate script, so a card editing it derives this gate by gate-script identity', isGateScriptPath('scripts/check-undeclared-dep-imports.mjs', gateFamilyFiles()));
25656+
t('every suspect entry that excepts carries a predicate and the reason for it', SUSPECT_TIER_GLOBS.every((g) => g.except === undefined || (typeof g.except === 'function' && typeof g.exceptWhy === 'string' && g.exceptWhy.length > 0)));
25657+
// The subtraction held LIVE, because a subtraction fails silent: the
25658+
// package's files[] ships every `*.zod.ts` under `src/` verbatim, so an
25659+
// exception that dropped one would take a shipped contract off the limb.
25660+
const specSrcTracked = trackedFiles().filter((f) => f.startsWith('packages/spec/src/'));
25661+
const specSrcZod = specSrcTracked.filter((f) => f.endsWith('.zod.ts'));
25662+
const exceptedZod = specSrcZod.filter((f) => fableOf([f]).suspects.length === 0);
25663+
t(`no tracked *.zod.ts on the contract surface is excepted (${specSrcZod.length} read; excepted: ${exceptedZod.join(', ') || 'none'})`, specSrcZod.length > 0 && exceptedZod.length === 0);
25664+
const exceptedTracked = specSrcTracked.filter((f) => fableOf([f]).suspects.length === 0);
25665+
t('the exception is live, not vacuous: it drops tracked test files on this tree, and only what the predicate names', exceptedTracked.length > 0 && exceptedTracked.every((f) => isTestPath(f)), `${exceptedTracked.length} of ${specSrcTracked.length}`);
25666+
2562225667
// ── The changed-line reading beside the tier verdict (2026-09-18 ruling) ──
2562325668
const overLine = changedLineLines({ additions: HUMAN_MERGE_LINE_THRESHOLD, deletions: 1 }).join('\n');
2562425669
t('over the threshold, the line says HUMAN MERGE, names the governed terminal and the landing pre-check',
@@ -26556,6 +26601,14 @@ function selfTest() {
2655626601
t('⭐ a second catalog SKILL.md prints the same mandate', catalogAiCli.status === 0 && (catalogAiCli.stdout ?? '').includes('MANDATORY') && (catalogAiCli.stdout ?? '').includes("'skills/**'"));
2655726602
const internalRefsCli = runCli(['--tier', '.claude/skills/pm-dispatch/references/state-machine.md']);
2655826603
t('⭐ --tier on an internal pm-dispatch references file still prints NO mandate', internalRefsCli.status === 0 && (internalRefsCli.stdout ?? '').includes('no path-derived mandate') && !(internalRefsCli.stdout ?? '').includes('MANDATORY'));
26604+
// The clause-② test exception on the real CLI (#19936): the ruling's pins are
26605+
// on `--tier` OUTPUT, so both acceptance paths are measured end to end. The
26606+
// lit case goes through the asserted helper, so a later rename of that file
26607+
// leaves the pin deciding rather than refusing as an absent path.
26608+
const litTierCli = runCliHypothetical(['--tier', 'packages/spec/src/type-alias-convention.pin.test.ts']);
26609+
t('⭐ --tier on the lit case (a test-only spec diff) derives, and prints NO SUSPECT line', litTierCli.status === 0 && (litTierCli.stdout ?? '').includes('no path-derived mandate') && !(litTierCli.stdout ?? '').includes('SUSPECT'), litTierCli.stdout);
26610+
const zodTierCli = runCliHypothetical(['--tier', 'packages/spec/src/ui/view.zod.ts']);
26611+
t('⭐ --tier on a *.zod.ts contract schema still prints the SUSPECT line naming it', zodTierCli.status === 0 && (zodTierCli.stdout ?? '').includes('SUSPECT') && (zodTierCli.stdout ?? '').includes('packages/spec/src/ui/view.zod.ts'), zodTierCli.stdout);
2655926612

2656026613
// ── The entry guard (#9757) ───────────────────────────────────────────────
2656126614
//

0 commit comments

Comments
 (0)