Part of #1412 (Track B).
Context
Global thresholds (78% statements / 80% lines in vitest.config.ts) are a repo floor, not a PR gate: new under-tested code hides behind well-covered old code and the threshold bites long after the culprit merged.
Deliverables
- A script that joins vitest
json-summary/lcov output with git diff --unified=0 origin/main...HEAD to compute covered/total for CHANGED LINES only, per file.
- Fail the Coverage CI job when changed-line coverage < 70% (constant in one place), excluding the same paths vitest coverage already excludes plus test files.
- Failure output lists each offending file with its uncovered changed-line numbers — the message must point at exactly the contributor's own lines.
- Escape hatch: a PR label (e.g.
coverage-waiver) skips the gate; the job summary still prints the numbers.
Acceptance
- Deterministic against renames/deletes (deleted lines never count).
- Green on a docs-only PR without running coverage twice.
- Numbers shown in the CI job summary in both pass and fail cases (feeds the quality-delta comment issue).
Review amendments (2026-07-27)
- Besides gating changed-line coverage, REPORT (non-gating): changed-branch coverage and the count of changed executable lines excluded from coverage (excluded paths/istanbul-ignore), so exclusions cannot silently absorb new logic. These become rows for the quality-delta comment.
Part of #1412 (Track B).
Context
Global thresholds (78% statements / 80% lines in vitest.config.ts) are a repo floor, not a PR gate: new under-tested code hides behind well-covered old code and the threshold bites long after the culprit merged.
Deliverables
json-summary/lcov output withgit diff --unified=0 origin/main...HEADto compute covered/total for CHANGED LINES only, per file.coverage-waiver) skips the gate; the job summary still prints the numbers.Acceptance
Review amendments (2026-07-27)