diff --git a/.changeset/cli-test-typecheck-wiring.md b/.changeset/cli-test-typecheck-wiring.md new file mode 100644 index 0000000000..0771625c64 --- /dev/null +++ b/.changeset/cli-test-typecheck-wiring.md @@ -0,0 +1,52 @@ +--- +"@objectstack/cli": patch +--- + +fix(cli): wire `packages/cli`'s test layer into `check:test-typecheck`, so its 115 test files are type-checked at all (#14710) + +`packages/cli/tsconfig.json` declares `include: ["src"]` and no `exclude` at +all, and the package's `typecheck` script was a bare `tsc --noEmit` against that +very config — so the 115 test files in the sibling `test/` tree were read by no +tsc program anywhere. This is the shape AGENTS.md already forbids, reached by +the OTHER spelling: not an `exclude` naming the test globs, but an `include` +that never reaches them. + +Measured at `5a5336b399` with the workspace closure built first, rather than +read off the config: `tsc --noEmit --listFiles -p tsconfig.json` puts 1009 files +in the program and **0** of the 115 among them, while 119 of 119 non-test +`src/**` files and all 121 `src/**` test files ARE there — so the zero is the +`include` line, not a probe that sees nothing. The directional control is +`packages/drivers/driver-memory`, whose tsconfig carries no test exclusion: the +same probe puts 40 of its 40 test files in the program. Under the new +`tsconfig.test.json` the count is **115 of 115**, plus the three package-root +harness modules (`vitest.config.ts`, `vitest-tiers.ts`, +`vitest-tiers.fixtures.ts`) and `test/helpers/serve-process.ts`. + +Onboarded by *wiring* to the shared mechanism (`scripts/check-test-typecheck.mts`) +the way `objectql`, `rest`, `lint`, the fourteen `packages/plugins/**` and +`runtime` are wired, never by copying it: a sibling `tsconfig.test.json` +matching vitest's real module semantics (`module: esnext`, +`moduleResolution: bundler`), named by `typecheck` via +`check:test-typecheck --project`. Strictness is untouched and inherited; not one +`any` and not one `@ts-expect-error` was added to any test file to open the +gate. `rootDir` IS widened to `../..`, the way `packages/client`'s test config +already does it — this package's tests sit outside the build config's +`rootDir: "src"`, and three of them import fixtures from +`examples/app-showcase/src/**`. + +**Seeded, not repaired, per this card's triage ruling.** The layer reports 28 +errors across 3 files and they are recorded EXACT and shrink-only in the new +`test-typecheck-debt.json`. Every one is pre-existing: no test file is edited +here. The other 112 files carry no entry, so any error they gain is red on +arrival. + +This is a CONVERSION rather than a new debt-opening decision. The same +population under the build config's inherited NodeNext reports 144 — exactly the +number `scripts/check-type-check-coverage.mjs` already held for this package in +its per-PACKAGE `TEST_DEBT` ledger, class for class — and that entry graduates +here, as the pairing forces. The 144 → 28 step is attributed in both directions +with no remainder: −120 config-tier diagnostics that dissolve under vitest's +module semantics (TS2835 ×56, the TS7006 ×59 cascading above them, TS2307 ×3, +TS18046 ×2) and +4 that collapsing the cascade exposed (TS18048 ×4 in +`test/i18n-extract-action-description.test.ts`, previously masked by an `any` +from two unresolved imports). The 24 TS2339 survive unchanged, file for file. diff --git a/packages/cli/package.json b/packages/cli/package.json index b6c8166d07..84bef8d86e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -22,7 +22,9 @@ "build": "if [ -n \"$OS_SKIP_DTS\" ]; then tsc -p tsconfig.build.json --noCheck --declaration false --declarationMap false; else tsc -p tsconfig.build.json; fi", "dev": "tsc -p tsconfig.build.json --watch", "test": "vitest run", - "typecheck": "tsc --noEmit", + "typecheck": "tsc --noEmit && pnpm check:test-typecheck", + "check:test-typecheck": "tsx ../../scripts/check-test-typecheck.mts --self-test && tsx ../../scripts/check-test-typecheck.mts --package packages/cli --project tsconfig.test.json", + "gen:test-typecheck-debt": "tsx ../../scripts/check-test-typecheck.mts --update --package packages/cli --project tsconfig.test.json", "check:app-nav-i18n": "node scripts/check-app-nav-i18n.mjs --self-test && node scripts/check-app-nav-i18n.mjs" }, "keywords": [ diff --git a/packages/cli/test-typecheck-debt.json b/packages/cli/test-typecheck-debt.json new file mode 100644 index 0000000000..12ffd47685 --- /dev/null +++ b/packages/cli/test-typecheck-debt.json @@ -0,0 +1,18 @@ +{ + "_comment": "Per-file tsc error debt of the @objectstack/cli TEST layer (#5286). `tsconfig.test.json` compiles `src/**/*.test.ts` — which `tsconfig.json` excludes and therefore no gate ever read — and every file below still carries errors from before that gate existed. THIS FIELD IS GENERATED: every regeneration rewrites it from scripts/check-test-typecheck.mts, and the EXACT ratchet below requires a regeneration on every repair — so an edit made here is gone by the next one. Anything true of THIS package goes in the sibling `_note` field, which is authored, is preserved verbatim, and is never written by the generator (#12624). This comment states NO cause for the errors, deliberately: the classes differ per package and per file, they move as the debt is paid down, and a cause written here is rewritten verbatim into every ledger by every regeneration — so it outlives its own repair and cannot be corrected in the file where it is read. Measure instead, before repairing anything: `tsc --noEmit --pretty false -p tsconfig.test.json` in the package prints the real classes with their TS codes. Each entry maps a file to its per-SIGNATURE error counts, never to a bare total (#13470): a signature is the TS code plus the diagnostic message with structural type blobs collapsed, and it carries NO line or column — so the pin survives edits that move code around, and only stops matching when the error itself becomes a different error. EXACT ratchet, judged by re-running tsc: a file that gains errors is red, a file that loses them is red until its number is re-recorded, a file that reaches zero is red until its entry is deleted, a signature that ARRIVES or VANISHES is red even when the file total is unchanged, and a file NOT listed here may have no errors at all. Regenerate with: pnpm --filter @objectstack/cli gen:test-typecheck-debt", + "_note": "SEEDED, NOT REPAIRED, and that was this card's own triage ruling rather than this author's convenience: 28 errors across 3 of this package's 115 test files, measured at 5a5336b399 with the workspace closure built first, on the PR that first put those files in front of tsc. Repairing them in the same change would have buried the gate under a cleanup, which is how an onboarding stops landing; the ratchet only has to shrink from here. THIS PACKAGE REACHED THE HIDDEN STATE BY THE OTHER SPELLING, which is the part worth carrying to the next package: `tsconfig.json` here has no `exclude` at all. It declares `include: [\"src\"]`, and these 115 files live in a sibling `test/` tree the glob simply never reaches — the identical end state AGENTS.md forbids by naming the `exclude` spelling, arriving through a door that rule's text does not cover. Measured rather than read off the config: `tsc --noEmit --listFiles -p tsconfig.json` puts 1009 files in the program and 0 of the 115 among them, while 119 of 119 non-test `src/**` files and all 121 `src/**` test files ARE there — so the zero is the `include` line, not a probe that sees nothing. The directional control is `packages/drivers/driver-memory`, whose tsconfig carries no test exclusion: the same probe puts 40 of its 40 test files in the program. Under `tsconfig.test.json` the count is 115 of 115, plus the 3 package-root harness modules (`vitest.config.ts`, `vitest-tiers.ts`, `vitest-tiers.fixtures.ts`) and `test/helpers/serve-process.ts`, none of which any tsc program read before either. WHERE THE 28 CAME FROM, both directions attributed with no unexplained remainder: the same file population under the build config's inherited NodeNext semantics reports 144, and 144 is exactly what `scripts/check-type-check-coverage.mjs` had recorded for this package in its per-PACKAGE `TEST_DEBT` ledger — class for class on re-measurement (TS7006 x59, TS2835 x56, TS2339 x24, TS2307 x3, TS18046 x2). So this file does not add debt, it holds the SAME population one level finer, per file and per signature, and that `TEST_DEBT` entry graduated in the same change. 120 of the 144 dissolve under this program's vitest-matching module semantics (TS2835 x56 extension-less relative imports, the TS7006 x59 cascading above them, TS2307 x3, TS18046 x2) and were about the CHECK rather than the code; collapsing that cascade EXPOSED 4 more (TS18048 x4 in test/i18n-extract-action-description.test.ts, previously hidden behind an `any` from its two unresolved imports of `../src/utils/i18n-extract` and `../src/utils/i18n-coverage`). The 24 TS2339 survive the move unchanged, file for file and count for count. 144 - 120 + 4 = 28. A SECOND CONFIG-TIER PILE THAT IS NOT IN THIS LEDGER, recorded so nobody re-derives it: this package's test tree sits OUTSIDE the build config's `rootDir` (`src`), so with `rootDir` inherited the same program adds 116 x TS6059 — one per file in `test/`, saying nothing about any test. `tsconfig.test.json` widens `rootDir` to `../..` the way `packages/client`'s test config already does, and for one measured reason beyond the 116: three of these tests import fixtures from `examples/app-showcase/src/**`, which is outside this package entirely, so even `rootDir: \".\"` still reports 3 x TS6059. WHAT IS ALREADY GATED, and it is nearly all of the package: 112 of the 115 test files carry no entry here at all, so any error any one of them gains is red on arrival. ⚠️ This layer holds ZERO `@ts-expect-error` directives — measured across all 115 files plus the 3 root harness modules, not assumed — so unlike packages/spec and packages/client no pin was silently dead here, and none reports TS2578 under this program. The card's sharpest line ('a `@ts-expect-error` in any of those files is a phantom check') is therefore a statement about what WOULD happen, not about an existing dead pin. What the gap really cost is the other half: 144 real diagnostics that no gate has ever reported, and no way to write a type-level pin in this tree at all. THE PILE IS CONCENTRATED, which is the useful thing for whoever pays it down: test/data-model-rules.test.ts x23 is 23 of the 28 on its own, and all 23 are TS2339 property reads off a lint-diagnostic type ('message' x17, 'fix' x4, 'severity' x2) — one narrowing helper on that type is very nearly the whole ledger. The remaining 5 are 4 TS18048 in one file and a single TS2339 in another. RECORDED EXACTLY, no bootstrap margin: this layer has never been gated, so the first new error in it should go red rather than be absorbed. ⚠️ That includes test/run-dev-unbuilt-workspace.e2e.test.ts, a known repo-wide RUNTIME flake whose fix was in the merge queue when this landed: it type-checks clean and carries no entry here, so a type error it gains is red like any other. A flake in `vitest` is not a diagnostic in `tsc` and this ledger never sees one.", + "entries": { + "test/data-model-rules.test.ts": { + "TS2339: Property 'fix' does not exist on type '…'.": 4, + "TS2339: Property 'message' does not exist on type '…'.": 17, + "TS2339: Property 'severity' does not exist on type '…'.": 2 + }, + "test/i18n-declared-surface-gate.test.ts": { + "TS2339: Property 'locale' does not exist on type '…'.": 1 + }, + "test/i18n-extract-action-description.test.ts": { + "TS18048: 'bundles.en.objects' is possibly 'undefined'.": 2, + "TS18048: '…' is possibly 'undefined'.": 2 + } + } +} diff --git a/packages/cli/tsconfig.test.json b/packages/cli/tsconfig.test.json new file mode 100644 index 0000000000..cf31dbccbf --- /dev/null +++ b/packages/cli/tsconfig.test.json @@ -0,0 +1,132 @@ +// The TEST-layer type-check program (#14710), adopting the mechanism #5286 set +// for `packages/spec` and #5449 generalised. `tsconfig.json` beside this file +// stays exactly as it is: it is the BUILD config, and its `include: ["src"]` is +// correct for a build — `tsconfig.build.json` ships `src` and nothing else. +// This sibling puts the unread layer in front of tsc, and `package.json`'s +// `typecheck` script NAMES it (via `check:test-typecheck --project`), because a +// config no script invokes is exactly the phantom this whole change is about. +// +// ⚠️ THIS PACKAGE REACHED THE HIDDEN STATE BY THE OTHER SPELLING, and that is +// the part worth carrying to the next package. AGENTS.md forbids `exclude`-ing +// `*.test.ts` from a package tsconfig. This package has no `exclude` AT ALL — +// it declares `include: ["src"]`, and its 115 test files live in a sibling +// `test/` tree the glob simply never reaches. Identical end state, arriving +// through a door the rule's text does not cover. Whether that gap should be +// closed in AGENTS.md or in `check:type-check-coverage` itself is deliberately +// NOT this file's business (the card's triage said so); what IS this file's +// business is that `hiddenTests` — which walks the whole package rather than +// the include roots — is the only detector that could ever have seen it. +// +// BEFORE THIS FILE, NO tsc PROGRAM COMPILED A SINGLE `test/` FILE HERE, and +// that is measured rather than read off the config. At 5a5336b399 with the +// workspace closure built first, `tsc --noEmit --listFiles -p tsconfig.json` +// puts 1009 files in the program and **0** of the 115 `test/**/*.test.ts` +// among them — while 119 of 119 non-test `src/**` files AND all 121 of the +// package's `src/**` test files ARE there. So the zero is the `include` line +// and not a probe that sees nothing, and the contrast is inside this one +// package: tests under `src` always compiled, tests under `test/` never did. +// The directional control is `packages/drivers/driver-memory`, whose tsconfig +// carries no test exclusion: the same probe puts **40 of its 40** test files in +// the program. Under this file the count is **115 of 115** (1416 files total). +// `pnpm --filter @objectstack/cli typecheck` exiting 0 was a true sentence +// carrying no information about any file in `test/`. +// +// THE THREE PACKAGE-ROOT HARNESS MODULES COME IN HERE TOO, deliberately and not +// as a separate concern: `vitest.config.ts`, `vitest-tiers.ts` and +// `vitest-tiers.fixtures.ts` were in no tsc program either (#14554 recorded +// them as NOT MEASURED rather than claiming coverage). They fall out of this +// same `include`, as does `test/helpers/serve-process.ts` — the one non-test +// module in the test tree, which had its own `UNCHECKED_SOURCE_DEBT` row in +// `scripts/check-type-check-coverage.mjs` predicting it would "graduate with +// the TEST_DEBT one rather than before it". Both graduated in this change. +// +// What differs from the build config, and what deliberately does NOT: +// - module semantics, plus `rootDir`. The tests are written and executed as +// ESM by vitest (esbuild/vite), and this package IS `"type": "module"`, so +// the build config's NodeNext compiles them as ESM too — and then demands +// explicit `.js` extensions on relative imports, which vitest does not. +// Measured cost of that mismatch here: 120 of the 144 raw diagnostics — +// TS2835 x56 (extension-less relative import), the TS7006 x59 sitting above +// them (an import that does not resolve makes every symbol it names `any`), +// TS2307 x3 and TS18046 x2. Those 120 are about the CHECK, never about the +// code. Matching vitest is fidelity, not laxity. `lib` keeps the build +// config's `DOM`/`DOM.Iterable`, restated rather than narrowed: unlike +// `packages/runtime`, this package's own build config already declares them. +// - ⚠️ Collapsing that cascade EXPOSES errors as well as removing them, so +// 144 − 120 is 24 and the real figure is 28: +// `test/i18n-extract-action-description.test.ts` gains 4 x TS18048 that its +// two unresolved imports (`../src/utils/i18n-extract`, +// `../src/utils/i18n-coverage`) had been hiding behind `any`. The 24 TS2339 +// survive the move unchanged, file for file and count for count. Both +// directions are attributed in `test-typecheck-debt.json`; there is no +// unexplained remainder. +// - `rootDir` IS widened, to `../..`, and this is the one place this file +// departs from `packages/runtime`'s. Runtime's tests live under `src`, so +// its `rootDir` could stay inherited. This package's live in a sibling +// `test/` tree that is NOT under the inherited `rootDir` of `src`, and the +// measured cost of leaving it alone is 116 x TS6059 — one per file in +// `test/`, saying nothing about any test. `"."` is not enough either: +// three of these tests import fixtures from `examples/app-showcase/src/**`, +// outside this package entirely, and still report 3 x TS6059. `../..` is +// the repo root and is exactly the shape `packages/client`'s test config +// already uses for the same reason. ⛔ It widens the ROOT, never the +// strictness. +// - ⛔ STRICTNESS IS UNTOUCHED. `strict`, `esModuleInterop`, +// `forceConsistentCasingInFileNames` and the rest are inherited from +// `tsconfig.json`, and `types: ["node"]` restates it. Nothing here may +// loosen a type rule; if a test does not compile, that is the finding. +// ⛔ Not one `any` and not one `@ts-expect-error` was added to any test file +// to open this gate — that shape is what turns a real gate into a phantom. +// +// ⚠️ ONE THING THE FILING OVERSTATED, recorded so the next reader does not go +// looking for it: the card's sharpest line — "a `@ts-expect-error` in any of +// those 112 files is a phantom check that evaluates never" — is a statement +// about what WOULD happen, not about an existing pin. Measured here: this layer +// holds **zero** `@ts-expect-error` directives across all 115 test files and +// the 3 root harness modules, so no pin was silently dead and none reports +// TS2578 under this program. What the gap really cost is the other half: 144 +// real diagnostics that no gate has ever reported, and no way to write a +// type-level pin in this tree at all. The first half is now ledgered; the +// second is now possible. +// +// MEASURED at 5a5336b399, workspace closure built first (an error count taken +// against an unbuilt closure is not a reading — unresolved-import cascades +// inflate it): this program reports **28 errors across 3 files**, from a raw +// 144 under the inherited NodeNext semantics. That 144 is the same number +// `scripts/check-type-check-coverage.mjs` recorded for this package in its +// per-PACKAGE `TEST_DEBT` ledger — reproduced class for class on re-measurement +// (TS7006 x59, TS2835 x56, TS2339 x24, TS2307 x3, TS18046 x2) — which is why +// that entry GRADUATES in this change rather than being paid down: the +// identical population is now held one level finer, per file and per signature, +// in `test-typecheck-debt.json` beside this config. By code the residue is +// TS2339 x24 and TS18048 x4; `test/data-model-rules.test.ts` carries 23 of the +// 28 on its own, all of them property reads off a lint-diagnostic type, so one +// narrowing helper there is very nearly the whole ledger. +// +// Every one of the 28 is PRE-EXISTING: this change edits no test file, and each +// would have been reported on `origin/main` had this program always existed. +// They are ledgered per file and per signature in `test-typecheck-debt.json`, +// EXACT and shrink-only — a file that gains an error is red, one that loses one +// is red until re-recorded, one that reaches zero is red until its entry is +// deleted, a signature that arrives or vanishes is red even at a constant +// total, and a file NOT listed there may have no errors at all. +// ⛔ The 28 are NOT repaired here, per this card's triage ruling: seeding the +// ledger and letting the ratchet shrink is the deliverable, and turning the +// onboarding into a cleanup is how it stops landing at all. +// +// ⚠️ 112 of the 115 files carry NO ledger entry, and that is load-bearing rather +// than incidental: any error any one of them ever gains is red on arrival. That +// is the half of this gate that starts working today. +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "../..", + "module": "esnext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["node"] + }, + "include": ["test/**/*", "vitest.config.ts", "vitest-tiers.ts", "vitest-tiers.fixtures.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index 12fdaecc18..31fc069c92 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -966,46 +966,42 @@ const EXEMPT = { // sharper instrument, one that also reddens on a wholesale substitution of // error IDENTITY at a constant total, which a per-package integer cannot see. const TEST_DEBT = { - '@objectstack/cli': { - errors: 144, - note: 'TS7006 x59 (implicit any), TS2835 x56 (NodeNext extensions), TS2339 x24, TS2307 x3, TS18046 x2. ' - + 'LOWERED 146 -> 144 (#13109) and RE-TALLIED above from the same run, not rescaled: ' - + 'test/platform-page-i18n-parity.test.ts 2 -> 0 (1 TS2835 + 1 TS7006), from adding the `.js` ' - + 'extension to its one `../src/utils/i18n-extract` import -- the same one-import repair #8612 made ' - + 'twice below, taken here because that file gained new tests in the same PR and untyped test code ' - + 'is what let the cascade grow. FULLY ATTRIBUTED: no other file moved, and the per-code and ' - + 'per-file tallies below were re-measured whole rather than decremented. ' - + 'The package #7353 was really about, and the largest single thing the exclude-shaped detector could ' - + 'not see: `tsconfig.json` says `include: ["src"]` and has no `exclude` AT ALL, so there was never an ' - + 'exclusion to notice, and the test files in the sibling `test/` tree are read by nothing -- not ' - + '`pnpm --filter @objectstack/cli typecheck`, which exits 0 on this package today, not CI, only this ' - + 'ledger. 65 hidden files now, up from 56 at #7353 while the layer itself stayed frozen; the other 57 ' - + 'test files sit under `src` and always compiled, which is why the file count reads 65 and not 122. ' - + 'Lowered 188 -> 146 (#8612), both numbers measured on main at 35086781b with the closure built and ' - + 'the two import extensions as the ONLY difference between the two trees, so the -42 is FULLY ' - + 'ATTRIBUTED with no unexplained remainder: test/i18n-coverage.test.ts 35 -> 0 ' - + '(1 TS2835 + 34 TS7006) and test/i18n-extract.test.ts 7 -> 0 (1 TS2835 + 6 TS7006), from adding the ' - + '`.js` extension to one import each. Outside those two files the before and after diagnostic sets ' - + 'are identical line for line, and nothing new appeared anywhere. ' - + 'WHAT THE PILE IS NOW MADE OF, and it is not a nearly-graduated one: 56 of the 59 extension-less ' - + 'relative imports this layer carried are still there, spread over 23 files, and every one of the 59 ' - + 'surviving TS7006 sits in a file that also carries a TS2835 -- there is no implicit-any anywhere in ' - + 'this layer without a broken import above it, and the 23 files carrying a TS2835 are EVERY file in ' - + 'this layer that carries any error at all. Read the top-of-ledger NodeNext note before sizing it: ' - + 'TS2835 plus the cascade it causes are 115 of the 144 and are 56 repairs, not 115. Concentrated ' - + 'rather than spread -- test/data-model-rules.test.ts x26, test/i18n-declared-surface-gate.test.ts ' - + 'x19, test/i18n-section-coverage.test.ts x18, test/commands.test.ts x15, ' - + 'test/remote-api-commands.test.ts x12 are 90 of it. ' - + 'One thing #8612 learned that the next extension fix here should expect: collapsing a cascade can ' - + 'EXPOSE errors rather than only remove them. Fixing the i18n-extract import took that file from 7 ' - + 'errors to 4 NEW TS2339, because it carried an `(e: { path: string[] })` parameter annotation ' - + 'written to dodge the implicit-any while the import was broken, and that annotation narrowed the ' - + 'real `ExpectedEntry` away; deleting the annotation took the file to 0. Those workaround ' - + 'annotations are part of this debt and are invisible to the count until the import above them ' - + 'resolves, so budget for a repair being bigger than its TS2835 line suggests. ' - + 'RECORDED EXACTLY, no bootstrap margin: this layer has never been gated, so the first new error in ' - + 'it should go red rather than be absorbed.', - }, +// ── #14710: `@objectstack/cli` GRADUATED, and it was not paid down ───────── +// +// `cli` (144) left this ledger on 2026-09-03, under the same shape as the five +// above and for the same reason: it now has a `tsconfig.test.json` its +// `typecheck` script NAMES, so `hidesTests` is false for it and this gate's +// per-PACKAGE approximation has nothing left to approximate. ⛔ Read that first +// — a deleted TEST_DEBT entry normally means the errors are gone, and here it +// does not. Not one of the 144 was repaired: this change edits no test file. +// +// ⚠️ This package reached the hidden state by the OTHER SPELLING, which is the +// reading worth carrying forward: its `tsconfig.json` has no `exclude` at all. +// It declares `include: ["src"]`, and its 115 test files live in a sibling +// `test/` tree that the glob simply never reaches. The exclude-shaped detector +// this ledger's own note describes could not see it; only `hiddenTests` (which +// walks the whole package rather than the include roots) could. +// +// The identical population is now held one level finer, per FILE and per +// SIGNATURE, in `packages/cli/test-typecheck-debt.json`: **28 errors across 3 +// of the package's 115 test files**, measured at 5a5336b399 with the closure +// built. The 144 -> 28 step is the config-tier subtraction this ledger's top +// note describes, attributed in both directions with no remainder: -120 that +// dissolve under the test program's vitest-matching module semantics (TS2835 +// x56 extension-less relative imports, the TS7006 x59 cascading above them, +// TS2307 x3, TS18046 x2) and +4 that collapsing the cascade EXPOSED (TS18048 +// x4 in test/i18n-extract-action-description.test.ts, previously masked by an +// `any` from two unresolved imports). The 24 TS2339 survive the move +// unchanged, file for file and count for count. The 144 recorded here was +// exact and stayed exact to the end — re-measured on the way out at +// 5a5336b399, the raw program reports 144 class for class. +// +// ⚠️ Unlike the four packages above, this one's 115 hidden files sat OUTSIDE +// the build config's `rootDir` (`src`), so the honest program needed +// `rootDir` widened the way `packages/client`'s test config already does. +// Measured: with `rootDir` inherited, the same program reports 116 additional +// TS6059 — a config-tier pile that says nothing about any test. + '@objectstack/mcp': { errors: 53, note: 'TS18046 x51 -- `json` is of type unknown, one `await res.json()` idiom repeated across four ' @@ -1259,13 +1255,19 @@ const PHANTOM_PIN_DEBT = {}; // worse than leaving it: COVERED would start passing on a script that never // reads `src`, and RECONCILED would then force out a 51-error DEBT entry whose // errors are all still there. It graduates with that entry, not before it. -const UNCHECKED_SOURCE_DEBT = { - 'packages/cli/test': 'One non-test module, `test/helpers/serve-process.ts`, the spawn harness the ' - + '`os serve` e2e tests share. It measures 0 errors on its own, and it is not separate debt: it ' - + 'sits inside the hidden test tree already measured by TEST_DEBT[\'@objectstack/cli\'] (56 of ' - + 'that package\'s 110 test files are outside `include`). Repairing it means repairing that ' - + 'layer, so this entry graduates with the TEST_DEBT one rather than before it.', -}; +// +// ── #14710: `packages/cli/test` GRADUATED, exactly as its own entry foretold ── +// +// The deleted entry said it: "this entry graduates with the TEST_DEBT one +// rather than before it." Both happened in the same change. `packages/cli`'s +// `typecheck` now names `tsconfig.test.json`, whose `include` reaches the whole +// `test/` tree, so `test/helpers/serve-process.ts` — the spawn harness the +// `os serve` e2e tests share, and the one non-test module in there — is read by +// a tsc program for the first time. It still measures 0 errors, as the entry +// recorded, so nothing was repaired to graduate it: the directory stopped being +// unread source. ⛔ This list only shrinks, and it is empty now; a new entry +// needs the same justification any DEBT entry needs. +const UNCHECKED_SOURCE_DEBT = {}; /** * GENERATED_COVERED's declared table (#10880) -- the generated `include` roots diff --git a/scripts/check-type-source-resolution.mjs b/scripts/check-type-source-resolution.mjs index cc83534e99..3c44652a1f 100644 --- a/scripts/check-type-source-resolution.mjs +++ b/scripts/check-type-source-resolution.mjs @@ -535,6 +535,57 @@ const KNOWN_DIST_RESOLVED_TYPE_IMPORTS = { '@objectstack/service-job', '@objectstack/service-messaging', '@objectstack/service-package', '@objectstack/spec', '@objectstack/types', ], + // ── #14710 re-baseline, on the onboarding limb above ───────────────────── + // + // A NEW entry, and this package was CLEAN before it -- unlike #14504's + // `runtime`, which already had one to re-measure. Read that first, because a + // package leaving the clean list normally means something regressed and here + // it does not: nothing about the code or the build state changed. This + // package's `typecheck` script began naming `tsconfig.test.json`, which is + // what onboarding a test layer IS, so a program that was always there became + // one this gate can SEE. The `packages/cli/test/` tree reached these nine + // deps on every vitest run already; no tsc program had ever been pointed at + // it, so no `--list` ever counted the pairs. + // + // CONDITION 1 by measurement, not assertion: the gate's own failure text + // annotates all nine `(via tsconfig.test.json)`, and a `--list` taken with + // the `package.json` wiring reverted -- the ONLY difference between the two + // trees, restored and verified by blob-hash equality -- reports NO + // `@objectstack/cli` entry at all. Every one of the nine is reached only + // through the program this change onboarded. + // + // CONDITION 2 -- the numbers, both from `--list` on the same checkout with + // the closure built: + // + // before 115 programs / 78 packages, 56 non-clean, 279 package-dep pairs + // after 116 programs / 78 packages, 57 non-clean, 288 package-dep pairs + // + // so +1 program, +9 pairs, +1 ENTRY and +1 non-clean package. The +1s are + // the honest cost of making an unseen program visible, and the ratchet is + // shrink-only from 288. + // + // CONDITION 3 -- reviewed, and `paths` is deliberately NOT the tool here, on + // the onboarding limb's own measured grounds (PR #12570, restated for + // `packages/rest` above): redirecting these specifiers to source would bill + // OTHER packages' source diagnostics into `packages/cli/test-typecheck-debt.json`, + // a ledger those packages cannot see and nobody can pay down -- and that + // ledger is this change's whole deliverable. + // + // ⚠️ One asymmetry recorded rather than repaired, because it is a property of + // this family and not of this card: `packages/cli/vitest.config.ts` aliases + // exactly four specifiers to source (`service-cache`, `plugin-auth`, + // `metadata-core`, and `create-objectstack/created-summary`), and + // `@objectstack/plugin-auth` is among the nine below -- so vitest reads it + // from `src` while this program reads its `dist`. That is the same trade + // `packages/rest`'s block above names (vitest aliases 2 of its 6), and it is + // why blanket `paths` would not be fidelity to vitest either. The + // source-vs-dist ledger for aliases is `scripts/check-test-source-alias.mjs`, + // which owns this question; this registry only records that the pair exists. + '@objectstack/cli': [ + '@objectstack/cloud-connection', '@objectstack/core', '@objectstack/driver-sql', + '@objectstack/lint', '@objectstack/mcp', '@objectstack/platform-objects', + '@objectstack/plugin-auth', '@objectstack/spec', '@objectstack/types', + ], // #14386 re-baseline (the onboarding limb above): a NEW entry, reached ONLY // through `tsconfig.typecheck.json` -- a program that card ADDED (this // package's `typecheck` was a bare `tsc --noEmit` before it, with no sibling diff --git a/scripts/regen-artifacts.mjs b/scripts/regen-artifacts.mjs index ab3b19303f..6f5ca474a7 100644 --- a/scripts/regen-artifacts.mjs +++ b/scripts/regen-artifacts.mjs @@ -564,6 +564,32 @@ export const NOT_DRIVER_MANAGED = Object.freeze([ + 'prevents both more likely and harder to see: at that size a recomputed number reads as ' + 'plausible, and the per-signature keys underneath it would be silently rewritten with it.', }, + // ── #14710: `packages/cli`'s starting ledger, same instrument ────────────── + // + // The seventh row this manifest adds for the mechanism, and the SMALLEST + // ledger in the family — 28 errors over 3 of the package's 115 test files. + // Size is the reason to read this row rather than skim it, and the reason is + // the opposite of the runtime row's above: at 28 errors over 3 files, a + // recomputation on a half-merged tree does not read as plausible noise, it + // reads as a CLEAN result. Three quarters of this package's test files that + // carry errors would have to vanish for the ledger to look wrong, and a + // recompute that silently drops an entry looks exactly like a repair somebody + // landed. The package's `TEST_DEBT` entry in + // `scripts/check-type-check-coverage.mjs` graduated in the same change, so + // this file is now the ONLY record of that population anywhere in the tree. + { + path: 'packages/cli/test-typecheck-debt.json', + gen: 'gen:test-typecheck-debt', + owner: '@objectstack/cli', + why: + 'a SHRINK-ONLY ratchet — see `packages/plugins/plugin-approvals/test-typecheck-debt.json` ' + + 'above; same generator, same per-package ledger, same reason a merge must never recompute ' + + 'it: the half-merged tree is not the tree whose type errors this file records, so a file ' + + 'that GAINED errors would enter the ledger as merge noise instead of as red. This ledger ' + + 'is the SMALLEST in the family (28 errors over 3 files) and that cuts the other way from ' + + 'the runtime row above: a recomputed value here reads as a clean, plausible REPAIR rather ' + + 'than as noise, and a dropped entry is indistinguishable from a file somebody fixed.', + }, { path: 'packages/sdui-parser/objectui-lockstep.json', gen: 'gen:sdui-lockstep',