From 3e920461ae022b83364fdbb78ac47e47eeb5d88f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 10:43:14 +0000 Subject: [PATCH] fix(scripts): declare the manifest read of gen-sdui-reference for dispatch-gates The generator reads the repo-root sdui.manifest.json through a bare single-segment literal, which the derivation refuses as too generic, so a diff that changed only the manifest never derived pnpm check:sdui-reference. Declare it with the repo-root-file hint idiom (sdui.manifest.json/**), hold the declaration equal to MANIFEST_PATH in the generator's self-test, and pin the placement against the live fleet in the dispatch-gates self-test. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU --- scripts/gen-sdui-reference.mjs | 35 +++++++++++++++++++++++++ scripts/pm/dispatch-gates.self-test.mjs | 9 +++++++ 2 files changed, 44 insertions(+) diff --git a/scripts/gen-sdui-reference.mjs b/scripts/gen-sdui-reference.mjs index eaacb0e2d2e..8075bfc1ff6 100644 --- a/scripts/gen-sdui-reference.mjs +++ b/scripts/gen-sdui-reference.mjs @@ -63,6 +63,24 @@ import { isEntrypoint } from './invoked-as.mjs'; const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); const MANIFEST_PATH = 'sdui.manifest.json'; const OUT_DIR = 'content/docs/ui/components'; + +/** + * The repo-root file this generator reads, declared for `scripts/pm/dispatch-gates.mjs`. + * + * The derivation reads SOURCE TEXT, and `MANIFEST_PATH` above is a literal with + * no separator, which `hintCovers` refuses as too generic. `OUT_DIR` was always + * visible and the manifest was not, so a diff that changed only the manifest + * never derived `pnpm check:sdui-reference` -- the one diff this gate exists to + * catch, since a regenerated manifest beside a stale reference reds it in CI. + * + * The `/**` suffix is the repo-root-file spelling of the idiom (the same one + * `scripts/check-sdui-manifest.mjs` declares for this file): a bare + * single-segment literal builds no hint, and the collapse reduces this one back + * to the single file it names and to nothing else. The self-test holds it equal + * to `MANIFEST_PATH`, so a moved read reds there instead of leaving the + * declaration describing the old file. + */ +const ROOT_FILE_WATCH_HINTS = ['sdui.manifest.json/**']; const URL_BASE = '/docs/ui/components'; const COMMAND = 'node scripts/gen-sdui-reference.mjs'; @@ -657,6 +675,23 @@ function selfTest() { } } + // ── The dispatch-gates population declaration ────────────────────────── + // Outside the cases table on purpose: each row there is a declared battery + // about the rendering, and this asserts what the derivation can see. + const declFail = (message) => { + console.error(`✗ self-test: ${message}`); + failed += 1; + }; + const declaredReads = ROOT_FILE_WATCH_HINTS.map((h) => h.replace(/\/\*+$/, '')); + if (declaredReads.length !== 1 || declaredReads[0] !== MANIFEST_PATH) { + declFail(`ROOT_FILE_WATCH_HINTS declares ${JSON.stringify(declaredReads)}, not the manifest this script reads ` + + `(${MANIFEST_PATH}) — an undeclared read is how a manifest-only diff stopped deriving this gate.`); + } + if (!ROOT_FILE_WATCH_HINTS.filter((h) => !h.replace(/\/\*+$/, '').includes('/')).every((h) => h.endsWith('/**'))) { + declFail('a repo-ROOT file is declared without the subtree suffix — a bare single-segment literal is ' + + 'refused by hintCovers as too generic, so it would contribute no hint at all.'); + } + const floorFailure = (message) => { console.error(`✗ self-test floor: ${message}`); failed += 1; diff --git a/scripts/pm/dispatch-gates.self-test.mjs b/scripts/pm/dispatch-gates.self-test.mjs index b2373bc8e79..4ed0a5e9c21 100644 --- a/scripts/pm/dispatch-gates.self-test.mjs +++ b/scripts/pm/dispatch-gates.self-test.mjs @@ -3525,6 +3525,15 @@ export function selfTest({ tier = 'full' } = {}) { t('a one-root declaration does not reach the other root file', !proseHints.some((h) => hintCovers(h, 'CLAUDE.md'))); const anchorRootHints = extractWatchHints(readFileSync(nodePath.join(ROOT, 'scripts/check-doc-anchors.mjs'), 'utf8'), 'scripts/check-doc-anchors.mjs'); t('and the doc-anchors pair claims neither instruction file', !anchorRootHints.some((h) => hintCovers(h, 'AGENTS.md') || hintCovers(h, 'CLAUDE.md'))); + // The same class on a non-instruction root file, driven end to end through the + // live fleet: `check:sdui-reference` reads the repo-root `sdui.manifest.json` + // and reds in CI when the manifest moves without the reference, so the + // manifest alone must place it. Fixed by the gate's own declaration, never by + // admitting bare root literals here. If the gate stops reading the manifest, + // delete these cases with the declaration. + const sduiReference = discoverFamilies().byCheck.get('check:sdui-reference'); + t('a diff of sdui.manifest.json alone derives pnpm check:sdui-reference', Boolean(sduiReference) && placeFamily(sduiReference, ['sdui.manifest.json']).verdict === 'matched'); + t('and that declaration claims no same-named file inside a directory', Boolean(sduiReference) && placeFamily(sduiReference, ['examples/sdui.manifest.json']).verdict !== 'matched'); // A THIRD shape of the same class, and the one with the worst failure // direction (#13207): a gate whose declared population was its OWN GUARDED