The runner already defaults to Node 24 and local dev is on v24.14.0, while CI
pinned 22 and @types/node was still ^22 — so the type definitions described an
older runtime than anything actually running.
- CI node-version 22 -> 24
- @types/node ^22 -> ^24 (matches the runtime major; the newer ^26 line targets
Node 26, which we don't run)
- declare engines.node >=24 so local, CI, and Vercel resolve the same major
All six CI gates pass locally on Node 24: secret-leak, format:check, test (97),
lint, typecheck, build.
Clears the "Node.js 20 is deprecated / being forced to run on Node.js 24"
warning by moving off the v4 actions:
- actions/checkout v4 -> v7
- actions/setup-node v4 -> v7
- pnpm/action-setup v4 -> v6
Kept pnpm/action-setup ahead of setup-node (its `cache: pnpm` needs pnpm on
PATH first) and noted why; pnpm version still comes from the packageManager
field in package.json.
The repo had zero tests despite PLAN §7 mandating them.
- 97 unit tests over the pure logic: checkup/customer/catalog/template Zod
schemas, parseNum, computeAge, and invoice math
- extracted lib/billing/totals.ts (sumLineTotals/formatVnd) so invoice math is
testable, and refactored the invoice route + dashboard to use it
- 16 Playwright smoke tests that need no database: auth gates redirect to
sign-in, VI-default rendering, password-reveal toggle, /en locale, 404
- playwright.config passes placeholder env inline via webServer.env so no
env file is ever needed; pnpm test wired into CI (E2E stays local)
tests/e2e/README.md documents the seeded-data prerequisites for the full
queue -> checkup -> prescription -> paid -> invoice happy path, which is
blocked on a provisioned Supabase project rather than faked with skipped tests.
Defense-in-depth check that fails the build (and the local pre-push
workflow) if a server secret value is assigned to a NEXT_PUBLIC_*
variable — those get bundled into the browser by Next.js.
- scripts/check-no-secret-leak.mjs: git grep for the assignment shape,
excluding lockfiles and the script itself
- package.json: pnpm check:no-secret-leak
- .github/workflows/ci.yml: run the guard right after install, before
format/lint/typecheck/build
- docs/threat-model.md: close the last Unresolved item