Quality Gates
Updated: 2026-05-28
Use these gates before pushing application changes. Code and test output are the source of truth; planning docs must be updated after the gates, not before them.
Required Commands
npm run typecheck
npm run lint:warning-budget
npm run test:run
npm run test:coverage
npm audit --omit=dev
npm run build
npm run self-hosted:standalone-check
curl -fsS "$APP_URL/api/version"
Expected current baseline:
- Runtime: Node
24.xis pinned inpackage.json, Docker, and the deploy pipeline's runtime settings. - TypeScript: 0 errors.
- Lint: 0 warnings, 0 errors. Budget enforced at 0 via
scripts/check-lint-warning-budget.mjs. - Unit Tests: 230 Vitest files / 1,653 tests pass in the current Node 24 launch-audit baseline.
- Test/spec inventory: 256 test/spec/setup files on disk, including 19 Playwright E2E spec files.
- E2E Tests: public production smoke currently returns
200for homepage, login, and/api/version; authenticated E2E seed credentials are normalized for the existing auth/case slices, but full launch-grade browser coverage still requires the route action-map. - Coverage: Vitest reporters: text + lcov + json-summary. Current enforced baseline is a low broad-surface regression floor (7% statements, 6% branches, 5% functions, 7% lines), 80% statement gates for
src/lib/auth,src/lib/billing,src/lib/cases,src/lib/ai, andsrc/proxy.ts, plus the existing Redis scoped gate. Documents and route-family thresholds are planned ratchets, not current launch blockers. - Audit: 0 advisories via targeted dependency overrides.
- Build:
npm run buildmust pass for the full local route surface. - Self-hosted runtime:
npm run self-hosted:standalone-checkmust pass afternpm run build, proving the standalone server, static assets, Docker copy rules, and the deploy start command are aligned. - Version endpoint:
/api/versionmust return200,Cache-Control: no-store,app: "CaseSolo", version, build SHA, build time, label, environment, and checked-at timestamp without requiring auth. Playwright launch gates use this as a server identity preflight before accepting an existing local server. - Build typecheck note: Next.js documents
typescript.ignoreBuildErrorsas safe only when type checks run elsewhere in the build/deploy process. CaseSolo keepsnpm run typecheckas the hard gate. The command runsnext typegenand thentscwithtsconfig.typecheck.json, which includes.next/typesand excludes stale.next/dev/typesartifacts that Next filters during its own build checker. Do not useCASESOLO_SKIP_NEXT_BUILD_TYPECHECK=1for local truth gates. - Deploy pipeline: hostssh (
git push hostssh mainto the bare repo on the app box) does a Dockerfile build + container swap;npm start(start-standalone.mjs) applies pending Prisma migrations fail-closed, then starts the standalone server. - API Documentation: OpenAPI 3.1.0 spec with 39 operations across 10 tag groups. Swagger UI at
/api-docs, raw JSON at/api/docs/openapi. - Environment Documentation:
.env.exampledocuments 50+ env vars across 17 categories.
Pre-Push Truth Script
Run the combined local gate when preparing a branch:
npm run truth:prepush
The script prints git status, typecheck, lint warning budget, tests, audit severity totals, and route/test inventory. It fails on type/test errors, ESLint errors, warning-budget growth, audit endpoint failures, and high or critical audit findings.
Current Security Expectations
- New metadata-writing case routes must verify tenant access and write with firm-scoped filters.
- Document update/delete routes should use firm-scoped
updateMany/deleteManyor a tenant-scoped helper instead of updating/deleting byidafter a separate ownership read. - Proxy bypass headers from outside the internal self-fetch path must not be accepted unless they carry the per-process random bypass token.
- Mutating API routes should keep CSRF validation active; any no-cookie grace behavior must be observable.
- New page/API pairs need contract tests so action names and response shapes cannot drift silently.
- Database-backed launch routes must have a migration/table-existence smoke so Prisma models cannot drift from the active database.
- Security-sensitive randomness must not use
Math.random(); usecrypto.randomUUID(),randomInt(), or persisted database IDs as appropriate. - Outbound webhook delivery IDs are externally visible and must remain cryptographic UUIDs, not timestamp/random suffixes.
- Settlement treasury finance IDs are ledger/payment identifiers and must remain cryptographic UUIDs, not timestamp/random suffixes.
- Registration firm slug suffixes are persisted tenant identifiers and must use cryptographic randomness.
- Registration and firm responses must serialize BigInt-backed values before returning JSON.
- Case numbers are generated per firm and must remain unique by
(firmId, caseNumber), not globally unique. - Dashboard shell controls should expose labels, landmarks, current-page state, and skip-navigation affordances so keyboard and assistive-tech users can reach primary workflows.
- Storage and public/share URL helpers must trim environment values and fall back safely for local preview origins.
- Client/portal upload flows that create a document record before storage transfer must keep an explicit pending/complete/failed state or cleanup path so unconfirmed objects do not fulfill client requests.
- Cache-backed authenticated APIs must use
privatecache headers and firm-scoped keys. Filtered list responses should bypass firm-wide list cache keys unless they have their own key and invalidation path. - Integration readiness responses must be secret-safe. Return env-name presence booleans and connection state, never OAuth tokens, client secrets, auth tokens, or provider account secrets.
- External provider clients (Stripe, Usermails, Deepgram, R2/S3, AI SDK, Textract, PostHog, Sentry, Redis) must use lazy initialization with explicit error messages when env vars are missing. Never create clients at module level with empty credentials.
- New API routes serving real data must not include
mock: trueflags or fabricated sample numbers. Usemode: 'partial'with honest zeroes when tracking infrastructure (e.g., Redis) is not configured. - Public build metadata belongs on
/api/version, not/api/health, so stale-client detection keeps working even when DB/Redis health is degraded. - Dev/test runner pages must not be reachable as ordinary production case workspaces.