Skip to content

[finding] packages/lint has no tsc program that compiles its tests — the 2,700-line rule pin file, receiver scan included, is type-checked by nobody #14173

Description

@claude

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions