From 21b440337a803a0a9ea84756fc115a426ce7b7e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 16:42:10 +0000 Subject: [PATCH 1/5] wip(scripts): check:error-status-conformance declares its scan surface to the dispatch derivation Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q Co-authored-by: Claude --- scripts/check-error-status-conformance.mjs | 118 ++++++++++++++++++++- 1 file changed, 113 insertions(+), 5 deletions(-) diff --git a/scripts/check-error-status-conformance.mjs b/scripts/check-error-status-conformance.mjs index 309db59a18a..cb7834b282a 100644 --- a/scripts/check-error-status-conformance.mjs +++ b/scripts/check-error-status-conformance.mjs @@ -160,6 +160,7 @@ import { import { tmpdir } from 'node:os'; import { maskComments, maskCommentsAndLiterals } from './js-comment-mask.mjs'; import { basename, dirname, join, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { isEntrypoint } from './invoked-as.mjs'; // ── The self-test's own battery roster and floor (#13489) ────────────────── @@ -208,11 +209,12 @@ const SELF_TEST_BATTERIES = Object.freeze({ '26 — a code the DOOR TRANSLATES away is not a wire producer: derived from': 8, '27 — the vocabulary parse reads the enum ARRAY and stops at its bracket: an': 2, '28 — the corpus walk skips an entry that VANISHED between readdir and stat,': 10, + '29 — the dispatch-gates declaration: a source edit under the scan root derives this gate,': 11, }); // DELETING an entry silences that battery's floor exactly as effectively as // zeroing it, so the roster's own size is pinned too. -const SELF_TEST_BATTERY_FLOOR = 29; +const SELF_TEST_BATTERY_FLOOR = 30; // The key an assertion is filed under when no battery is open. It is not a // declared battery, so it reds by the same set difference rather than silently @@ -221,6 +223,43 @@ const UNATTRIBUTED_BATTERY = '(no battery open)'; const SCAN_ROOT = 'packages'; const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage', 'build', 'fixtures']); + +// ── The dispatch-gates declaration: the `ROOT_DIR_WATCH_HINTS` idiom (#22320) ── +// +// `scripts/pm/dispatch-gates.mjs` builds a card's gate list by reading PATH +// LITERALS out of each gate's source and matching them against the card's +// changed files. `SCAN_ROOT` is a bare single-segment word, and that extractor +// refuses a bare word BY DESIGN (its own docblock prices admitting one). So the +// only literals it read here were the four tracked files below, a roster of +// files that already exist. It filed this gate under its artifact-roster block +// for EVERY card. A dev's local sweep for a diff that added a status-emitting +// site under the scan root derived its command list without this gate, and CI +// then failed the PR here (#22311, a route file under the services tree). +// +// The array names what `walk` admits, in the subtree spelling the extractor +// accepts: every `.ts` or `.tsx` file at any depth under the scan root. +// Measured at 4e4111ca0: the walk admits 2999 files. The two hints cover 7847 +// of the 8526 tracked files under the root, and every walked file is among +// them (complete). The 4848 they name and the walk never opens are 4826 test +// files, 21 files under a skipped `fixtures` directory and one declaration +// file: the filters this idiom cannot spell. So the declaration errs WIDE, and +// that direction is chosen. A wasted run is one source scan with no build; a +// miss is a CI round trip. +// +// ⛔ No `.mts` hint: the walk never opens one, so it would name files this gate +// does not read. ⛔ Not the bare subtree of the root either: that adds every +// manifest, README and config under it, and nothing here opens those. +// ⛔ Never computed from `SCAN_ROOT`. The runtime value would be identical and +// every local assertion would stay green, but the extractor reads a computed +// spelling as NOTHING, so the gate would drop back out of every brief +// (`check:watch-hint-literal` holds the literal spelling). Self-test battery 29 +// holds the other side: the derivation reads these hints off this source, a +// real emit-site path derives the gate, a docs-only path does not, and every +// file the walk admits on the live tree is covered. +// `scripts/pm/bare-root-worklist.mjs` records the matching DECLARED-NARROWER +// row and holds its spelling SET-EQUAL to this array. +const ROOT_DIR_WATCH_HINTS = ['packages/**/*.ts', 'packages/**/*.tsx']; + const ERRORS_ZOD = 'packages/spec/src/api/errors.zod.ts'; const DOC_HANDLING = 'content/docs/protocol/kernel/error-handling.mdx'; const DOC_CATALOG = 'content/docs/api/error-catalog.mdx'; @@ -1181,7 +1220,7 @@ function runFixture({ files, handling, catalog, members }) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; -function selfTest() { +async function selfTest() { // The battery ledger this self-test's floor is evaluated against (#13489). // `battery()` opens a battery; every assertion below is attributed to the one // most recently opened, so a section that stops running stops registering and @@ -1928,7 +1967,74 @@ function selfTest() { rmSync(walkRoot, { recursive: true, force: true }); } - const CASES = 70; + // 29 — the dispatch-gates declaration (#22320). `ROOT_DIR_WATCH_HINTS` is read + // by ANOTHER tool, so nothing in this gate's own run can go red when it + // is wrong. A missing hint is a gate the derivation never names for a + // source edit under the scan root, which is the defect this battery was + // written for. A surplus hint names the gate for files it never opens. + // Both directions are asked here, of the live tree, through the + // derivation's OWN extractor and covering rule. The import sits inside + // this body, where the extractor's self-test mask blanks the specifier, + // so it adds nothing to the population this file declares (29b holds + // that). The fixture paths are strings, not files: `hintCovers` judges a + // path, so a later rename cannot empty a case. + battery('29 — the dispatch-gates declaration: a source edit under the scan root derives this gate,'); + const { extractWatchHints, hintCovers } = await import('./pm/dispatch-gates.mjs'); + const ownFile = fileURLToPath(import.meta.url); + const checkoutRoot = fileURLToPath(new URL('..', import.meta.url)); + const repoPath = (p) => relative(checkoutRoot, p).split('\\').join('/'); + const derivedHints = extractWatchHints(readFileSync(ownFile, 'utf8'), repoPath(ownFile)); + const derives = (path, hints = derivedHints) => hints.some((h) => hintCovers(h, path)); + check('29a the derivation reads every declared hint off this source', + ROOT_DIR_WATCH_HINTS.length > 0 && ROOT_DIR_WATCH_HINTS.every((h) => derivedHints.includes(h)), + `declared ${JSON.stringify(ROOT_DIR_WATCH_HINTS)}, extracted ${JSON.stringify(derivedHints)}. A declaration ` + + 'the extractor cannot read (a computed spelling, a leading glob) names this gate for nothing'); + check('29b the self-test\'s own import of the derivation adds no hint: the self-test mask blanks it', + !derivedHints.some((h) => h.includes('dispatch-gates')), + `extracted ${JSON.stringify(derivedHints)}`); + // The positive case the card asked for, spelled as the path of the real diff + // that CI failed on this gate while the local derivation did not name it. + const EMIT_SITE_EDIT = 'packages/services/service-storage/src/storage-routes.ts'; + check('29c an edit to the emit site a real red diff touched derives this gate', + derives(EMIT_SITE_EDIT), `not derived: ${EMIT_SITE_EDIT}`); + const walkedAbs = []; + walk(join(checkoutRoot, SCAN_ROOT), walkedAbs); + const walked = walkedAbs.map(repoPath); + check('29d …and that path is a file the walk really opens on the live tree, so 29c is about the population', + walked.includes(EMIT_SITE_EDIT), `${walked.length} file(s) walked, ${EMIT_SITE_EDIT} not among them`); + // COMPLETENESS against the walk itself, not against a copy of its filter. + const undeclared = walked.filter((p) => !ROOT_DIR_WATCH_HINTS.some((h) => hintCovers(h, p))); + check('29e every file the walk admits is named by a declared hint: an undeclared one is a file this gate ' + + 'opens and no dispatch brief can see', + walked.length > 0 && undeclared.length === 0, + `${walked.length} walked, ${undeclared.length} undeclared${undeclared.length ? `: ${undeclared.slice(0, 3).join(', ')}` : ''}`); + const deadHints = ROOT_DIR_WATCH_HINTS.filter((h) => !walked.some((p) => hintCovers(h, p))); + check('29f no declared hint is DEAD: each one names at least one file the walk opens', + deadHints.length === 0, `dead: ${JSON.stringify(deadHints)}`); + // The card's control: a docs-only edit to a page this gate never reads. + const DOCS_ONLY_EDIT = 'content/docs/ai/agents.mdx'; + check('29g CONTROL: a docs-only edit to a page this gate never reads does not derive it', + !derives(DOCS_ONLY_EDIT), + `derived through ${derivedHints.filter((h) => hintCovers(h, DOCS_ONLY_EDIT)).join(', ')}`); + check('29h …while an edit to either page this gate DOES read still derives it: the roster stays beside the ' + + 'declaration', + derives(DOC_CATALOG) && derives(DOC_HANDLING)); + // PRECISION: what the declaration must NOT name under its own root. + const NON_SOURCE_EDITS = [ + 'packages/services/service-storage/README.md', + 'packages/services/service-storage/package.json', + ]; + check('29i PRECISION: a non-source file under the scan root does not derive this gate', + NON_SOURCE_EDITS.every((p) => !derives(p)), + `derived: ${NON_SOURCE_EDITS.filter((p) => derives(p)).join(', ')}`); + check('NEGATIVE CONTROL 29j: the bare subtree spelling of the root WOULD name them, so 29i can fail', + NON_SOURCE_EDITS.every((p) => derives(p, extractWatchHints(`const H = ['${SCAN_ROOT}/**'];`)))); + check('NEGATIVE CONTROL 29k: the extractor still refuses the bare root and reads a computed spelling as ' + + 'nothing, so the refusal stays as designed and 29a can fail', + extractWatchHints(`const SCAN_ROOT = '${SCAN_ROOT}';`).length === 0 + && extractWatchHints('const H = [`${SCAN_ROOT}/**/*.ts`];').length === 0); + + const CASES = 81; // ── The floor: every declared battery RAN, and ran its cases (#13489) ─── // // Evaluated after every battery has had its chance and BEFORE the verdict, so @@ -1993,7 +2099,9 @@ function selfTest() { + 'DOOR TRANSLATES away is no wire producer — reported, kept out of the unpinned census, with the arm-less ' + 'negative control still producing and an untranslated code untouched, and the corpus walk skips an entry that ' + 'VANISHED between readdir and stat (every present file still scanned) while a dangling symlink, ENOTDIR, a ' - + 'permission error and a missing root still throw.', + + 'permission error and a missing root still throw, and the dispatch-gates declaration is read off this ' + + 'source, derives this gate for a real emit-site path and for every file the walk opens, and does not ' + + 'derive it for a docs-only path or a non-source file under the scan root.', ); selfTestReachedVerdict = true; process.exit(0); @@ -2311,7 +2419,7 @@ function main() { // this gate's full report into the importer's stdout before returning a binding. if (isEntrypoint(import.meta.url)) { if (process.argv.includes('--self-test')) { - selfTest(); + await selfTest(); if (!selfTestReachedVerdict) { console.error( '\n✗ check-error-status-conformance self-test: selfTest() returned without reaching its verdict,\n' From 38eaa648cfe7be39a29359b51f6087b9e0ea1f29 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 16:43:49 +0000 Subject: [PATCH 2/5] wip(pm): bare-root worklist re-decides the error-status-conformance row to DECLARED-NARROWER Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q Co-authored-by: Claude --- scripts/pm/bare-root-worklist.mjs | 62 +++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/scripts/pm/bare-root-worklist.mjs b/scripts/pm/bare-root-worklist.mjs index 23eabf6a460..b9f4b2cf0b3 100644 --- a/scripts/pm/bare-root-worklist.mjs +++ b/scripts/pm/bare-root-worklist.mjs @@ -403,6 +403,20 @@ const POPULATION_PROPERTY = /^(?:[a-z][A-Za-z0-9]*Roots?|[a-z][A-Za-z0-9]*Dirs?| * reads the import graph `os lint` bundles from each config, so the recorded set * is now that graph's two shapes, measured with the bundler itself on one tree. * + * ⭐ A TWENTY-SECOND row was re-decided on 2026-10-08 (#22320), and it is the + * first to leave REFUSE-WIDE: `check:error-status-conformance SCAN_ROOT + * packages`. That verdict says a declaration would be TRUE and is refused for + * width, so nothing in its definition moved. What arrived is the consumer the + * 2026-08-26 split treats as the deciding fact: a real diff the derivation was + * silent for and CI failed (PR #22311). The card's triage directed the + * declaration and kept the extractor's bare-word refusal as designed. ⚠️ Two + * readings made the refusal weaker than its siblings: the population is an + * EXTENSION filter inside the root, never the root itself, and the + * check:logger-receiver-detach row already declares that shape at this root. + * So the row is re-measured whole and takes the escape. ⛔ This is no licence + * for the other REFUSE-WIDE rows at this root: each still lacks a measured + * consumer, so each keeps its verdict until one arrives. + * * ⭐ ATTRIBUTION was added on 2026-09-19 (#15926), and it is a widening of a * PIN rather than a re-decision on any row: no verdict is added, changed or * withdrawn, and the 46/13/14 census is the one it was measured at. The @@ -563,6 +577,20 @@ const SPELLINGS = new Map([ holds: (s) => s[0] === 'packages' && s.length >= 2 && ['.ts', '.tsx', '.mts'].some((ext) => s[s.length - 1].endsWith(ext)), }], + ['packages TS and TSX source', { + // The entry above WITHOUT its third extension, and kept apart from it on + // purpose: the gate that declares this set walks only the two extensions + // here (#22320), so a `.mts` hint would name files it never opens. One + // entry per declared array, never the nearest wider one, which the + // declaration pin below would refuse in both directions anyway. + segments: [ + ['packages', '**', '*.ts'], + ['packages', '**', '*.tsx'], + ], + claim: 'every `.ts` or `.tsx` file at any depth under the packages root', + holds: (s) => s[0] === 'packages' && s.length >= 2 + && ['.ts', '.tsx'].some((ext) => s[s.length - 1].endsWith(ext)), + }], ['example app configs and their source trees', { // What `os lint` loads when check:i18n-coverage hands it a config: the // config, and the relative import graph its bundler follows from it, which @@ -906,6 +934,36 @@ const TRIAGE = new Map([ + 'still not covered — no arbitrary file at the top of examples/ is reached — which is what ' + 'this verdict says and is correct, not outstanding debt', }], + ['scripts/check-error-status-conformance.mjs SCAN_ROOT packages', { + verdict: 'DECLARED-NARROWER', + spelling: 'packages TS and TSX source', + why: 'RE-DECIDED 2026-10-08 (#22320) from REFUSE-WIDE, whose recorded reason was the precision ' + + 'trade alone: a true declaration names this gate for every card that touches a package. ' + + '#22320 is the measured consumer that trade had not priced. PR #22311 added an emit site ' + + 'under the services tree, the derivation for its diff filed this family in the ' + + 'artifact-roster block and printed a command list without it, and CI then failed that PR ' + + 'here. The triage of #22320 directed a declaration in the gate rather than a weaker ' + + 'extractor, so this row takes the escape and claims no wider authorisation. ⛔ The ' + + 'population was never the root itself: the walk admits every .ts or .tsx file that is not ' + + 'a test, spec or declaration file and does not sit in a skipped directory. Since #12300 the ' + + 'extension half of that filter is spellable, and the check:logger-receiver-detach row ' + + 'above already declares that shape at this root. RE-MEASURED at commit 4e4111ca0, every ' + + 'term on one tree: the walk opens 2999 of the 8526 tracked files under the bare root ' + + '(35.2%). The two hints the gate declares beside SCAN_ROOT under the ROOT_DIR_WATCH_HINTS ' + + 'idiom cover 7847 of the 8526 (92.0%), and every walked file is among them, so the ' + + 'declaration is complete. ⚠️ The DIRECTION it errs is stated rather than left to the ratio: ' + + 'it errs WIDE, naming 4848 files the walk never opens (4826 test or spec files, 21 under a ' + + 'skipped fixtures directory, one declaration file), so the walk is 38.2% of what it names. ' + + 'That remainder is the filter no subtree glob can spell, and it is accepted on purpose: the ' + + 'gate is a source scan with no build, so a wasted run costs one cheap command, while the ' + + 'miss cost a CI round trip. ⛔ The previous reading, 1898 of 4903, is superseded WHOLE and ' + + 'neither term is carried forward. The recorded set is SET-EQUAL to the gate array at this ' + + 'root, and the gate own --self-test holds the other side: the derivation reads both hints ' + + 'off its source, the emit site of the red diff derives the gate, a docs-only path does not, ' + + 'and every walked file is covered. The row STAYS in the sweep because the bare root is ' + + 'still not covered — no manifest, README or other non-source file under packages/ is ' + + 'reached — which is what this verdict says and is correct, not outstanding debt', + }], // ── Refused: the population is the whole root, and the root is saturated ── ['scripts/check-skill-identifier-liveness.mjs IMPL_ROOTS packages', { verdict: 'REFUSE-WIDE', @@ -989,10 +1047,6 @@ const TRIAGE = new Map([ verdict: 'REFUSE-WIDE', why: 'tests included, 4408 of 4903 (90%) — same trade as engine-double-contract', }], - ['scripts/check-error-status-conformance.mjs SCAN_ROOT packages', { - verdict: 'REFUSE-WIDE', - why: 'walks every non-test TS source under the root — 1898 of 4903 (39%)', - }], ['scripts/check-optional-error-sink-contract.mjs SCAN_ROOTS packages', { verdict: 'REFUSE-WIDE', why: '1898 of 4903 (39%). Its own failure text already prints the subtree spelling, which is ' From 9903181a2cf969a889359b7ee5f2e67bec3564ce Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:10:00 +0000 Subject: [PATCH 3/5] fix(scripts): self-test case 29d asks the services tree, not one file, so a rename elsewhere cannot red this gate Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q Co-authored-by: Claude --- scripts/check-error-status-conformance.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/check-error-status-conformance.mjs b/scripts/check-error-status-conformance.mjs index cb7834b282a..824d031123b 100644 --- a/scripts/check-error-status-conformance.mjs +++ b/scripts/check-error-status-conformance.mjs @@ -2000,8 +2000,14 @@ async function selfTest() { const walkedAbs = []; walk(join(checkoutRoot, SCAN_ROOT), walkedAbs); const walked = walkedAbs.map(repoPath); - check('29d …and that path is a file the walk really opens on the live tree, so 29c is about the population', - walked.includes(EMIT_SITE_EDIT), `${walked.length} file(s) walked, ${EMIT_SITE_EDIT} not among them`); + // Asked of the tree that path sits in, not of the one file: a later rename of + // that file must not red this gate, and 29e below already holds every walked + // file to the declaration. + const servicesTree = EMIT_SITE_EDIT.split('/').slice(0, 2).join('/'); + check('29d …and the walk really opens source files in that tree on the live checkout, so 29c is about the ' + + 'population and not about a path this gate never reads', + walked.some((p) => p.startsWith(`${servicesTree}/`)), + `${walked.length} file(s) walked, none under ${servicesTree}`); // COMPLETENESS against the walk itself, not against a copy of its filter. const undeclared = walked.filter((p) => !ROOT_DIR_WATCH_HINTS.some((h) => hintCovers(h, p))); check('29e every file the walk admits is named by a declared hint: an undeclared one is a file this gate ' From 11e2a5299b0bc9f32e7e67da16199fd200395967 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:36:46 +0000 Subject: [PATCH 4/5] revert(scripts): return check:error-status-conformance and the bare-root worklist to their main bytes The ROOT_DIR_WATCH_HINTS declaration, its self-test battery 29 and the REFUSE-WIDE to DECLARED-NARROWER re-decision are withdrawn: the seat verdict on the card routes the family through the content trigger in dispatch-gates instead, so the REFUSE-WIDE row stands as recorded. Reverts 21b440337a, 38eaa648cf and 9903181a2c. Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q Co-authored-by: Claude --- scripts/check-error-status-conformance.mjs | 124 +-------------------- scripts/pm/bare-root-worklist.mjs | 62 +---------- 2 files changed, 9 insertions(+), 177 deletions(-) diff --git a/scripts/check-error-status-conformance.mjs b/scripts/check-error-status-conformance.mjs index 824d031123b..309db59a18a 100644 --- a/scripts/check-error-status-conformance.mjs +++ b/scripts/check-error-status-conformance.mjs @@ -160,7 +160,6 @@ import { import { tmpdir } from 'node:os'; import { maskComments, maskCommentsAndLiterals } from './js-comment-mask.mjs'; import { basename, dirname, join, relative } from 'node:path'; -import { fileURLToPath } from 'node:url'; import { isEntrypoint } from './invoked-as.mjs'; // ── The self-test's own battery roster and floor (#13489) ────────────────── @@ -209,12 +208,11 @@ const SELF_TEST_BATTERIES = Object.freeze({ '26 — a code the DOOR TRANSLATES away is not a wire producer: derived from': 8, '27 — the vocabulary parse reads the enum ARRAY and stops at its bracket: an': 2, '28 — the corpus walk skips an entry that VANISHED between readdir and stat,': 10, - '29 — the dispatch-gates declaration: a source edit under the scan root derives this gate,': 11, }); // DELETING an entry silences that battery's floor exactly as effectively as // zeroing it, so the roster's own size is pinned too. -const SELF_TEST_BATTERY_FLOOR = 30; +const SELF_TEST_BATTERY_FLOOR = 29; // The key an assertion is filed under when no battery is open. It is not a // declared battery, so it reds by the same set difference rather than silently @@ -223,43 +221,6 @@ const UNATTRIBUTED_BATTERY = '(no battery open)'; const SCAN_ROOT = 'packages'; const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.turbo', 'coverage', 'build', 'fixtures']); - -// ── The dispatch-gates declaration: the `ROOT_DIR_WATCH_HINTS` idiom (#22320) ── -// -// `scripts/pm/dispatch-gates.mjs` builds a card's gate list by reading PATH -// LITERALS out of each gate's source and matching them against the card's -// changed files. `SCAN_ROOT` is a bare single-segment word, and that extractor -// refuses a bare word BY DESIGN (its own docblock prices admitting one). So the -// only literals it read here were the four tracked files below, a roster of -// files that already exist. It filed this gate under its artifact-roster block -// for EVERY card. A dev's local sweep for a diff that added a status-emitting -// site under the scan root derived its command list without this gate, and CI -// then failed the PR here (#22311, a route file under the services tree). -// -// The array names what `walk` admits, in the subtree spelling the extractor -// accepts: every `.ts` or `.tsx` file at any depth under the scan root. -// Measured at 4e4111ca0: the walk admits 2999 files. The two hints cover 7847 -// of the 8526 tracked files under the root, and every walked file is among -// them (complete). The 4848 they name and the walk never opens are 4826 test -// files, 21 files under a skipped `fixtures` directory and one declaration -// file: the filters this idiom cannot spell. So the declaration errs WIDE, and -// that direction is chosen. A wasted run is one source scan with no build; a -// miss is a CI round trip. -// -// ⛔ No `.mts` hint: the walk never opens one, so it would name files this gate -// does not read. ⛔ Not the bare subtree of the root either: that adds every -// manifest, README and config under it, and nothing here opens those. -// ⛔ Never computed from `SCAN_ROOT`. The runtime value would be identical and -// every local assertion would stay green, but the extractor reads a computed -// spelling as NOTHING, so the gate would drop back out of every brief -// (`check:watch-hint-literal` holds the literal spelling). Self-test battery 29 -// holds the other side: the derivation reads these hints off this source, a -// real emit-site path derives the gate, a docs-only path does not, and every -// file the walk admits on the live tree is covered. -// `scripts/pm/bare-root-worklist.mjs` records the matching DECLARED-NARROWER -// row and holds its spelling SET-EQUAL to this array. -const ROOT_DIR_WATCH_HINTS = ['packages/**/*.ts', 'packages/**/*.tsx']; - const ERRORS_ZOD = 'packages/spec/src/api/errors.zod.ts'; const DOC_HANDLING = 'content/docs/protocol/kernel/error-handling.mdx'; const DOC_CATALOG = 'content/docs/api/error-catalog.mdx'; @@ -1220,7 +1181,7 @@ function runFixture({ files, handling, catalog, members }) { // handshake is a flag rather than a returned sentinel. let selfTestReachedVerdict = false; -async function selfTest() { +function selfTest() { // The battery ledger this self-test's floor is evaluated against (#13489). // `battery()` opens a battery; every assertion below is attributed to the one // most recently opened, so a section that stops running stops registering and @@ -1967,80 +1928,7 @@ async function selfTest() { rmSync(walkRoot, { recursive: true, force: true }); } - // 29 — the dispatch-gates declaration (#22320). `ROOT_DIR_WATCH_HINTS` is read - // by ANOTHER tool, so nothing in this gate's own run can go red when it - // is wrong. A missing hint is a gate the derivation never names for a - // source edit under the scan root, which is the defect this battery was - // written for. A surplus hint names the gate for files it never opens. - // Both directions are asked here, of the live tree, through the - // derivation's OWN extractor and covering rule. The import sits inside - // this body, where the extractor's self-test mask blanks the specifier, - // so it adds nothing to the population this file declares (29b holds - // that). The fixture paths are strings, not files: `hintCovers` judges a - // path, so a later rename cannot empty a case. - battery('29 — the dispatch-gates declaration: a source edit under the scan root derives this gate,'); - const { extractWatchHints, hintCovers } = await import('./pm/dispatch-gates.mjs'); - const ownFile = fileURLToPath(import.meta.url); - const checkoutRoot = fileURLToPath(new URL('..', import.meta.url)); - const repoPath = (p) => relative(checkoutRoot, p).split('\\').join('/'); - const derivedHints = extractWatchHints(readFileSync(ownFile, 'utf8'), repoPath(ownFile)); - const derives = (path, hints = derivedHints) => hints.some((h) => hintCovers(h, path)); - check('29a the derivation reads every declared hint off this source', - ROOT_DIR_WATCH_HINTS.length > 0 && ROOT_DIR_WATCH_HINTS.every((h) => derivedHints.includes(h)), - `declared ${JSON.stringify(ROOT_DIR_WATCH_HINTS)}, extracted ${JSON.stringify(derivedHints)}. A declaration ` - + 'the extractor cannot read (a computed spelling, a leading glob) names this gate for nothing'); - check('29b the self-test\'s own import of the derivation adds no hint: the self-test mask blanks it', - !derivedHints.some((h) => h.includes('dispatch-gates')), - `extracted ${JSON.stringify(derivedHints)}`); - // The positive case the card asked for, spelled as the path of the real diff - // that CI failed on this gate while the local derivation did not name it. - const EMIT_SITE_EDIT = 'packages/services/service-storage/src/storage-routes.ts'; - check('29c an edit to the emit site a real red diff touched derives this gate', - derives(EMIT_SITE_EDIT), `not derived: ${EMIT_SITE_EDIT}`); - const walkedAbs = []; - walk(join(checkoutRoot, SCAN_ROOT), walkedAbs); - const walked = walkedAbs.map(repoPath); - // Asked of the tree that path sits in, not of the one file: a later rename of - // that file must not red this gate, and 29e below already holds every walked - // file to the declaration. - const servicesTree = EMIT_SITE_EDIT.split('/').slice(0, 2).join('/'); - check('29d …and the walk really opens source files in that tree on the live checkout, so 29c is about the ' - + 'population and not about a path this gate never reads', - walked.some((p) => p.startsWith(`${servicesTree}/`)), - `${walked.length} file(s) walked, none under ${servicesTree}`); - // COMPLETENESS against the walk itself, not against a copy of its filter. - const undeclared = walked.filter((p) => !ROOT_DIR_WATCH_HINTS.some((h) => hintCovers(h, p))); - check('29e every file the walk admits is named by a declared hint: an undeclared one is a file this gate ' - + 'opens and no dispatch brief can see', - walked.length > 0 && undeclared.length === 0, - `${walked.length} walked, ${undeclared.length} undeclared${undeclared.length ? `: ${undeclared.slice(0, 3).join(', ')}` : ''}`); - const deadHints = ROOT_DIR_WATCH_HINTS.filter((h) => !walked.some((p) => hintCovers(h, p))); - check('29f no declared hint is DEAD: each one names at least one file the walk opens', - deadHints.length === 0, `dead: ${JSON.stringify(deadHints)}`); - // The card's control: a docs-only edit to a page this gate never reads. - const DOCS_ONLY_EDIT = 'content/docs/ai/agents.mdx'; - check('29g CONTROL: a docs-only edit to a page this gate never reads does not derive it', - !derives(DOCS_ONLY_EDIT), - `derived through ${derivedHints.filter((h) => hintCovers(h, DOCS_ONLY_EDIT)).join(', ')}`); - check('29h …while an edit to either page this gate DOES read still derives it: the roster stays beside the ' - + 'declaration', - derives(DOC_CATALOG) && derives(DOC_HANDLING)); - // PRECISION: what the declaration must NOT name under its own root. - const NON_SOURCE_EDITS = [ - 'packages/services/service-storage/README.md', - 'packages/services/service-storage/package.json', - ]; - check('29i PRECISION: a non-source file under the scan root does not derive this gate', - NON_SOURCE_EDITS.every((p) => !derives(p)), - `derived: ${NON_SOURCE_EDITS.filter((p) => derives(p)).join(', ')}`); - check('NEGATIVE CONTROL 29j: the bare subtree spelling of the root WOULD name them, so 29i can fail', - NON_SOURCE_EDITS.every((p) => derives(p, extractWatchHints(`const H = ['${SCAN_ROOT}/**'];`)))); - check('NEGATIVE CONTROL 29k: the extractor still refuses the bare root and reads a computed spelling as ' - + 'nothing, so the refusal stays as designed and 29a can fail', - extractWatchHints(`const SCAN_ROOT = '${SCAN_ROOT}';`).length === 0 - && extractWatchHints('const H = [`${SCAN_ROOT}/**/*.ts`];').length === 0); - - const CASES = 81; + const CASES = 70; // ── The floor: every declared battery RAN, and ran its cases (#13489) ─── // // Evaluated after every battery has had its chance and BEFORE the verdict, so @@ -2105,9 +1993,7 @@ async function selfTest() { + 'DOOR TRANSLATES away is no wire producer — reported, kept out of the unpinned census, with the arm-less ' + 'negative control still producing and an untranslated code untouched, and the corpus walk skips an entry that ' + 'VANISHED between readdir and stat (every present file still scanned) while a dangling symlink, ENOTDIR, a ' - + 'permission error and a missing root still throw, and the dispatch-gates declaration is read off this ' - + 'source, derives this gate for a real emit-site path and for every file the walk opens, and does not ' - + 'derive it for a docs-only path or a non-source file under the scan root.', + + 'permission error and a missing root still throw.', ); selfTestReachedVerdict = true; process.exit(0); @@ -2425,7 +2311,7 @@ function main() { // this gate's full report into the importer's stdout before returning a binding. if (isEntrypoint(import.meta.url)) { if (process.argv.includes('--self-test')) { - await selfTest(); + selfTest(); if (!selfTestReachedVerdict) { console.error( '\n✗ check-error-status-conformance self-test: selfTest() returned without reaching its verdict,\n' diff --git a/scripts/pm/bare-root-worklist.mjs b/scripts/pm/bare-root-worklist.mjs index b9f4b2cf0b3..23eabf6a460 100644 --- a/scripts/pm/bare-root-worklist.mjs +++ b/scripts/pm/bare-root-worklist.mjs @@ -403,20 +403,6 @@ const POPULATION_PROPERTY = /^(?:[a-z][A-Za-z0-9]*Roots?|[a-z][A-Za-z0-9]*Dirs?| * reads the import graph `os lint` bundles from each config, so the recorded set * is now that graph's two shapes, measured with the bundler itself on one tree. * - * ⭐ A TWENTY-SECOND row was re-decided on 2026-10-08 (#22320), and it is the - * first to leave REFUSE-WIDE: `check:error-status-conformance SCAN_ROOT - * packages`. That verdict says a declaration would be TRUE and is refused for - * width, so nothing in its definition moved. What arrived is the consumer the - * 2026-08-26 split treats as the deciding fact: a real diff the derivation was - * silent for and CI failed (PR #22311). The card's triage directed the - * declaration and kept the extractor's bare-word refusal as designed. ⚠️ Two - * readings made the refusal weaker than its siblings: the population is an - * EXTENSION filter inside the root, never the root itself, and the - * check:logger-receiver-detach row already declares that shape at this root. - * So the row is re-measured whole and takes the escape. ⛔ This is no licence - * for the other REFUSE-WIDE rows at this root: each still lacks a measured - * consumer, so each keeps its verdict until one arrives. - * * ⭐ ATTRIBUTION was added on 2026-09-19 (#15926), and it is a widening of a * PIN rather than a re-decision on any row: no verdict is added, changed or * withdrawn, and the 46/13/14 census is the one it was measured at. The @@ -577,20 +563,6 @@ const SPELLINGS = new Map([ holds: (s) => s[0] === 'packages' && s.length >= 2 && ['.ts', '.tsx', '.mts'].some((ext) => s[s.length - 1].endsWith(ext)), }], - ['packages TS and TSX source', { - // The entry above WITHOUT its third extension, and kept apart from it on - // purpose: the gate that declares this set walks only the two extensions - // here (#22320), so a `.mts` hint would name files it never opens. One - // entry per declared array, never the nearest wider one, which the - // declaration pin below would refuse in both directions anyway. - segments: [ - ['packages', '**', '*.ts'], - ['packages', '**', '*.tsx'], - ], - claim: 'every `.ts` or `.tsx` file at any depth under the packages root', - holds: (s) => s[0] === 'packages' && s.length >= 2 - && ['.ts', '.tsx'].some((ext) => s[s.length - 1].endsWith(ext)), - }], ['example app configs and their source trees', { // What `os lint` loads when check:i18n-coverage hands it a config: the // config, and the relative import graph its bundler follows from it, which @@ -934,36 +906,6 @@ const TRIAGE = new Map([ + 'still not covered — no arbitrary file at the top of examples/ is reached — which is what ' + 'this verdict says and is correct, not outstanding debt', }], - ['scripts/check-error-status-conformance.mjs SCAN_ROOT packages', { - verdict: 'DECLARED-NARROWER', - spelling: 'packages TS and TSX source', - why: 'RE-DECIDED 2026-10-08 (#22320) from REFUSE-WIDE, whose recorded reason was the precision ' - + 'trade alone: a true declaration names this gate for every card that touches a package. ' - + '#22320 is the measured consumer that trade had not priced. PR #22311 added an emit site ' - + 'under the services tree, the derivation for its diff filed this family in the ' - + 'artifact-roster block and printed a command list without it, and CI then failed that PR ' - + 'here. The triage of #22320 directed a declaration in the gate rather than a weaker ' - + 'extractor, so this row takes the escape and claims no wider authorisation. ⛔ The ' - + 'population was never the root itself: the walk admits every .ts or .tsx file that is not ' - + 'a test, spec or declaration file and does not sit in a skipped directory. Since #12300 the ' - + 'extension half of that filter is spellable, and the check:logger-receiver-detach row ' - + 'above already declares that shape at this root. RE-MEASURED at commit 4e4111ca0, every ' - + 'term on one tree: the walk opens 2999 of the 8526 tracked files under the bare root ' - + '(35.2%). The two hints the gate declares beside SCAN_ROOT under the ROOT_DIR_WATCH_HINTS ' - + 'idiom cover 7847 of the 8526 (92.0%), and every walked file is among them, so the ' - + 'declaration is complete. ⚠️ The DIRECTION it errs is stated rather than left to the ratio: ' - + 'it errs WIDE, naming 4848 files the walk never opens (4826 test or spec files, 21 under a ' - + 'skipped fixtures directory, one declaration file), so the walk is 38.2% of what it names. ' - + 'That remainder is the filter no subtree glob can spell, and it is accepted on purpose: the ' - + 'gate is a source scan with no build, so a wasted run costs one cheap command, while the ' - + 'miss cost a CI round trip. ⛔ The previous reading, 1898 of 4903, is superseded WHOLE and ' - + 'neither term is carried forward. The recorded set is SET-EQUAL to the gate array at this ' - + 'root, and the gate own --self-test holds the other side: the derivation reads both hints ' - + 'off its source, the emit site of the red diff derives the gate, a docs-only path does not, ' - + 'and every walked file is covered. The row STAYS in the sweep because the bare root is ' - + 'still not covered — no manifest, README or other non-source file under packages/ is ' - + 'reached — which is what this verdict says and is correct, not outstanding debt', - }], // ── Refused: the population is the whole root, and the root is saturated ── ['scripts/check-skill-identifier-liveness.mjs IMPL_ROOTS packages', { verdict: 'REFUSE-WIDE', @@ -1047,6 +989,10 @@ const TRIAGE = new Map([ verdict: 'REFUSE-WIDE', why: 'tests included, 4408 of 4903 (90%) — same trade as engine-double-contract', }], + ['scripts/check-error-status-conformance.mjs SCAN_ROOT packages', { + verdict: 'REFUSE-WIDE', + why: 'walks every non-test TS source under the root — 1898 of 4903 (39%)', + }], ['scripts/check-optional-error-sink-contract.mjs SCAN_ROOTS packages', { verdict: 'REFUSE-WIDE', why: '1898 of 4903 (39%). Its own failure text already prints the subtree spelling, which is ' From 28bf1d4448f72ca7dd770f45c913aa4161dceffb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 18:05:16 +0000 Subject: [PATCH 5/5] fix(pm): dispatch-gates names check:error-status-conformance for a file that binds an HTTP status, judged from content A sibling CHANGE_KIND_GATES entry with its own predicate, emitsAnHttpStatus: a status or statusCode bound to a 4xx/5xx value, the positional sendError door, or a SCREAMING_SNAKE status constant. Measured at the bytes of main 28bff18d0c, it reaches all 128 files that carry a derived producer, while the ADR-0112 code predicate misses 19 of them (86 sites), so the gate does not simply join the vocabulary entry. The gate's REFUSE-WIDE bare-root row stands as recorded. Self-test cases pin the PR #22311 emit site, the docs-only, README and no-emission controls, and the rendered line. Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q Co-authored-by: Claude --- scripts/pm/dispatch-gates.mjs | 193 ++++++++++++++++++++++++++++++++-- 1 file changed, 186 insertions(+), 7 deletions(-) diff --git a/scripts/pm/dispatch-gates.mjs b/scripts/pm/dispatch-gates.mjs index 023fe8ba324..5447f770ee5 100644 --- a/scripts/pm/dispatch-gates.mjs +++ b/scripts/pm/dispatch-gates.mjs @@ -10823,9 +10823,10 @@ export function isTestFilePath(path) { * * ## Why this one is judged from CONTENT, when every other kind reads a PATH * - * The four predicates around this one answer questions about a path: is it a + * The four path predicates around this one answer questions about a path: is it a * test file, does its package own an extract config, is it a gate script, is it - * in the root program. This one cannot, and the reason is recorded elsewhere in + * in the root program (`emitsAnHttpStatus` below is the one other CONTENT + * predicate, for the same reason). This one cannot, and the reason is recorded elsewhere in * the tree rather than argued here. The vocabulary gate computes its own * population by walking a bare top-level root, and `scripts/pm/bare-root- * worklist.mjs` already carries the verdict for that spelling: REFUSE-WIDE, @@ -10927,6 +10928,93 @@ export function stampsAnErrorCodeLiteral(path, readSource = readTrackedSource) { return CODE_STAMP_POSITION.test(masked) || CODE_CONSTANT_BINDING.test(masked); } +/** + * Does this file bind an HTTP STATUS to an error response? This is the content + * trigger for `check:error-status-conformance` (#22320). + * + * ## Why a SIBLING of the code predicate above, not a second gate on its entry + * + * That gate reconciles the (code, HTTP status) pairs the runtime can emit with + * the statuses the two error pages publish. It walks the same bare top-level + * root the vocabulary gate walks, and the bare-root ledger records it + * REFUSE-WIDE for the same trade. So no path prefix names it either, and it + * reaches a card here, by content, the way its sibling does. ⛔ That ledger row + * is not re-decided to make a path match possible: REFUSE-WIDE refuses a TRUE + * declaration on purpose, and re-deciding it would take a ruling. + * + * It does not simply join the entry above, because the two questions DIVERGE + * on this tree. Measured at 11e2a5299b (the bytes of main 28bff18d0c) with the + * gate's own deriver, one file at a time: 128 files carry a derived (code, + * status) producer, and 19 of them, holding 86 producer sites, carry no + * code-shaped value `stampsAnErrorCodeLiteral` can see. Two shapes account for + * all 19. The four-argument `sendError` door takes its code as a POSITIONAL + * argument, with no `code` token near it. The assignment pair reads its code + * from an enum member (`StandardErrorCode.enum.X`), not a quoted literal or a + * bare SCREAMING_SNAKE name. A dev editing one of those files is exactly where + * PR #22311 stood: the derived union read green, and CI failed this gate. + * + * ## What it matches + * + * Every rule the gate derives a producer with needs either a status VALUE in a + * status position or the door call. So the limbs read those, never the code: + * + * - STATUS POSITION: the token `status` or `statusCode`, bound through `:` or + * `=` (never `==`, `=>` or `::`) to a 4xx/5xx literal, or to a + * SCREAMING_SNAKE name, bare or ending a member chain. That covers a class's + * own `readonly status`, the `{ code, status }` and `{ status, body }` + * terminals, and the `err.status =` assignment. + * - DOOR CALL: `sendError(` whose second argument is a 4xx/5xx literal or + * such a name. This is the positional door, the first shape the code + * predicate misses. + * - STATUS CONSTANT BINDING: a SCREAMING_SNAKE binding to a 4xx/5xx literal, + * which is how this repo declares a refusal's status before any emit site + * uses it (`const NOT_UPLOADER_STATUS = 403`). The gate resolves such a + * name across files, so the declaring file moves the gate even though it + * emits nothing itself. + * + * ⛔ This is not a copy of the gate's derivation rules, for the reason the code + * predicate gives about `SHAPES`: a net broader than the rules already holds a + * rule added there, so staleness can only fall in the cheap direction. + * + * ## Measured, so a narrowing has a number to beat + * + * On the same tree, 185 of the 3247 non-test TypeScript files match. Every one + * of them is inside the gate's own scanned population, and 128 of them carry a + * producer (69%). All 128 producer files match, so recall over what can fail + * the gate is complete. The code predicate names 283 files for its own gate. + * There are two residues, both stated here rather than left to be found: + * + * - 9 files hold only UNRESOLVED pairs, where the status is a runtime value, + * and 6 of them do not match. The gate reports those and never fails on + * them, so as written they cannot fail it. The edit that would make one + * resolvable writes a status value, which this predicate then sees. + * - 4 files resolve a producer elsewhere through a constant they declare. + * The binding limb reaches 3 of them. The fourth declares only a CODE + * constant, and the entry above reaches it, this one does not. A + * code-constant limb here would add 45 matches (185 to 230) for that one + * file, and this entry declines that precision trade. + * + * The false-positive trade is the code predicate's: one extra run of a gate + * that needs no build, against a CI round trip. The unreadable branch is the + * code predicate's too: a file with nothing on disk answers false. ⛔ Do not + * close it by falling back to a path. + */ +const STATUS_STAMP_POSITION = + /\b(?:status|statusCode)\s*\??\s*(?::(?!:)|=(?![=>]))\s*(?:[45]\d\d\b|(?:[A-Za-z_$][\w$]*\.)*[A-Z][A-Z0-9_]*\b)/; +const STATUS_DOOR_CALL = /\bsendError\(\s*[\w$.]+\s*,\s*(?:[45]\d\d\b|(?:[A-Za-z_$][\w$]*\.)*[A-Z][A-Z0-9_]*\b)/; +const STATUS_CONSTANT_BINDING = /\b(?:const|readonly|static|let)\s+[A-Z][A-Z0-9_]*\s*(?::[^=;\n]+)?=\s*[45]\d\d\b/; + +export function emitsAnHttpStatus(path, readSource = readTrackedSource) { + if (!/\.[cm]?tsx?$/.test(path) || /\.d\.[cm]?ts$/.test(path)) return false; + if (isTestFilePath(path)) return false; + const source = readSource(path); + if (source === null || source === undefined) return false; + // Masked for the code predicate's reason: a status DISCUSSED in prose is not + // a status bound in source. + const masked = maskedComments(source); + return STATUS_STAMP_POSITION.test(masked) || STATUS_DOOR_CALL.test(masked) || STATUS_CONSTANT_BINDING.test(masked); +} + /** * Is this path inside the ROOT package's tsc program — the population behind * the `@objectstack/spec-monorepo` entry of `check:type-check-debt`? @@ -11507,6 +11595,12 @@ export function reachesMetadataFormModule(path, modulePaths) { * about whether a dispatch brief can NAME it. ⛔ Nor does this predicate * going quiet on a given card: it reads content, so silence about a file * nobody can read yet is not evidence in either direction. + * - status entry: on the same criterion as the error-code entry. The + * conformance gate's source would have to declare the population it walks + * in a form this derivation can read, and that means its bare-root ledger + * row moving off REFUSE-WIDE, which takes a ruling. ⛔ Growing the gate's + * derivation rules does NOT qualify, and ⛔ neither does this predicate + * going quiet on a card, for the reason given above. * - root-program entry: when the gate's own source names its root population * in a form this derivation can read — a positive literal, or a generated * manifest of the resolved program — the ordinary path match names it and @@ -11621,6 +11715,16 @@ export const CHANGE_KIND_GATES = [ }, ], }, + { + kind: 'adds or edits a file that binds an HTTP STATUS to an error response (judged from CONTENT — no path derivation can name this gate)', + matches: emitsAnHttpStatus, + gates: [ + { + name: 'check:error-status-conformance', + why: 'it derives every (code, HTTP status) pair the non-test TypeScript sources under the package root can emit (an error class declaring both, the four-argument sendError door, a code-and-status or status-and-body terminal, an assignment pair on one error) and reconciles that set in BOTH directions with the statuses the error catalog and the error-handling page publish. So a status added, changed or removed at an emit site moves it, and so does a status constant another file resolves. This is a gate no path derivation can name: it walks a bare top-level root that the bare-root ledger records as REFUSE-WIDE, so it scores the same quiet silence for every card, and PR #22311 paid a CI round trip for that silence (#22320). It needs NO build, being a source scan in one pass over the whole tree, and it names the code, the status and the emit site. Repair by DOCUMENTING the status on that code entry (an exception line when the code already publishes another status), or by correcting the emit site when the status is the defect. ⛔ Never by admitting the code to the unpinned baseline, which is ⛔ MAINTAINER-ONLY under the #8435 convention. ⚠ This lead fires on any file that binds a status value, not only one that changes it, for the trade the vocabulary entry above states: the wasted run is one cheap gate and the miss is a CI round trip', + }, + ], + }, ]; /** @@ -20780,8 +20884,9 @@ function selfTest() { // ── The error-code CONTENT kind (#12850) ───────────────────────────────── // - // The only entry in this table judged from a file's CONTENT rather than its - // path, so its cases are shaped differently: the limbs are driven through an + // The first entry in this table judged from a file's CONTENT rather than its + // path (the HTTP-status entry below is the second), so its cases are shaped + // differently: the limbs are driven through an // INJECTED reader (offline, no tree), and the tree itself is used only for // the two properties a fixture cannot pin — that the predicate still reaches // the real specimen, and that it still DISCRIMINATES. @@ -20858,6 +20963,77 @@ function selfTest() { t('…and writes the false-positive trade down, so nobody assumes narrowing is free', /deliberately WIDE/.test(codeLine) && /CI round trip/.test(codeLine)); + // ── The HTTP-status CONTENT kind (#22320) ──────────────────────────────── + // + // The second content entry, pinned in the first one's shape: limbs through an + // injected reader, then the live tree for the specimen and the controls, then + // the rendered section. The specimen is the emit site PR #22311 lost a CI + // round on. Two of the limb cases are paired with the CODE predicate missing + // the same text, because that divergence is the whole reason this is a + // sibling entry and not a second gate on the one above. + const emits = (text, path = 'packages/x/src/a.ts') => emitsAnHttpStatus(path, codeSrc(text)); + t('a status literal in a status-and-body terminal is a hit', + emits("return { status: 409, body: { code: 'RESOURCE_CONFLICT' } };")); + t('an error class declaring its own status is a hit', + emits("class E extends Error { readonly code = 'X_Y'; readonly status = 422; }")); + const ENUM_PAIR = 'err.code = StandardErrorCode.enum.INVALID_FILTER;\nerr.status = 400;'; + t('an assignment pair whose code is an enum member is a hit', emits(ENUM_PAIR)); + t('…and the code predicate misses that text, so the status entry is not redundant', !stamps(ENUM_PAIR)); + const DOOR_CALL = "sendError(res, 403, 'SETTINGS_FORBIDDEN', err.message);"; + t('the positional four-argument sendError door is a hit', emits(DOOR_CALL)); + t('…and the code predicate misses that one too', !stamps(DOOR_CALL)); + t('a status named by a SCREAMING_SNAKE constant is a hit', + emits('return { code: NOT_UPLOADER_CODE, status: NOT_UPLOADER_STATUS };')); + t('a status constant declared for another file to resolve is a hit', emits('export const NOT_UPLOADER_STATUS = 403;')); + t('a quoted status WORD is not a hit', !emits("const row = { status: 'active' };")); + t('a 2xx status is not a hit: the gate reconciles 4xx and 5xx only', !emits('return { status: 200, body: {} };')); + t('a comparison is not a binding', !emits('if (res.status === 404) return null;')); + t('a status read from a runtime value is not a hit: the gate cannot resolve it either', !emits('err.status = status;')); + t('a status discussed only in a comment is not a hit', !emits('// status: 409 is answered elsewhere\nexport const x = 1;')); + t('a .ts file with no status and no code at all is not a hit', + !emits('export function add(a: number, b: number) { return a + b; }')); + t('a test file binding a status is not a hit', !emits('const e = { status: 409 };', 'packages/x/src/a.test.ts')); + t('a d.ts binding a status is not a hit', !emits('const e = { status: 409 };', 'packages/x/src/a.d.ts')); + t('a non-TS file binding a status is not a hit', !emits('status: 409', 'packages/x/README.md')); + t('a path with nothing to read is not a hit, and does not throw', !emitsAnHttpStatus('packages/x/src/a.ts', () => null)); + + // The live tree: the specimen is reached, and the card's controls are not. + // Spelled rather than discovered, like the code specimen above: a derived + // probe would answer about some other file. + const STATUS_SPECIMEN = 'packages/services/service-storage/src/storage-routes.ts'; + t('the live tree still carries the #22311 emit site, and the predicate reaches it', emitsAnHttpStatus(STATUS_SPECIMEN)); + t('a docs-only page is not reached', !emitsAnHttpStatus('content/docs/ai/agents.mdx')); + t('the package README beside the specimen is not reached', !emitsAnHttpStatus('packages/services/service-storage/README.md')); + t('a live .ts with no status and no code (the package barrel) is reached by neither content entry', + !emitsAnHttpStatus('packages/services/service-storage/src/index.ts') + && !stampsAnErrorCodeLiteral('packages/services/service-storage/src/index.ts')); + const statusHits = codeCorpus.filter((f) => emitsAnHttpStatus(f)); + t(`the status trigger discriminates: ${statusHits.length} of ${codeCorpus.length} non-test TS files (neither vacuous nor tree-wide)`, + statusHits.length > 20 && statusHits.length < codeCorpus.length / 4); + + // The rendered section, through this entry alone and then through the whole + // table, which is where the card's controls have to hold. + const statusEntry = CHANGE_KIND_GATES.filter((k) => k.gates.some((g) => g.name === 'check:error-status-conformance')); + t('exactly one entry in the table names the status gate', statusEntry.length === 1); + const statusKind = changeKindLines([STATUS_SPECIMEN], resolved, statusEntry); + t('the emit-site path emits the status convention section', + statusKind.length === 2 && statusKind[0].includes('HTTP STATUS') && statusKind[0].includes('judged from CONTENT')); + t('and it names the status gate runnably, anchored on the delimiter', + statusKind.some((l) => l.includes('- pnpm check:error-status-conformance —'))); + const statusLine = statusKind.find((l) => l.includes('- pnpm check:error-status-conformance —')) ?? ''; + t('the status line states why no path derivation reaches it', /REFUSE-WIDE/.test(statusLine)); + t('…and pushes the repair to documenting the status, with the baseline remedy kept maintainer-only', + /DOCUMENTING/.test(statusLine) && /MAINTAINER-ONLY/.test(statusLine)); + t('…and says it needs no build', /needs NO build/.test(statusLine)); + const throughTable = (p) => changeKindLines([p], resolved).some((l) => l.includes('check:error-status-conformance')); + t('through the whole table, the specimen derives the status gate', throughTable(STATUS_SPECIMEN)); + t('…a docs-only path does not', !throughTable('content/docs/ai/agents.mdx')); + t('…the package README does not', !throughTable('packages/services/service-storage/README.md')); + t('…and the package barrel does not', !throughTable('packages/services/service-storage/src/index.ts')); + const statusStale = changeKindLines([STATUS_SPECIMEN], () => null, statusEntry); + t('an undiscoverable status gate renders STALE for this entry', + statusStale.filter((l) => l.includes('⚠ check:error-status-conformance: STALE')).length === 1); + // ── The metadata-form edge (#9116) ──────────────────────────────────────── // // The bundles' OTHER producer, and the half no owning-package test can reach: @@ -21862,11 +22038,14 @@ function selfTest() { liveFamilies.has('check:cross-package-test-inputs'), ); // #12850's entry, pinned here for the same reason and with one of its own: - // this is the only gate in the table reached by a CONTENT predicate, so the - // census guard above is the only thing standing between a rename and a lead - // that renders STALE on a card nobody re-reads. + // its gate is reached only by a CONTENT predicate, so the census guard above + // is the only thing standing between a rename and a lead that renders STALE + // on a card nobody re-reads. t('check:dispatcher-error-vocabulary is a live family, so naming it is not a guess', liveFamilies.has('check:dispatcher-error-vocabulary')); + // #22320's entry, the second content-reached gate, pinned for the same reason. + t('check:error-status-conformance is a live family, so naming it is not a guess', + liveFamilies.has('check:error-status-conformance')); // ── The test-file entry's deletion criterion, MEASURED (#11199) ─────────── //