Skip to content

Three tsconfigs exclude test files and CI typechecks none of them, so a type-level guard in any worker test is inert #599

Description

@serge-ivo

No CI gate typechecks a worker test, so every compile-time exhaustiveness guard in one is inert

Found while closing #588. RunHealth had a Record<RunHealth, true> arm intended to fail when a member was added. It could never fail: it is not compiled by anything.

workers/api/tsconfig.json   "exclude": ["src/**/*.test.ts", "src/lib/d1-sqlite.ts"]
workers/mcp/tsconfig.json   "exclude": ["src/**/*.test.ts"]
packages/sdk/tsconfig.json  "exclude": ["src/**/*.test.ts"]

ci.yml's four tsc --noEmit projects therefore never see a worker test, and vitest transpiles without typechecking. So a type-level assertion in a test file compiles nowhere and fails never — it is a comment that looks like a guard.

Population, measured: grep -rl 'Record<' workers/api/src workers/mcp/src --include='*.test.ts'145 files. Not every one is an exhaustiveness check, but that is the set in which such a check would be silently inert, and it includes at least one that was.

Why this outranks the individual guards it hides

This repo's whole verification discipline is ADR 0002 — a guard states what it measured — and its G4 practice is to break a guard deliberately and watch it go red. A type-level guard in a worker test cannot go red, so G4 silently passes it: the mutation is made, nothing fails, and the failure is indistinguishable from "the arm was already covered elsewhere".

Concretely, this defeated the exact mechanism #588 relied on. The fix there was to make RunHealth a runtime const array so an executed test could check it. That works — and it is a workaround for this issue, not a solution to it. Every other type-level assertion in those 145 files is still unchecked.

Acceptance criteria

  1. Worker and SDK test files are typechecked by CI — a tsconfig.test.json, a lifted exclude, or vitest --typecheck. State the choice and its cost; the exclusions are presumably there for a reason (build output, speed) and that reason must be answered, not ignored.
  2. Introducing a type error in a worker test fails CI. Demonstrated red.
  3. The count of type-level assertions that were inert is reported, not estimated. 145 is the Record< population, not the answer — how many were actually exhaustiveness guards, and how many were already wrong when this landed?
  4. Any that were wrong are fixed or removed. A guard that never worked and now fails is a finding, not a regression.
  5. store/console is checked too — its tsconfig has no exclude, so it may already be fine; confirm rather than assume.

Verified vs inferred

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions