Running your test suite to compute a coverage number would be a terrible product decision. It's slow, it's expensive, it requires a container that can install arbitrary dependencies, and it breaks the moment you use a non-Python test runner. We don't do that.
Instead, PulseBoard looks for coverage the same way a human reviewer would: in the two places where people actually publish it.
Step 1: the README badge
The fast path. If your README has a coverage badge (Codecov, Coveralls, shields.io, whatever), we parse the image URL and extract the number. The badge is already the source of truth your contributors see, so we don't need a separate pipeline.
Pros: fast, free, no GitHub API cost beyond the README fetch.
Cons: the badge can be stale. The maintainer pushed new tests, CI published a new XML report, but the README image URL is cached. We accept this — our goal is "what your repo presents to the world," not "what coverage was 3 seconds ago."
Step 2: coverage.xml in the repo
If no badge is present or parseable, we check the repo root and a couple of common paths (coverage.xml, reports/coverage.xml) for a Cobertura-format XML report. We parse the <coverage line-rate="..."> attribute and convert to a percentage.
Step 3: honest unknown
If neither exists, the collector returns status: unknown with a note explaining why. We never invent a number. The Pulse Score normalizes over the signals we actually have — a missing coverage reading doesn't secretly count as zero and tank your score.
What we don't do, and why
- We don't run your tests. Not in a sandbox, not anywhere. Your infrastructure is yours.
- We don't clone your repo. Everything goes through the GitHub REST API + raw.githubusercontent.com fetches for specific files. Bandwidth cost is cents per user.
- We don't parse arbitrary formats. LCOV, JaCoCo, clover — if people ask, we'll add them. Today it's badges and Cobertura. Simple and done beats general and broken.
Threshold bands
- ≥ 80 → green (
ok) - 60–79 → yellow (
warn) - < 60 → red (
bad)
These are defensible, round numbers. If your team disagrees, fork the scorer — the logic is a dozen lines in pulseboard.collectors.coverage.
The point isn't that 80 is right. The point is that a number based on what you already publish, refreshed on every webhook, tells you more than a monthly PDF report ever will.