veritas-kanban/docs/testing/critical-path-coverage.md
Brad Groux 93e946693d
test: establish critical-path coverage ratchets (#1183)
* test: add critical-path coverage ratchets

* fix: harden coverage ratchet enforcement

* fix: fetch coverage comparison history

* fix: close coverage policy downgrade paths

* fix: verify changed critical statements

* fix: ignore non-executable coverage changes

* fix: distinguish mixed type-only coverage edits

* fix: classify coverage changes by runtime output

* fix: enforce runtime-significant whitespace changes

* fix: classify multiline coverage changes as hunks

* fix: group equal-line coverage rewrites
2026-08-23 13:48:41 -05:00

5.6 KiB

Critical-path coverage ratchets

Veritas measures risk-weighted execution boundaries instead of presenting one repository-wide percentage as a quality claim. The governed policy is critical-path-coverage.json.

Run the coverage gate

pnpm test:coverage

The command builds @veritas-kanban/shared, then runs each applicable workspace from its own working directory. It uses one Vitest worker for the lock-sensitive server boundary suite and at most four elsewhere. This preserves package-specific path and timeout behavior while collecting V8 coverage for server, web, cli, mcp, and desktop. Server and web execute the governed critical-path test files listed exactly in the policy; the smaller CLI, MCP, and desktop suites run completely. Live provider and MCP integration opt-ins are removed from the coverage process environment. The ordinary canonical unit gate remains responsible for every test. Coverage requires no live credentials or external services.

To measure only the packages selected by CI:

pnpm test:coverage --packages server,web

Each measured package writes HTML, coverage-summary.json, and statement-level coverage-final.json under coverage/<package>/. The root coverage/critical-path-summary.json and .md files contain the machine-readable and human-readable boundary results. Coverage output is generated evidence and remains ignored by Git.

Governed boundaries

The initial floors were measured on 2026-08-23 with Node 22-compatible Vitest 4.1.11 and V8. A floor is the exact measured percentage, not a rounded repository target. Any lower line, branch, function, or statement result fails the gate. CI checks out complete Git history and compares the policy with the event's base commit, so removing boundaries or runner inputs, redirecting a report, narrowing include patterns, or lowering a floor fails even when the edited policy would otherwise pass.

Package Boundary Lines Branches Functions Statements
server dispatch-runtime 64.97% 57.48% 70.22% 63.67%
server auth-validation-redaction 57.58% 52.85% 50.91% 56.89%
server storage-locks-migrations 39.51% 34.11% 46.90% 38.73%
web api-auth-realtime 37.74% 34.36% 28.67% 36.79%
cli api-compatibility 52.07% 44.53% 65.45% 51.02%
mcp api-tool-contracts 52.39% 43.67% 60.00% 51.44%
desktop preload-ipc-trust 66.24% 66.54% 54.86% 65.50%

The long-term floor for every critical boundary and metric is 80%. Raise ratchets whenever added tests improve a result. Prioritize branches and functions below 80%, starting with the web API, authentication, mutation, and realtime boundary. Do not reduce a floor to make CI pass.

CI behavior

The deterministic scope selector emits coverage_packages separately from ordinary affected workspaces. A full verification run measures all five packages. A focused run measures only a package whose governed critical-path source or tests changed. CI publishes the complete coverage/ directory for 14 days and writes the boundary table to the job summary. Documentation and non-critical source changes do not repeat the coverage suite. Artifact upload uses always() so partial and failing reports remain available for diagnosis.

Broad source patterns in the policy automatically include new files in governed areas. Because workspace coverage uses all: true, an untested critical file contributes zero coverage and drops its boundary. CI additionally rejects every changed governed source file with no executable coverage entry or zero covered lines, so stronger coverage elsewhere cannot hide new untested code. Every added or modified executable statement in governed source must be covered by the measured suite or carry an explicit reviewed exception. The gate reads the base-to-head diff alongside the statement-level V8 report, so historical coverage elsewhere in an existing file cannot satisfy the new-code test requirement. Comments and type-only edits do not manufacture executable statements. Ambiguous mixed rewrites fail closed: if an unequal-line hunk combines runtime-neutral formatting with a runtime change and cannot be separated safely, all changed lines in that hunk require coverage. This favors enforcement over guessing and bounds the classification work per hunk. The authentication boundary includes server Zod schemas and the shared authoritative API permission map in addition to middleware and redaction code.

Exclusions and reviewed exceptions

Generated output, test files, fixture directories, declaration files, type-only types.ts modules, and the SQLite test helper are excluded consistently in workspace Vitest configs. Production runtime source is not globally excluded.

If a critical source file cannot be tested immediately, add a narrow exceptions entry to its boundary with all four fields:

{
  "path": "server/src/example.ts",
  "reason": "Why automated coverage is not currently practical.",
  "owner": "BradGroux",
  "trackingIssue": "#1234",
  "reviewBy": "2026-09-30"
}

Exception paths must name one exact repository-relative file; globs and traversal are rejected. The reason must be substantive, the owner must be a GitHub login, the tracking issue must remain linked, and the real ISO review date must fall within 90 days. The pull request must explain the compensating verification and receive normal review. Remove the exception when coverage lands.