Skip to content

Commit 4d80569

Browse files
fix(scripts): a diff of sdui.manifest.json alone now derives pnpm check:sdui-reference (#27528)
<!-- claude-projects-attribution: {"github_login":"marchtian"} --> _Requested by **田騏** · [project thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSi8irujFEnBnqcKvK4Kv1YDL)_ Fixes #27367 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`scripts/gen-sdui-reference.mjs` 新增一行 `ROOT_FILE_WATCH_HINTS = ['sdui.manifest.json/**']` 声明它读取仓根清单,并在其自测里校验该声明与 `MANIFEST_PATH` 一致;`dispatch-gates` 自测加两条用例钉住这一推导。 - 为什么改:只改 `sdui.manifest.json` 的 PR 之前推导不出 `pnpm check:sdui-reference`,开发者照工具清单跑完仍会在 CI 变红;现在能推导出来。 - 风险与回滚:只动仓根 `scripts/`,不发布任何包,不改匹配器;回滚即 revert 本 PR。 - 先看:`scripts/gen-sdui-reference.mjs`(声明与自测里的声明校验)、`scripts/pm/dispatch-gates.self-test.mjs`(两条新用例)。 - 两分钟验证:`node scripts/pm/dispatch-gates.mjs --commands sdui.manifest.json`,预期输出里出现 `pnpm check:sdui-reference`(main 上没有)。 - 需要你做的:无,席位落地。 ## What changes - `scripts/gen-sdui-reference.mjs`: declares the repo-root manifest it reads as `ROOT_FILE_WATCH_HINTS = ['sdui.manifest.json/**']`, the same hint `scripts/check-sdui-manifest.mjs` declares for that file. The bare `MANIFEST_PATH` literal has no separator, and `hintCovers` refuses that shape as too generic, so only `OUT_DIR` was visible to the derivation. Per the triage ruling, the matcher is not widened. The fix is this declaration. - The same file's `--self-test` now checks the declaration, the way `check-sdui-manifest.mjs` checks its own (outside the cases table, so the battery roster and floor do not change). It fails when the declared paths are not exactly `[MANIFEST_PATH]`, and when a repo-root entry is missing the `/**` suffix. - `scripts/pm/dispatch-gates.self-test.mjs`: two cases beside the existing repo-root-file declarations. They run against the live fleet (`discoverFamilies()` + `placeFamily`). (1) A diff of `sdui.manifest.json` alone places `check:sdui-reference` as `matched`. (2) `examples/sdui.manifest.json` does not, so the declaration covers only the one file it names. - ⛔ No change to `scripts/pm/dispatch-gates.mjs` or `dispatch-gates.data.mjs`. No changeset: root `scripts/` belongs to no published package (the root manifest `@objectstack/spec-monorepo` is `private: true`), so this PR carries `skip-changeset`. - Hint name: `ROOT_FILE_WATCH_HINTS`, not `ROOT_WATCH_HINTS`. `check-watch-hint-literal.mjs` defines the name as the kind of root: repo-root files are `ROOT_FILE_`, mixed roots are `ROOT_`. This population is one repo-root file. The hint value is byte-identical to the one `check-sdui-manifest.mjs` declares, and `check:watch-hint-literal` admits it (14 `ROOT_FILE_WATCH_HINTS` declarations, previously 13). ## Tests Tree: `objectstack-ai/objectstack` at `3e920461` (base `ded67bfb`). - `pnpm check:sdui-reference`: exit 0. `✓ gen-sdui-reference self-test: 20 cases pass.` and `✓ gen-sdui-reference --check: content/docs/ui/components/ matches sdui.manifest.json (106 components on 7 pages).` - `pnpm check:pm-dispatch-gates`: exit 0. `✓ dispatch-gates self-test (fast tier): 1937 cases pass; 7 slow section(s) deferred.` The two new cases ran in the fast tier: - `✓ a diff of sdui.manifest.json alone derives pnpm check:sdui-reference` - `✓ and that declaration claims no same-named file inside a directory` - `node scripts/check-sdui-manifest.mjs --self-test`: exit 0 (21 cases). - The new placement case fails on the base tree, measured with the same calls in a probe script. On `ded67bfb` the family's hints were `["content/docs/ui/components"]` and `placeFamily(..., ['sdui.manifest.json']).verdict` was `silent`. On `3e920461` the hints are `["content/docs/ui/components","sdui.manifest.json/**"]` and the verdict is `matched`. `ui/apps/console/sdui.manifest.json` stays `silent`. ## Acceptance notes - No other gate was surveyed for the same miss (a root-level file read with no hint). Per the triage, finding many such gates would be a card for a lint over gate scripts, not a matcher change. Nothing here measured how many there are. ## Verification <details><summary>Before / after: dispatch-gates --commands sdui.manifest.json</summary> Before, on `ded67bfb` (11 commands, no `check:sdui-reference`): ```text node scripts/check-closing-keyword-parity.mjs node scripts/check-closing-keyword-parity.mjs --self-test node scripts/check-comment-mask-corpus.mjs node scripts/check-sdui-manifest.mjs node scripts/check-sdui-manifest.mjs --self-test node scripts/ci/plan-test-core-shards.mjs --self-test pnpm check:driver-memory-census pnpm check:gitlink-declared pnpm check:nul-bytes pnpm check:refd-timer-probe pnpm check:watch-hint-literal ``` After, on `3e920461` (12 commands): the same 11 rows plus `pnpm check:sdui-reference`. `diff before after` prints exactly `> pnpm check:sdui-reference`. Footer moves from `11 command(s) — 5 pnpm, 6 direct node (3 matched by path …)` to `12 command(s) — 6 pnpm, 6 direct node (4 matched by path …)`. </details> <details><summary>Local gates derived for this diff (31), with exit codes</summary> `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `3e920461` derived 31 commands. Each one ran with exit 0: `check-closing-keyword-parity` (+ `--self-test`), `check-comment-mask-corpus`, `check-declaration-mirrors` (+ `--self-test`), `check-self-test-wired` (+ `--self-test`), `check-self-test-workflow-commands` (+ `--self-test`), `check-whole-set-label-write` (+ `--self-test`), `ci/plan-test-core-shards --self-test`, `pm/bare-root-worklist --self-test`, and `pnpm check:` `agent-test-spelling`, `bash32-floor`, `ci-filter-parity`, `cli-command-ids`, `cross-package-test-inputs`, `driver-memory-census`, `entry-guard`, `gitlink-declared`, `nul-bytes`, `parse-guard`, `pm-dispatch-gates`, `pnpm-filter-targets`, `pnpm-flag-spellings`, `ratchet-remedy-authority`, `refd-timer-probe`, `scripts-symbol-anchors`, `sdui-reference`, `watch-hint-literal`. Reconciliation, `--ran` over a record carrying an exit code on every row: `✓ dispatch-gates --ran: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of them is 3).` CI-only steps that `--commands` does not print (Plan Test Core shards and the type-check lanes) are left to this PR's check-runs. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0368504 commit 4d80569

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

‎scripts/gen-sdui-reference.mjs‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ import { isEntrypoint } from './invoked-as.mjs';
6363
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
6464
const MANIFEST_PATH = 'sdui.manifest.json';
6565
const OUT_DIR = 'content/docs/ui/components';
66+
67+
/**
68+
* The repo-root file this generator reads, declared for `scripts/pm/dispatch-gates.mjs`.
69+
*
70+
* The derivation reads SOURCE TEXT, and `MANIFEST_PATH` above is a literal with
71+
* no separator, which `hintCovers` refuses as too generic. `OUT_DIR` was always
72+
* visible and the manifest was not, so a diff that changed only the manifest
73+
* never derived `pnpm check:sdui-reference` -- the one diff this gate exists to
74+
* catch, since a regenerated manifest beside a stale reference reds it in CI.
75+
*
76+
* The `/**` suffix is the repo-root-file spelling of the idiom (the same one
77+
* `scripts/check-sdui-manifest.mjs` declares for this file): a bare
78+
* single-segment literal builds no hint, and the collapse reduces this one back
79+
* to the single file it names and to nothing else. The self-test holds it equal
80+
* to `MANIFEST_PATH`, so a moved read reds there instead of leaving the
81+
* declaration describing the old file.
82+
*/
83+
const ROOT_FILE_WATCH_HINTS = ['sdui.manifest.json/**'];
6684
const URL_BASE = '/docs/ui/components';
6785
const COMMAND = 'node scripts/gen-sdui-reference.mjs';
6886

@@ -657,6 +675,23 @@ function selfTest() {
657675
}
658676
}
659677

678+
// ── The dispatch-gates population declaration ──────────────────────────
679+
// Outside the cases table on purpose: each row there is a declared battery
680+
// about the rendering, and this asserts what the derivation can see.
681+
const declFail = (message) => {
682+
console.error(`✗ self-test: ${message}`);
683+
failed += 1;
684+
};
685+
const declaredReads = ROOT_FILE_WATCH_HINTS.map((h) => h.replace(/\/\*+$/, ''));
686+
if (declaredReads.length !== 1 || declaredReads[0] !== MANIFEST_PATH) {
687+
declFail(`ROOT_FILE_WATCH_HINTS declares ${JSON.stringify(declaredReads)}, not the manifest this script reads `
688+
+ `(${MANIFEST_PATH}) — an undeclared read is how a manifest-only diff stopped deriving this gate.`);
689+
}
690+
if (!ROOT_FILE_WATCH_HINTS.filter((h) => !h.replace(/\/\*+$/, '').includes('/')).every((h) => h.endsWith('/**'))) {
691+
declFail('a repo-ROOT file is declared without the subtree suffix — a bare single-segment literal is '
692+
+ 'refused by hintCovers as too generic, so it would contribute no hint at all.');
693+
}
694+
660695
const floorFailure = (message) => {
661696
console.error(`✗ self-test floor: ${message}`);
662697
failed += 1;

‎scripts/pm/dispatch-gates.self-test.mjs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,6 +3525,15 @@ export function selfTest({ tier = 'full' } = {}) {
35253525
t('a one-root declaration does not reach the other root file', !proseHints.some((h) => hintCovers(h, 'CLAUDE.md')));
35263526
const anchorRootHints = extractWatchHints(readFileSync(nodePath.join(ROOT, 'scripts/check-doc-anchors.mjs'), 'utf8'), 'scripts/check-doc-anchors.mjs');
35273527
t('and the doc-anchors pair claims neither instruction file', !anchorRootHints.some((h) => hintCovers(h, 'AGENTS.md') || hintCovers(h, 'CLAUDE.md')));
3528+
// The same class on a non-instruction root file, driven end to end through the
3529+
// live fleet: `check:sdui-reference` reads the repo-root `sdui.manifest.json`
3530+
// and reds in CI when the manifest moves without the reference, so the
3531+
// manifest alone must place it. Fixed by the gate's own declaration, never by
3532+
// admitting bare root literals here. If the gate stops reading the manifest,
3533+
// delete these cases with the declaration.
3534+
const sduiReference = discoverFamilies().byCheck.get('check:sdui-reference');
3535+
t('a diff of sdui.manifest.json alone derives pnpm check:sdui-reference', Boolean(sduiReference) && placeFamily(sduiReference, ['sdui.manifest.json']).verdict === 'matched');
3536+
t('and that declaration claims no same-named file inside a directory', Boolean(sduiReference) && placeFamily(sduiReference, ['examples/sdui.manifest.json']).verdict !== 'matched');
35283537

35293538
// A THIRD shape of the same class, and the one with the worst failure
35303539
// direction (#13207): a gate whose declared population was its OWN GUARDED

0 commit comments

Comments
 (0)