Measured while implementing #10269 (PR #10755). Not repaired there: that card's surface
is four entry guards, and this is a property of the tsconfig include globs.
The measurement
@objectstack/objectql declares a typecheck script, passes it, and is counted as
covered by check:type-check-coverage (64/77 workspace packages type-checked ... 13 in the DEBT ledger, 1 exempt — objectql is in neither ledger). But its tsconfig is
{ "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.test.ts"] }so packages/objectql/scripts/** is outside the program the package's own
typecheck compiles (see the CORRECTION below: that is narrower than "no tsc program
at all", which is what this body first claimed). Ablation on
origin/main + the #10269 branch, at ca022387f2:
inject const OS_ABLATION_TYPE_ERROR: number = "definitely not a number";
into packages/objectql/scripts/dry-run-hash-compat.ts
confirm on disk grep -c OS_ABLATION_TYPE_ERROR -> 1
pnpm --filter @objectstack/objectql typecheck -> EXIT 0, zero `error TS` lines
pnpm exec tsc --noEmit --listFiles | grep -c scripts/dry-run-hash-compat.ts -> 0
restore, confirm on disk grep -c OS_ABLATION_TYPE_ERROR -> 0
The file appears zero times in tsc's own file list, and a number initialised with a
string sits in it without moving the gate.
CORRECTION (2026-08-21) — "in no tsc program at all" was too broad
This body originally read "so packages/objectql/scripts/** is in no tsc program at
all". That is wrong as written, and PR #10755 going red is what measured it. The
accurate statement is the one already in the title: that directory is invisible to
the package's owntsc -p, and it is not invisible to every tsc program.
The TEST_DEBT re-measure builds a different project. measureTestDebt calls
remeasureProject (scripts/check-type-check-coverage.mjs), which copies the package
tsconfig and drops every test glob from exclude — so
packages/objectql/src/dry-run-hash-compat.test.ts becomes a root file, and its
line 5 is:
import{runDryRun,typeLegacyMetadataRow}from'../scripts/dry-run-hash-compat';tsc pulls every transitively imported file into the program regardless of include,
so the script is compiled there. It is reached through the test, not through
include.
Measured: the import.meta.url that PR #10755 added to that script (the package
declares no "type", so under the repo-wide module: NodeNext it compiles as
CommonJS) produced exactly one TS1470 in the re-measure and drove
@objectstack/objectql from its frozen TEST_DEBT of 355 to 356 — a red
Type Check · debt ledger — while pnpm --filter @objectstack/objectql typecheck
stayed at exit 0 the whole time.
Why the ablation above could not see it: it ran the package'stypecheck and a
--listFiles over the package's own config, and that is precisely the program the
file is missing from. It never built the test-inclusive project.
The finding stands and this card stays open. The blind spot is real, and the
accidental coverage described here argues for the gate rather than against it: it is
not a property anyone declared. It holds only for the files some test happens to
import, only while that import exists, and it reports through a shrink-only debt
ledger — as a drifted count on a package the ratchet calls covered — rather than
through the package's own typecheck.
Why this is worth a card rather than a shrug
packages/objectql/scripts/dry-run-hash-compat.ts is not scratch: it is a
compatibility checker with a documented CLI, and packages/objectql/src/dry-run-hash-compat.test.ts
imports runDryRun from it. So a package test depends on a module whose types nothing
verifies.
The same shape holds for packages/core/examples/** (include: ["src/**/*"],
rootDir: "./src"), which docs/qa/platform-checklist/FOLLOW-UPS.md already notes in
passing for phase2-integration.ts — but @objectstack/core has no typecheck script at
all and is a ledgered DEBT entry (#4311), so that half is already tracked. The objectql
half is not: it is a package the ratchet reports as covered.
The gap in the ratchet, stated precisely
check:type-check-coverage is package-granular: it asks whether a package declares a
typecheck script or carries a ledger entry. It cannot see a package that declares one
and whose tsconfig include omits a real source directory. That is the same class AGENTS.md
already warns about for test files —
Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json — tsc --noEmit reads that config, so the exclusion hides the tests from the very check the
typecheck script advertises (a green gate over source nothing read).
— one level up: a whole directory rather than a file glob, and reached through include
rather than exclude, so the existing prose does not cover it.
Not proposing the remedy here
Several shapes are possible (a sibling tsconfig.scripts.json named in the typecheck
script, the packages/spec pattern; widening include; or a ratchet limb that compares a
package's tracked .ts files against tsc --listFiles), and which one is right depends on
how many packages are in this state — which nobody has counted. Filed so the count gets
made rather than guessed.
Dedupe
Grepped the titles and bodies of all 299 open issues for packages/objectql/scripts,
packages/core/examples, include.{0,20}src/\*\*, outside (every|any) tsconfig and
--listFiles. Only #10269 matches, and only because it names the two paths.
(search_issues was unavailable to this seat.)
Generated by Claude Code
Measured while implementing #10269 (PR #10755). Not repaired there: that card's surface
is four entry guards, and this is a property of the tsconfig
includeglobs.The measurement
@objectstack/objectqldeclares atypecheckscript, passes it, and is counted ascovered by
check:type-check-coverage(64/77 workspace packages type-checked ... 13 in the DEBT ledger, 1 exempt— objectql is in neither ledger). But its tsconfig is{ "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.test.ts"] }so
packages/objectql/scripts/**is outside the program the package's owntypecheckcompiles (see the CORRECTION below: that is narrower than "no tsc programat all", which is what this body first claimed). Ablation on
origin/main+ the #10269 branch, atca022387f2:The file appears zero times in tsc's own file list, and a
numberinitialised with astring sits in it without moving the gate.
CORRECTION (2026-08-21) — "in no tsc program at all" was too broad
This body originally read "so
packages/objectql/scripts/**is in no tsc program atall". That is wrong as written, and PR #10755 going red is what measured it. The
accurate statement is the one already in the title: that directory is invisible to
the package's own
tsc -p, and it is not invisible to every tsc program.The TEST_DEBT re-measure builds a different project.
measureTestDebtcallsremeasureProject(scripts/check-type-check-coverage.mjs), which copies the packagetsconfig and drops every test glob from
exclude— sopackages/objectql/src/dry-run-hash-compat.test.tsbecomes a root file, and itsline 5 is:
tsc pulls every transitively imported file into the program regardless of
include,so the script is compiled there. It is reached through the test, not through
include.Measured: the
import.meta.urlthat PR #10755 added to that script (the packagedeclares no
"type", so under the repo-widemodule: NodeNextit compiles asCommonJS) produced exactly one
TS1470in the re-measure and drove@objectstack/objectqlfrom its frozenTEST_DEBTof 355 to 356 — a redType Check · debt ledger— whilepnpm --filter @objectstack/objectql typecheckstayed at exit 0 the whole time.
Why the ablation above could not see it: it ran the package's
typecheckand a--listFilesover the package's own config, and that is precisely the program thefile is missing from. It never built the test-inclusive project.
The finding stands and this card stays open. The blind spot is real, and the
accidental coverage described here argues for the gate rather than against it: it is
not a property anyone declared. It holds only for the files some test happens to
import, only while that import exists, and it reports through a shrink-only debt
ledger — as a drifted count on a package the ratchet calls covered — rather than
through the package's own
typecheck.Why this is worth a card rather than a shrug
packages/objectql/scripts/dry-run-hash-compat.tsis not scratch: it is acompatibility checker with a documented CLI, and
packages/objectql/src/dry-run-hash-compat.test.tsimports
runDryRunfrom it. So a package test depends on a module whose types nothingverifies.
The same shape holds for
packages/core/examples/**(include: ["src/**/*"],rootDir: "./src"), whichdocs/qa/platform-checklist/FOLLOW-UPS.mdalready notes inpassing for
phase2-integration.ts— but@objectstack/corehas notypecheckscript atall and is a ledgered DEBT entry (#4311), so that half is already tracked. The objectql
half is not: it is a package the ratchet reports as covered.
The gap in the ratchet, stated precisely
check:type-check-coverageis package-granular: it asks whether a package declares atypecheckscript or carries a ledger entry. It cannot see a package that declares oneand whose tsconfig
includeomits a real source directory. That is the same class AGENTS.mdalready warns about for test files —
— one level up: a whole directory rather than a file glob, and reached through
includerather than
exclude, so the existing prose does not cover it.Not proposing the remedy here
Several shapes are possible (a sibling
tsconfig.scripts.jsonnamed in thetypecheckscript, the
packages/specpattern; wideninginclude; or a ratchet limb that compares apackage's tracked
.tsfiles againsttsc --listFiles), and which one is right depends onhow many packages are in this state — which nobody has counted. Filed so the count gets
made rather than guessed.
Dedupe
Grepped the titles and bodies of all 299 open issues for
packages/objectql/scripts,packages/core/examples,include.{0,20}src/\*\*,outside (every|any) tsconfigand--listFiles. Only #10269 matches, and only because it names the two paths.(
search_issueswas unavailable to this seat.)Generated by Claude Code