Found while implementing #13935 (packages/lint field-rule root vocabulary). Out of that card's scope — recorded, not fixed.
Same class as #14062 (packages/plugins/**), #13676 (packages/objectql) and #12542 (packages/rest, closed). This instance is packages/lint, which no card names yet — and it is the package whose job is hosting the platform's static-analysis rules.
Measurement
packages/lint/tsconfig.json:
{
"extends": "../../tsconfig.json",
"compilerOptions": { "outDir": "./dist", "rootDir": "./src", "types": ["node"] },
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
}
The package's typecheck script is a bare tsc --noEmit, there is no sibling tsconfig.test.json, and packages/lint carries no test-typecheck-debt.json — the four packages wired to scripts/check-test-typecheck.mts are client, objectql, rest, spec.
Verified with --listFiles on this checkout rather than inferred from the config:
npx tsc --noEmit --listFiles | grep -c 'validate-expressions.test.ts' -> 0
npx tsc --noEmit --listFiles | grep -c 'validate-expressions.ts' -> 1
So pnpm --filter @objectstack/lint typecheck renders a green verdict having read the implementation and none of its tests.
Why this instance is worth naming separately
The count is not the point; what is sitting in the untyped zone is. packages/lint/src/validate-expressions.test.ts alone is roughly 2,700 lines and is built almost entirely out of compile-time and meta pins — the #5017 receiver scan that reads the rule's own source text, the TRACKED_UNDECLARED_READS shrink-only list, the residual-root table generated from the real baseline, the FieldSchema.shape key cross-checks. AGENTS.md names this exact shape as how a pin becomes a phantom check: an assertion in a file no tsc program compiles asserts nothing about types, and weakening it is invisible to every gate.
It also has a second-order effect the sibling cards do not: this package's rules are consumed by scripts/check-doc-formula-expressions.mjs and by os validate, so a pin that quietly stops meaning what it says takes a published diagnostic surface with it.
⚠️ To be precise about what is and is not broken, in the same spirit as #12511: vitest still RUNS these tests and they still pass. What is absent is type checking of the test sources — so a wrong key, a stale schema shape, or a signature drift in a pin's own scaffolding is caught by nobody.
Not proposing the remedy
Two routes exist in-repo and choosing between them is a maintainer/triage call, not this finding's: the packages/spec pattern (a sibling tsconfig.test.json named in the typecheck script) or onboarding to scripts/check-test-typecheck.mts with a measured shrink-only ledger. Onboarding by wiring, never by copying, per AGENTS.md. ⛔ I am not proposing a severity.
Provenance
Measured in /home/user/objectstack-issue-13935 at origin/main base 2bff79ae3, during #13935.
Generated by Claude Code
Found while implementing #13935 (
packages/lintfield-rule root vocabulary). Out of that card's scope — recorded, not fixed.Same class as #14062 (
packages/plugins/**), #13676 (packages/objectql) and #12542 (packages/rest, closed). This instance ispackages/lint, which no card names yet — and it is the package whose job is hosting the platform's static-analysis rules.Measurement
packages/lint/tsconfig.json:{ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src", "types": ["node"] }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] }The package's
typecheckscript is a baretsc --noEmit, there is no siblingtsconfig.test.json, andpackages/lintcarries notest-typecheck-debt.json— the four packages wired toscripts/check-test-typecheck.mtsareclient,objectql,rest,spec.Verified with
--listFileson this checkout rather than inferred from the config:So
pnpm --filter @objectstack/lint typecheckrenders a green verdict having read the implementation and none of its tests.Why this instance is worth naming separately
The count is not the point; what is sitting in the untyped zone is.
packages/lint/src/validate-expressions.test.tsalone is roughly 2,700 lines and is built almost entirely out of compile-time and meta pins — the #5017 receiver scan that reads the rule's own source text, theTRACKED_UNDECLARED_READSshrink-only list, the residual-root table generated from the real baseline, theFieldSchema.shapekey cross-checks. AGENTS.md names this exact shape as how a pin becomes a phantom check: an assertion in a file no tsc program compiles asserts nothing about types, and weakening it is invisible to every gate.It also has a second-order effect the sibling cards do not: this package's rules are consumed by
scripts/check-doc-formula-expressions.mjsand byos validate, so a pin that quietly stops meaning what it says takes a published diagnostic surface with it.Not proposing the remedy
Two routes exist in-repo and choosing between them is a maintainer/triage call, not this finding's: the
packages/specpattern (a siblingtsconfig.test.jsonnamed in thetypecheckscript) or onboarding toscripts/check-test-typecheck.mtswith a measured shrink-only ledger. Onboarding by wiring, never by copying, per AGENTS.md. ⛔ I am not proposing a severity.Provenance
Measured in
/home/user/objectstack-issue-13935atorigin/mainbase2bff79ae3, during #13935.Generated by Claude Code