Skip to content

New and changed executable code is covered by a test; legacy debt may only shrink

New and changed executable code is covered by a test; legacy debt may only shrink

majordomus://rule/project.new-code-is-covered@1active coveragetestingdoctrine

Rationale

Repository-wide coverage sits below 100% and the legacy debt is real; a single crate-wide floor cannot forbid new debt, because a change can add uncovered code and the aggregate number barely moves. #214 draws the line where it can be held without rewriting history: the code a change touches. A new file owes 100%, a new function owes 100%, a changed executable line owes coverage, and a changed branch owes coverage. Untouched legacy code may stay uncovered — legacy_debt_after <= legacy_debt_before — but a change may add none.

The measurement is not a second opinion. It is the same cargo llvm-cov export, the same exclusion of test code from both sides of the fraction, and the same line/function/region rule the crate gate uses (scripts/rust-coverage); the differential is that judgment intersected with the lines git says the change touched. There is one coverage authority, and this rule is a narrower question asked of it — not a parallel calculation that could disagree.

Required behaviour

  1. Every executable line, function and region that a change adds or touches under apps/majordomus-cli/src/*.rs, measured against the branch's merge-base with the trunk (working tree included), is covered by a test.
  2. A changed line that maps to no executable code — a comment, a blank, a use, a type declaration, test code — is not held: there is nothing there to run, so a documentation or test-only diff passes with no findings.
  3. The differential is measured over the one canonical export and the one test-exclusion of scripts/rust-coverage --changed; no consumer computes coverage a second way.
  4. The gate distinguishes PASS, FAIL, UNKNOWN (the base cannot be resolved) and BLOCKED (the coverage toolchain is absent or its run could not be trusted). Unknown is not pass; blocked is not pass; a crashed measurement is never green.

Failure behaviour

scripts/ci/coverage-differential exits 10 naming every changed executable line, function or region that no test covers, with its file and line; the coverage-differential gate in .ai/repo/ci/gates.yaml runs it for every change under the crate. It exits 13 when git cannot resolve the base (a shallow clone, no remote) and 12 when cargo-llvm-cov is missing or the instrumented run failed — each a refusal to certify, never a pass. What a static reading cannot see — that the measurement rule is the crate gate's own — is held by the behavioural case named below, which plants a deliberately uncovered changed line and proves the gate fails, then covers it and proves the gate passes.

Verification

scripts/ci/coverage-differential (the gate, exit 10 on an uncovered changed item) and test/cases/132_coverage_differential.sh, which drives scripts/rust-coverage --changed against a fixed llvm-cov export and a fixed diff: it asserts an uncovered changed line FAILs (exit 10), a covered changed line PASSes (exit 0), a documentation-only change is held to nothing, and an unresolvable base is UNKNOWN (exit 13) rather than a silent pass.

Where this comes from

canonical file
.ai/repo/rules/project/new-code-is-covered.v1.md
identity
project.new-code-is-covered@1
also served as
MCP resource majordomus://rule/project.new-code-is-covered@1 · majordomus entity show rule/project-new-code-is-covered-1 · GET /api/v1/entity?uri=majordomus://rule/project.new-code-is-covered@1
in the Cockpit
/cockpit/objects/rule/project-new-code-is-covered-1

This page is generated from the file above by scripts/generate-site-data; editing the page changes nothing, editing the file changes every surface. The relations above are edges of docs/generated/graph.json, which the executable writes from the same index.

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