Skip to content

fix(frontend): make the typecheck and coverage gates actually validate - #576

Open
umzcio wants to merge 3 commits into
ui-insight:mainfrom
umzcio:stack/01-ci-gates
Open

fix(frontend): make the typecheck and coverage gates actually validate#576
umzcio wants to merge 3 commits into
ui-insight:mainfrom
umzcio:stack/01-ci-gates

Conversation

@umzcio

@umzcio umzcio commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Two CI gates in frontend/ currently pass without validating anything. This makes
both real.

npm run typecheck checks zero files. It is defined as tsc --noEmit, but
frontend/tsconfig.json is solution-style ("files": [] plus references), and
tsc --noEmit without -b does nothing on such a config. Verified with
npx tsc --noEmit --listFiles, which emits 0 files. So make frontend-typecheck
and the typecheck step of frontend-ci have been vacuous. Type errors were only ever
caught incidentally by npm run build, which does run tsc -b. Changing the script
to tsc -b makes it check 1,444 files.

The coverage gate measures about a fifth of the codebase. v8 only instruments
files that a test imports, and vitest.config.ts sets no coverage.include, so the
denominator is "whatever the tests happened to pull in". Measured before this change:
45.9% lines (1,404/3,057) against a real tree of 17,839 lines — an honest 7.9%.

Worse, the gate is non-monotonic: adding the first test to a large untested file
lowers the reported percentage by widening the denominator. Adding one test that
imports ConfigTab (~2,400 lines) would have dropped coverage to roughly 31% and
turned CI red. The gate actively penalised writing the first test for any big module.

Setting a fixed whole-src denominator makes coverage monotonic — adding a test can
now only ever raise it — and comparable over time. The headline number drops to
single digits because that is the truth about a ~95k-line frontend.

Changes

  • frontend/package.json: typecheck script tsc --noEmittsc -b.
  • frontend/vitest.config.ts: add coverage.include: ['src/**/*.{ts,tsx}'], an
    exclude list (test files, src/main.tsx, *.d.ts), and coverage.thresholds
    re-baselined just under the honest measurement.
  • Thresholds are now single-sourced in vitest.config.ts. Removed the duplicated
    --coverage.thresholds.lines=40 flag from the frontend-test Makefile target and
    from package.json's ci script.
  • Makefile: replace the stale "set just below current measured (~44%)" comment with
    one explaining the whole-src denominator and the monotonicity property.
  • CLAUDE.md: the documented figure said 35%, the Makefile enforced 40%, and neither
    described what was being measured. Corrected.

Test Plan

  • Shared checks pass (make ci)
  • Release check passes if packaging or deployment changed (make release-check) — n/a, no packaging change
  • Manually tested the affected feature(s) — see verification below
  • Updated CHANGELOG.md for user-facing or operator-facing changes — not done; this is operator-facing (CI behaviour), please advise on preferred wording
  • Updated deploy/release docs — CLAUDE.md corrected; no install/release path change

Verification performed:

  • npx tsc --noEmit --listFiles | wc -l0 before, npx tsc -b --listFiles | wc -l1444 after.
  • Proved the gate now bites: injected const x: number = 'not a number' into a src
    file and confirmed npm run typecheck fails with
    TS2322: Type 'string' is not assignable to type 'number'; reverted and confirmed
    it passes again. Exit 0 alone was not accepted as evidence, since that is precisely
    the signal that hid the problem.
  • make frontend-ci exits 0.
  • grep -rn "thresholds.lines" Makefile frontend/package.json returns nothing.

Note for reviewers: tsc -b surfaced no pre-existing type-error backlog, because
npm run build had been running it all along. The broken script was buying false
confidence, not hiding real damage.

Related Issues

Closes #

umzcio and others added 2 commits July 24, 2026 22:07
`tsc --noEmit` is a no-op on this solution-style tsconfig (`files: []`
+ project references) — `npx tsc --noEmit --listFiles` emitted 0 files,
so the typecheck gate in `frontend-ci` was vacuous. Switch to `tsc -b`,
which builds both referenced configs (`tsconfig.app.json` and
`tsconfig.node.json`, both already `noEmit: true`) and now checks 1440
files.

Verified: introduced a deliberate `string` to `number` assignment in
main.tsx and confirmed `npm run typecheck` failed with
`TS2322: Type 'string' is not assignable to type 'number'.`, then
reverted and confirmed it passes again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v8 only instrumented files a test happened to import (no coverage.include),
so the 40% gate measured an import-driven denominator (45.92% lines,
1404/3057) rather than the whole src/ tree (7.87%, 1404/17839), and was
non-monotonic: importing a large untested file into its first test could
lower the reported number below the gate. Set coverage.include to
'src/**/*.{ts,tsx}' with excludes for test files, main.tsx, and .d.ts files,
so the denominator is fixed and adding a test can only raise the number.

Measured with the plan 009 test tranche in place: statements 8.1%
(1673/20643), branches 6.97% (1347/19312), functions 6.37% (401/6289),
lines 8.37% (1493/17837). Thresholds set just below each, rounded down:
statements 8, branches 6, functions 6, lines 8 — single-sourced in
vitest.config.ts.

Remove the duplicated --coverage.thresholds.lines=40 flag from Makefile's
frontend-test target and frontend/package.json's ci script, and fix the
stale coverage figures in CLAUDE.md and the Makefile comment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eline

The thresholds set alongside the whole-src-tree coverage denominator
(statements/lines 8%, branches/functions 6%) were measured wrong — actual
whole-src coverage is ~6.4% lines / 6.1% statements / 5.1% functions / 5.2%
branches, so CI failed on every push regardless of changes. Lower the
thresholds to sit just below the real numbers (matching the stated intent:
'set just below the honest whole-src measurement'), and correct CLAUDE.md's
stale ~8% note to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant