From 0a406118e44067bef48395ed825c317a8baa91fc Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 12:06:58 +0000 Subject: [PATCH 1/2] fix(check-type-check-coverage): withdraw the exclusion route from the TEST_DEBT graduation remedy The TEST_DEBT graduation remedy offered two routes. Route (b) -- drop the `**/*.test.ts` entry from `tsconfig.json`'s `exclude`, or widen `include` to reach the test tree -- had its availability decided, per package, by `check:type-source-resolution`. That gate was retired under the maintainer ruling of 2026-09-18 on #18373, and what it had measured did not change with it: RED for 14 of the 18 entries that had an exclusion to drop. An official route that is wrong 14 times out of 18, with nothing left to say so, is not a route. The maintainer ruling of 2026-09-18 on #18953 (decision batch #159 item 4, letter 2) withdraws it. The printed prescription now names the #5286 sibling route alone. Having this gate measure the precondition itself was rejected in the same ruling -- it rebuilds half of a gate the maintainer had just retired -- so nothing here measures anything new. The self-test case that pinned the old two-route message is rewritten rather than deleted, and turned from a presence-only assertion into ANTI-content: `exclude`, `widen`, `check:type-source-resolution` and `SHRINK-ONLY` must all be absent from the TEST_DEBT branch, because a presence-only assertion sits green through exactly the re-merge that hands the route back. The unrecognised-ledger case's TEST_DEBT needle moves from the retired gate's name (which no branch can emit any more) to `tsconfig.test.json`, which one can. Case count, battery roster and every floor are unchanged: 5 graduation cases and `'graduation remedy (#11491)': 8` as before, and the printed self-test tally is identical at 55/97/56/28/19/18. `packages/verify/tsconfig.test.json` restated the route by letter and said the gate "tells the next reader to assume it is not" available; that sentence is false once the gate says nothing about the route at all, so the record is rewritten as history. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude --- packages/verify/tsconfig.test.json | 24 ++++-- scripts/check-type-check-coverage.mjs | 105 ++++++++++++++++---------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/packages/verify/tsconfig.test.json b/packages/verify/tsconfig.test.json index 25bb5b40307..73c531db2bb 100644 --- a/packages/verify/tsconfig.test.json +++ b/packages/verify/tsconfig.test.json @@ -14,20 +14,28 @@ // line and not a probe that sees nothing. The same probe against THIS config, // on that same tree, names 10 of 10 (981 program files -> 990). // -// ⚠️ ROUTE (b) WAS AVAILABLE HERE AND WAS NOT TAKEN, which is worth recording -// because `check-type-check-coverage.mjs` tells the next reader to assume it is -// not: dropping `**/*.test.ts` from `tsconfig.json`'s `exclude` leaves -// `pnpm check:type-source-resolution` GREEN for this package (measured under a -// trap-restored mutation at 1d67130585 -- exit 0, 124 programs across 78 -// packages), unlike the 14 of 18 entries that gate reports as red. It was -// declined on module semantics, not on availability: vitest executes these +// ⚠️ THE OTHER EDIT -- dropping `**/*.test.ts` from `tsconfig.json`'s +// `exclude` -- WAS AVAILABLE HERE AND WAS NOT TAKEN, and the reason is worth +// recording because it is not the reason the gate's prescription now gives. It +// was declined on module semantics, not on availability: vitest executes these // files through vite, which resolves extension-less relative specifiers, while // `tsconfig.json` inherits `NodeNext` from the repo root and demands `.js` on -// every one of them. Route (b) would hold the test layer to a resolver that +// every one of them. That edit would hold the test layer to a resolver that // never runs it -- the same config-tier noise the shared gate's header // attributes 108 of spec's 842 raw errors to. Matching vitest is fidelity, not // laxity, and it is why 28 of the 30 sibling configs override these two keys. // +// ⛔ Availability was never the question here, and it is nobody's question any +// more: measured under a trap-restored mutation at 1d67130585, dropping that +// exclusion left `pnpm check:type-source-resolution` GREEN for this package +// (exit 0, 124 programs across 78 packages), unlike the 14 of 18 entries that +// gate reported as red. That gate was retired (2026-09-18, #18373), and +// `check-type-check-coverage.mjs` no longer offers the exclusion edit as a +// route at all -- withdrawn under the maintainer ruling of 2026-09-18 on +// #18953, so the sibling config is the only route it prescribes. ⛔ Read the +// green above as a reading taken on one tree, never as a standing clearance, +// and ⛔ read the gate's silence as neither. +// // ⛔ STRICTNESS IS UNTOUCHED. `strict`, `noUnusedLocals`, `noUnusedParameters`, // `noImplicitReturns`, `esModuleInterop` and the rest are inherited from // `tsconfig.json` (and through it from the repo root). `rootDir: ".."` and the diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index 9f550119a0f..479aa36f4de 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -937,9 +937,12 @@ const EXEMPT = { // So it took the #5286 sibling route (`tsconfig.test.json` named by the // `typecheck` script), which puts the same 10 files in front of tsc while // leaving `tsconfig.json` -- the only config that gate reads -- untouched. The -// general lesson, which is this ledger's to carry: the two remedies are +// general lesson, which is this ledger's to carry: the two edits were // interchangeable only where the excluded tests import nothing the src layer -// does not, and that is a property to MEASURE per package, never to assume. +// does not, and that was a property to MEASURE per package, never to assume. +// ⛔ Nobody has to weigh them any more -- the exclusion edit is no longer an +// offered route (#18953); the sibling config is the whole prescription, and +// this paragraph is the record of why, not a second option. // // `@objectstack/rest` GRADUATED from this ledger (#12542; entry: 155 raw, // re-measured 37 raw across 13 files under the sibling program). It is worth a @@ -987,10 +990,13 @@ const EXEMPT = { // an exclusion go red the way trigger-record-change did, 4 stay green // (`objectql`, `lint`, `formula`, `verify`), and the 19th (`cli`) has no // exclusion to drop at all -- its tests are hidden by an `include` that never -// reaches them. So that package was the majority case, not the exception, and -// the graduation message no longer offers the exclusion route without its -// precondition. Re-measure before relying on the split: it moves with every -// import a test file gains. +// reaches them. So that package was the majority case, not the exception -- +// and that is the measurement the graduation message was withdrawn on: it no +// longer offers the exclusion route AT ALL (maintainer ruling of 2026-09-18 on +// #18953), once the gate that decided the precondition had itself been retired +// (#18373). ⛔ The 14/4 split above is kept here as a READING, never as a route +// to pick from: it was taken at e47d5ef61, it moves with every import a test +// file gains, and nothing measures it any more. // // ── #14062: three plugin entries GRADUATED, and what replaced them ─────────── // @@ -1094,14 +1100,16 @@ const EXEMPT = { // what WOULD happen, not about an existing dead pin, exactly as `cli`'s // graduation recorded for its own 115 files. // -// ⚠️ ROUTE (b) WAS AVAILABLE HERE and was still not taken. The #11491 note -// above names `verify` as one of the 4 entries whose exclusion could be dropped -// with `check:type-source-resolution` staying green, and that split was -// re-measured on 2026-09-04 under a trap-restored mutation and still holds for -// this package (exit 0; 124 programs across 78 packages). It was declined on -// module semantics: `tsconfig.json` inherits NodeNext from the repo root and -// would hold the test layer to a resolver vitest never runs it under. Onboard -// by WIRING, not by widening the build config. +// ⚠️ DROPPING THE EXCLUSION WAS AVAILABLE HERE and was still not taken. The +// #11491 note above names `verify` as one of the 4 entries whose exclusion +// could be dropped with `check:type-source-resolution` staying green, and that +// split was re-measured on 2026-09-04 under a trap-restored mutation and still +// held for this package (exit 0; 124 programs across 78 packages). It was +// declined on module semantics: `tsconfig.json` inherits NodeNext from the repo +// root and would hold the test layer to a resolver vitest never runs it under. +// Onboard by WIRING, not by widening the build config -- which is now the only +// route the graduation message prescribes at all (#18953), so this record is +// history and ⛔ not a second option anybody still has to weigh. const TEST_DEBT = { // ── #14710: `@objectstack/cli` GRADUATED, and it was not paid down ───────── // @@ -4482,11 +4490,32 @@ function ratchetRemedyCarriesAuthority(message) { // // So the fix is not more words. It is the branch: `m.ledger` is already on // every measurement, and each ledger's remedy prints only where it is the -// remedy. Neither is dropped -- both are still offered, in the branch that -// owns them. Within TEST_DEBT there IS a real choice of route, so that one -// keeps both and names the PRECONDITION plus the command that decides it: a -// message the reader has to open a gate's source to act on has not fixed -// anything. +// remedy. Neither LEDGER is dropped -- each still gets the remedy that is its +// own, in the branch that owns it. +// +// ── Within TEST_DEBT the exclusion route came OUT (#18953) ────────────────── +// +// TEST_DEBT used to print a SECOND route beside the sibling config: drop the +// `**/*.test.ts` entry from `exclude` (or widen `include` to reach the test +// tree). Whether that route was available for a given package was decided by +// `check:type-source-resolution` -- and that gate was RETIRED under the +// maintainer ruling of 2026-09-18 on #18373, leaving an official route whose +// precondition nothing measured any more. What it had measured is the second +// bullet above: RED for 14 of the 18 entries that had an exclusion to drop. A +// remedy that is wrong 14 times out of 18, with nothing left to say so, is not +// a remedy, so the maintainer ruling of 2026-09-18 on #18953 (decision batch +// #159 item 4, letter ②, maintainer verbatim 「同意」) withdrew it: the printed +// prescription names (a) alone, and the docs that restated the route drop it +// with the same edit. +// +// ⛔ The other way out was REJECTED in that same ruling, so the way back in is +// closed from both sides: having THIS gate measure the precondition itself +// rebuilds half of a gate the maintainer had just retired. An author who +// widens `include` to the test tree anyway does so on their own judgement -- +// ⛔ not on this gate's advice, which now says nothing about that edit in +// either direction. The self-test pins the withdrawal as ANTI-content, the way +// every other branch here is pinned, because the state a well-meaning re-merge +// returns to is the one that printed both. // // ⛔ This changes no verdict and no number. Graduation candidates were, and // remain, a NOTE -- never a failure. @@ -4511,16 +4540,9 @@ function graduationRemedy({ ledger, isRoot = false }) { `Onboard it: put the hidden test files in front of tsc, and delete the TEST_DEBT entry in the same ` + `PR. ⛔ Adding a \`typecheck\` script is NOT the remedy here -- this ledger is "src checks, tests ` + `are hidden", so the package already has one.\n` + - ` (a) The #5286 sibling route: add a \`tsconfig.test.json\` that reaches the ` + - `tests and NAME it in the \`typecheck\` script. Always available -- it leaves \`tsconfig.json\` alone.\n` + - ` (b) Drop the \`**/*.test.ts\` entry from \`exclude\` in \`tsconfig.json\` (or widen \`include\` to ` + - `reach the test tree). ⛔ The gate that DECIDED whether this route was available, ` + - `\`check:type-source-resolution\`, was RETIRED under the maintainer ruling of 2026-09-18 on #18373 ` + - `-- so nothing measures it for you any more, and ⛔ its silence is not a clearance. What it measured ` + - `has not changed: the re-admitted tests import workspace packages this package's src program never ` + - `held, its registry was ⛔ SHRINK-ONLY so registering the new ones was never the way out, and it read ` + - `red on 14 of the 18 entries that have an exclusion to drop. Treat (b) as the worse route and prefer ` + - `(a), which leaves \`tsconfig.json\` alone; nothing in this gate's own verdict will tell you.` + ` (a) The #5286 sibling route, and the ONLY route this gate prescribes: add a ` + + `\`tsconfig.test.json\` that reaches the tests and NAME it in the \`typecheck\` script. Always ` + + `available -- it leaves \`tsconfig.json\` alone.` ); } if (ledger === 'DEBT') { @@ -6405,15 +6427,17 @@ function selfTest() { + 'that remedy is a no-op on every one of them -- the misfire #11491 was filed on.', }, { - label: 'TEST_DEBT graduation still carries what the retired gate measured about the exclusion route', + label: 'TEST_DEBT graduation prescribes the sibling config ALONE -- the exclusion route is withdrawn', message: testDebtGrad, - present: ['check:type-source-resolution', 'SHRINK-ONLY', 'tsconfig.test.json'], - absent: [], - why: 'the exclusion route read red on 14 of the 18 entries that have an exclusion, and this gate ' - + 'never ran it. A message the author has to read a second gate\'s SOURCE to act on is the half ' - + 'of #11491 that a correct-but-terse rewrite would leave unfixed -- and once that gate was ' - + 'retired (2026-09-18, #18373) the measurement is the ONLY thing left warning the author, so ' - + 'these needles stay exactly as they were.', + present: ['tsconfig.test.json', 'the ONLY route this gate prescribes'], + absent: ['exclude', 'widen', 'check:type-source-resolution', 'SHRINK-ONLY'], + why: 'the exclusion route read red on 14 of the 18 entries that had an exclusion, and the gate that ' + + 'decided the precondition per package was retired (2026-09-18, #18373), so nothing measured it. ' + + 'The maintainer ruling of 2026-09-18 on #18953 withdrew the route rather than leave an official ' + + 'path that is wrong 14 times out of 18: the remedy names (a) alone. These are ANTI-content ' + + 'needles on purpose -- a presence-only assertion would sit green through exactly the ' + + 're-merge that hands the route back, and the retired gate\'s NAME is only one of the spellings ' + + 'it could come back under, which is why `exclude` and `widen` are named beside it.', }, { label: 'the workspace root graduates through `typecheck:root`, never through `typecheck`', @@ -6428,9 +6452,12 @@ function selfTest() { label: 'an unrecognised ledger inherits NEITHER remedy', message: gradNote({ ledger: 'FUTURE_DEBT' }), present: ['FUTURE_DEBT'], - absent: [ADD_SCRIPT, 'drop the test exclusion', 'check:type-source-resolution'], + absent: [ADD_SCRIPT, 'drop the test exclusion', 'tsconfig.test.json'], why: 'a third ledger silently receiving DEBT\'s advice is how this message was wrong for TEST_DEBT ' - + 'for its whole life. Saying less is the only safe default.', + + 'for its whole life. Saying less is the only safe default. ⚠️ The TEST_DEBT needle here is ' + + '`tsconfig.test.json` and NOT the retired gate\'s name: that name left the message when #18953 ' + + 'withdrew the exclusion route, and an anti-content needle naming a string no branch can emit ' + + 'proves nothing about inheritance.', }, ]; for (const c of gradCases) { From bbd5e225a2ce88995c21b5694f17b67590fa2fef Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 12:08:32 +0000 Subject: [PATCH 2/2] refactor(check-type-check-coverage): the ANTI-content failure line no longer claims a cause it cannot know Every `absent` needle failed with "which is the other ledger's remedy". That was true while the only anti-content assertions were cross-ledger ones; the TEST_DEBT case now pins a route this gate withdrew rather than another ledger's advice, and the sentence read false on exactly the four failures the withdrawal's ablation produces. The line now states what the case pins and leaves the cause to each case's own `why`, which already carries it. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude --- scripts/check-type-check-coverage.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index 479aa36f4de..0172bd0836f 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -6469,8 +6469,8 @@ function selfTest() { for (const needle of c.absent) { if (c.message.includes(needle)) failures.push( - `#11491 graduation remedy — ${c.label}: message STILL contains ${needle}, which is the other ` - + `ledger's remedy. ${c.why}`, + `#11491 graduation remedy — ${c.label}: message STILL contains ${needle}, which this case pins ` + + `as ABSENT -- another ledger's remedy, or a route this one no longer offers. ${c.why}`, ); } }