fix(ci): wire coverage observability into CI pipelines (Phase 1)#1404
Merged
Conversation
Add [tool.coverage] config to pyproject.toml (branch coverage, TYPE_CHECKING exclusions). Update ci.yml test step to collect coverage and render a summary table to GITHUB_STEP_SUMMARY. Add coverage collection to ci-integration.yml: each of the 4 shards uploads coverage.json as an artifact; the fan-in job downloads all shards, merges totals, and writes an integration coverage summary to GITHUB_STEP_SUMMARY. No threshold gate -- this is observability-only to establish a baseline. Local unit baseline: 80% statement coverage (42905 stmts). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix critical bug: fan-in was summing coverage JSON totals across 4 shards, quadruple-counting statements for files appearing in all shards. Now uploads .coverage binary data files and uses `coverage combine` for mathematically correct merge. - Create scripts/coverage-summary.py: shared stdlib-only renderer that both CI pipelines call, producing markdown summary with overall %, metrics table, and collapsible lowest-coverage files section. - Add include-hidden-files: true to shard artifact upload (dotfiles are excluded by default in actions/upload-artifact@v4). - Make coverage summary soft-fail (continue-on-error) so missing or partial coverage data never blocks the required fan-in check. - Reorder fan-in: coverage summary runs before shard-result gate so observability is preserved even when some shards fail. Closes #1399 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dd4efc7 to
87fbf4c
Compare
Address review feedback: read covered_lines from the JSON totals instead of computing it from num_statements - missing_lines. The values are identical but using the explicit field is clearer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danielmeppiel
approved these changes
May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Adds test coverage reporting to both CI pipelines (unit and integration) as Phase 1 of the progressive coverage ratchet (#1398). Coverage summaries appear in GitHub Actions job summaries as markdown tables — no PR is blocked.
Note
Phase 1 is observability only — no
fail_undergates. Phases 2–4 (#1401, #1402, #1400) will progressively tighten thresholds.Problem (WHY)
Approach (WHAT)
[tool.coverage.run]and[tool.coverage.report]topyproject.tomlwithbranch = true,parallel = true, and standard exclusion patterns--cov+ JSON report intoci.ymlunit test step; add coverage summary step--covinto integration shardPYTEST_EXTRA_ARGS; upload.coveragedata files per shard withinclude-hidden-files: truecoverage combine→coverage json→ shared summary script for correct multi-shard mergescripts/coverage-summary.py— stdlib-only shared renderer used by both pipelinescontinue-on-error: true/if: always()so coverage never blocks the required checkImplementation (HOW)
pyproject.toml— Adds[tool.coverage.run](source, branch, parallel) and[tool.coverage.report](exclusions forpragma: no cover,TYPE_CHECKING,NotImplementedError,@overload).scripts/coverage-summary.py— New file. Readscoverage.json, renders a markdown summary (overall %, metrics table, collapsible lowest-coverage files). Writes to stdout and$GITHUB_STEP_SUMMARY. Exits 0 on missing input (soft-fail). Stdlib only, ASCII source..github/workflows/ci.yml— Adds--cov --cov-report=term-missing:skip-covered --cov-report=json:coverage.jsonto the pytest invocation. Calls the shared summary script..github/workflows/ci-integration.yml— Each shard produces a.coveragebinary data file, renamed per-shard and uploaded withinclude-hidden-files: true. Fan-in job: checkout → Python →pip install coverage[toml]→ download withmerge-multiple: true→coverage combine→coverage json→ summary script. Coverage summary runs before shard-result gate so observability is preserved even when shards fail.Diagrams
Legend: Integration coverage data flow from shards to fan-in. The yellow region highlights the
coverage combinemerge path.sequenceDiagram participant S as Shard Job 1..4 participant A as Upload Artifact participant F as Fan-in Job S->>S: pytest --cov S->>S: mv .coverage .coverage.shard-N S->>A: upload .coverage.shard-N rect rgb(255, 247, 200) Note over F: NEW: proper coverage combine A-->>F: download all shards F->>F: coverage combine F->>F: coverage json F->>F: coverage-summary.py end F->>F: aggregate shard resultsTrade-offs
coverage combineover JSON merge.coverage combineis the only correct way to aggregate parallel coverage data — it handles source-file deduplication and branch-level merging internally.pip install coverage[toml]in fan-in, not fulluv sync. The fan-in job only runs data processing (combine + json + summary), not tests. Installing justcoveragekeeps the step fast (~5s vs ~60s).continue-on-error). Trades coverage visibility for CI stability — if coverage collection breaks, the required check still passes. Acceptable for Phase 1 observability; will be revisited when gates are introduced in Phase 2+.Benefits
coverage combine.Validation
Lint (ruff check + format)
Coverage summary script — end-to-end test
Missing-file edge case
How to test
Build & Test (Linux)job summary — it should show a "Unit Test Coverage" markdown table with overall % and a collapsible lowest-coverage files section.Integration Tests (Linux)fan-in job summary — it should show an "Integration Test Coverage" table with correctly merged shard data.fail_undergate exists — coverage should never cause a red check.Closes #1399
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com