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
- 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.
- Introducing a type error in a worker test fails CI. Demonstrated red.
- 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?
- Any that were wrong are fixed or removed. A guard that never worked and now fails is a finding, not a regression.
store/console is checked too — its tsconfig has no exclude, so it may already be fine; confirm rather than assume.
Verified vs inferred
No CI gate typechecks a worker test, so every compile-time exhaustiveness guard in one is inert
Found while closing #588.
RunHealthhad aRecord<RunHealth, true>arm intended to fail when a member was added. It could never fail: it is not compiled by anything.ci.yml's fourtsc --noEmitprojects 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
RunHealtha 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
tsconfig.test.json, a liftedexclude, orvitest --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.Record<population, not the answer — how many were actually exhaustiveness guards, and how many were already wrong when this landed?store/consoleis checked too — its tsconfig has no exclude, so it may already be fine; confirm rather than assume.Verified vs inferred
excludelines verbatim; the fourtsc --noEmitprojects inci.yml; the 145-filegrep -rlcount; that vitest does not typecheck by default; thatRecord<RunHealth, true>inworkers/apiwas inert (the The server-side health verdict reports 'working' for all 89 finished runs — the enum has no member for a closed run #588 agent demonstrated it).Record<has many uses — but not measured, which is exactly what AC3 asks for. Do not assume the number is small because the fix looks large.