Uh oh!
There was an error while loading. Please reload this page.
fix(devx): type-check-coverage sees include-shaped hidden test layers (#7353) - #7921
Conversation
…#7353) TESTS_COVERED asked one per-config question -- does some `exclude` name the tests -- so it detected a config that steered tsc AWAY from the test layer and never a config that had simply never steered tsc TOWARD it. A package with no `exclude` at all and `include: ["src"]` beside a sibling `test/` tree reported as fully test-covered while nothing compiled a line of it. #7312 moved two example apps from hiding their tests to compiling them and this gate's headline did not move by one file, because neither app had ever counted toward it. Decide it per FILE instead, against the tsc programs that ACCOUNT for the package: the configs the `typecheck` script invokes, or -- where there is no such script -- `tsconfig.json`, which is what `measureDebt` runs, so a test file that config reads is counted in DEBT rather than hidden. Without that second clause every DEBT package's whole test tree would land in TEST_DEBT for the sole reason that DEBT already owns it. Per file also makes partial coverage sayable: `packages/cli` keeps 54 of its 110 test files under `include` and 56 outside it, and 56 is the honest number. `measureTestDebt` learns the same lesson -- dropping exclusions is only half of un-hiding, because only half of hiding is an exclusion. It now adds the unread files to `include` one at a time (a directory glob would have pulled `e2e/global-setup.ts` into app-showcase's number) and neutralises `rootDir`, which is `src` in most of these packages and otherwise answers with one TS6059 per added file: cli measured 56 TS6059 that way and 188 real errors without it. Census over all 78 workspace packages: 3 flip, 65 files, 198 raw errors. @objectstack/cli 56 files, 188 errors (include-shaped) @objectstack/metadata-fs 6 files, 6 errors (include-shaped) @objectstack/example-showcase 3 files, 4 errors (e2e/ beside include) test layer: 19 -> 22 packages, 719 -> 784 files, 1607 -> 1805 frozen errors Not new debt: all three were in this state before the ledger existed, and only the question changed. The 9 pre-existing ledger surpluses (#7888) are untouched. Fixes#7353
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-zhuang
commented
Aug 12, 2026
The gate's own message names the resolution:
This PR releases nothing. The entire diff is Both other options were considered and rejected on the record in the PR body's Changeset section before this run went red; my dispatch also names the label as the route explicitly. Rest of CI at Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#7353
scripts/check-type-check-coverage.mjsonly. No package'stsconfig*.jsonis touched — this changes what the gate can see, not what the repo declares.The defect
TESTS_COVERED asked one per-config question — does some
excludename the tests:So it detected a config that steered tsc away from the test layer and never a config that had simply never steered tsc toward it.
rootswas computed fromincludea few lines above and fedconfigCoversfor the PINS_CHECKED walk, but never fed this.The dispatch's second reading is confirmed: the package-level aggregate
anyExcludesTests && testsInvoked.length === 0makes the hole strictly bigger. A package with noexcludeat all hasanyExcludesTests === false, so it is judged covered at the aggregate level too, regardless ofinclude. The include-shaped package was invisible at both levels.Also confirmed: the
countRootscomment hands the sibling-test/case to PINS_CHECKED, and PINS_CHECKED only collects files carrying@ts-expect-error. All 65 newly-visible files carry zero pin directives, so the hand-off caught none of them — reported by neither half, exactly as the card says.Census — all 78 workspace packages (root included), at
b9f930bThe census the card asks for, and the reason the ratchet move is small. The key question is not "does this package have a typecheck script" but which tsc program accounts for its test files:
My first census pass over-counted this at 16 packages / 422 files by treating "no
typecheckscript" as "nothing reads the tests". That was wrong: for those packagesmeasureDebtrunstsc -p tsconfig.json, and every one of them already has its tests under that config'sinclude(e.g.metadata-protocol's 72 test files live insrc/, which itsinclude: ["src/**/*"]reaches). Their tests are counted in DEBT, not hidden. Corrected count: 3 packages, 65 files, 198 raw errors. No staging is needed and there is no separate follow-up card to file.Which packages flip status — the full list
@objectstack/cliinclude: ["src"], noexcludeat all, 56 tests in a siblingtest/tree@objectstack/metadata-fsinclude: ["src/**/*"], noexcludenaming tests, all 6 tests intest/@objectstack/example-showcaseincludenamessrc/test, thee2e/Playwright tree beside it is unreadNothing else changes status. All 19 existing TEST_DEBT packages keep the same verdict and the same file count — for each of them the old include-roots count and the new unread-file count were already identical (719 both ways), so their numbers move by zero.
packages/cliis the headline: 188 raw tsc errors in a test layer that no gate, no ledger and no CI job had ever read. 159 of the 188 are TS2835 plus the TS7006 cascade it causes — the NodeNext pair from the top-of-ledger note, one repair rather than 159.The fix
Decide it per file, against the programs that account for the package:
The second clause is load-bearing. A package with no
typecheckscript has no invoked program, but it is not unmeasured —measureDebtrunstsc -p tsconfig.json, so a test file that config reads is counted in DEBT rather than hidden. Without it, 353 files would move into the ledger that means "src checks, tests are hidden" purely because DEBT already owns them.Per file also makes partial coverage sayable, which the per-config form could not express: cli keeps 54 of its 110 test files under
includeand 56 outside it, and the honest number is 56 — not 0, and not 110.measureTestDebtlearns the same lesson: dropping exclusions is only half of un-hiding, because only half of hiding is an exclusion. It now adds the unread files toincludeone at a time —e2e/**/*would have pullede2e/global-setup.tsinto app-showcase's number and billed the test layer 6 errors from a file that is not a test — and neutralisesrootDir, which issrcin most of these packages: widening past it makes tsc answer with one TS6059 per added file and nothing else. Measured both ways on cli: 56 TS6059 withrootDirkept, 188 real errors with it neutralised. Under--noEmitthere is no output layout forrootDirto protect, so a TS6059 there measures the tape measure. For the 19 existing entries the generated config is byte-identical to before.Before / after gate output
Intermediate state, before the three ledger entries were added — the gate correctly refusing the newly-seen packages:
Reverse verification
A green run proves little here, so the load-bearing evidence is a planted regression. Planted a
TS2322inpackages/metadata-fs/test/contract.test.ts— a package #7312 never touched — and ran both detectors against the same tree:pnpm --filter @objectstack/metadata-fs exec tsc --noEmitorigin/main, run in-tree)19 package(s) ... 719 files, metadata-fs not mentioned22 package(s) ... 784 files(structural pass runs no compiler, as designed)--re-measure@objectstack/metadata-fs: TEST_DEBT records 6 raw tsc error(s), tsc --noEmit now reports 7 (+1)That is the card's mechanism reproduced on a fresh package and then closed: a type error that used to leave both
tsc --noEmitand this gate green now has exactly one gate that goes red on it.The three new entries are recorded exactly, with no bootstrap margin, so the first new error in any of these layers goes red immediately rather than being absorbed.
Relationship to #7888 (same file)
Read before writing, per the dispatch. My
--re-measureatb9f930bconfirms#7888's reading: 9 entries, 273 raw errors of surplus — identical total, identical entry set. My change moves none of them; the three new entries carry zero surplus, so the figure is 273 before and after.One correction to #7888's table:
@objectstack/plugin-authmeasures 107, not 106 (131 → 107is the−24its own surplus column and its273total already assume — themeasuredcell is the typo, the arithmetic is right). Nothing here fixes#7888;--lowerremains its own deliberate PR.Gates run locally
node scripts/check-type-check-coverage.mjs --self-testpnpm check:type-check-coveragepnpm check:type-check-debt(full--re-measure, 36 entries, 255s)node scripts/check-nul-bytes.mjspnpm exec eslint scripts/check-type-check-coverage.mjs --no-inline-configturbo run build --filter='./packages/*' --filter='./packages/*/*'— 70/70Self-test grew by 8 observation cases: 5 pinning
unreadTests(the include-shaped form with noexcludeanywhere; partial coverage; the #5286 sibling-config repair; the empty-program case) and 3 pinningaccountedPrograms(including the DEBT-territory clause, so the 353-file over-reach cannot be reintroduced silently).Changeset
None —
scripts/-only gate change, no published package, nothing reader-visible ships. Route is theskip-changesetlabel at review. Deliberately not adding an empty-frontmatter changeset (#5471 / #4898).Generated by Claude Code