Skip to content

Every executable line, function and region a change adds or touches in the Rust crate must be covered by a test; a change that leaves new code uncovered is refused, and untouched legacy debt is left alone

Coverage across the whole crate sits below 100% and the legacy debt is real, so a single

declared statusdeclared status: guaranteed Declared deterministic and blocking, implemented, and naming the behavioural test that is to prove it. Whether a recorded run supports it is the evidence verdict, shown beside it.

recorded evidencerecorded evidence: not run Declared guaranteed, and not supported by a recorded run: the test is named, and no passing run of the current inputs is recorded. how evidence is recorded

scripts/ci/coverage-differential intersects the one canonical llvm-cov export (test code excluded) with the lines git says the change touched against the branch's merge-base with the trunk, working tree included, and exits 10 on the first changed executable item no test covers. The rule is project.new-code-is-covered; case 132 plants a deliberately uncovered changed line, proves the gate fails, then covers it and proves the gate passes, and proves an unresolvable base is UNKNOWN rather than a silent pass.

What it means

Coverage across the whole crate sits below 100% and the legacy debt is real, so a single crate-wide floor cannot forbid new debt: a change can add uncovered code while the aggregate number barely moves. The differential invariant of issue #214 draws the line where it can be held without rewriting history — at the code a change touches. A new file owes complete coverage, a new function owes it, a changed executable line owes coverage, a changed branch owes coverage; untouched legacy code may stay as it is. The invariant is new_debt == 0 and legacy_debt_after <= legacy_debt_before.

How it works

scripts/rust-coverage --changed <base> reads the one canonical cargo llvm-cov export — test code excluded from both sides of every fraction, the same line/function/region rule the crate gate uses — and intersects it with the lines git diff <base> says the change added or touched, working tree included so an uncommitted edit is held like a committed one. A changed line that maps to executable code with a zero count is a finding; a changed line that maps to nothing executable (a comment, a blank, a use, a type declaration, test code) is not. scripts/ci/coverage-differential is the thin gate: it resolves the base to the branch's merge-base with the trunk (or an explicit one), passes the verdict through, and distinguishes PASS, FAIL (exit 10), UNKNOWN (exit 13, the base cannot be resolved) and BLOCKED (exit 12, the coverage toolchain is absent or its run could not be trusted). The rule is project.new-code-is-covered; the CI plan runs the gate in the coverage job for every change under the crate.

How to see it

scripts/ci/coverage-differential                          # this branch against origin/master's merge-base
scripts/rust-coverage --changed HEAD~1                    # against the previous commit
MJ_COVERAGE_EXPORT=cov.json scripts/ci/coverage-differential   # reuse an export, no reinstrument

test/cases/132_coverage_differential.sh is the executable proof: against a synthetic self-consistent fixture it asserts a deliberately uncovered changed line FAILs (exit 10), the same line covered PASSes, a non-crate change is held to nothing, and an unresolvable base is UNKNOWN (exit 13) rather than a silent pass.

What it does not cover

It holds the dimensions cargo-llvm-cov measures for this crate — line, function and region. It does not mutation-test: for a predicate whose sense matters (an authorization check, a trust decision, a state transition), it proves the changed line ran, not that a test would fail if the line were wrong; those subsystems carry their own adversarial suites and coverage is necessary, not sufficient, for them. It never demands that untouched legacy debt be paid — it looks only at the lines a change touched — and a git hook run with --no-verify, or a base a shallow clone cannot resolve, is UNKNOWN, not a pass it cannot justify.

Why it exists

A crate-wide floor lets a change add uncovered code and stay green, because one new uncovered function barely moves a five-figure denominator; the debt accretes a commit at a time and nobody's change is ever the one that crossed the line. Issue #214 makes the question local — is this change covered — which is the only form of the question a contributor can answer and a gate can hold without a rewrite of the crate's history.

Detail rendered from docs/claims/new-code-is-covered.md.

From the statement to the proof

  1. 1 · the public statement

    Every executable line, function and region a change adds or touches in the Rust crate must be covered by a test; a change that leaves new code uncovered is refused, and untouched legacy debt is left alone

    Made on Coverage.

  2. 2 · the canonical claim

    new-code-is-covereddeclared status: guaranteed in docs/CLAIMS.yaml

    Defined in read it on this site · docs/COVERAGE.md

  3. 3 · the implementation

    scripts/rust-coverage

  4. 4 · the test and the scenarios

    Test: test/cases/132_coverage_differential.sh

  5. 5 · the recorded runs

    recorded evidence: not run how evidence is recorded

Verify it yourself

The test runs in a disposable temporary repository and asserts the behaviour, not a string in the source.

from a clone of the repository
bash test/run.sh 132_coverage_differential

Related claims same implementation

Searches the titles and descriptions of every page this site publishes.