Skip to Content
doctor_frankentuiCoverage gate

Coverage gate

The coverage gate enforces minimum line / branch / function coverage percentages for the doctor_frankentui crate. It reads a threshold table from thresholds.toml, runs cargo llvm-cov, and fails with a human-readable report when any metric falls below its floor.

Source: scripts/doctor_frankentui_coverage.sh + crates/doctor_frankentui/coverage/thresholds.toml + crates/doctor_frankentui/coverage/README.md.

Running the gate

./scripts/doctor_frankentui_coverage.sh # or, with a custom output directory ./scripts/doctor_frankentui_coverage.sh /tmp/doctor_frankentui_coverage_gate

Default output directory: target/doctor_frankentui_coverage/.

Under it you get:

  • coverage_summary.json — raw cargo llvm-cov --branch --summary-only --json.
  • coverage_gate_report.json — threshold evaluation.
  • coverage_gate_report.txt — human-readable report.

Exit code is non-zero iff any threshold fails.

Prerequisites

  • cargo — Rust toolchain.
  • cargo-llvm-cov — install with cargo install cargo-llvm-cov.
  • python3 — 3.11+ ships tomllib in the stdlib. On 3.9/3.10, install tomli (pip install tomli); the script detects this and errors with a clear message otherwise.

The threshold table

crates/doctor_frankentui/coverage/thresholds.toml has three kinds of entries.

Totals

Crate-wide floors for lines, branches, functions.

[total] lines = 89.0 branches = 69.0 functions = 86.0

Groups

Logical groupings of files. Two canonical groups:

  • [group.orchestration] — command-heavy modules with high branch complexity (capture.rs, doctor.rs, report.rs, seed.rs, suite.rs). Floors are looser because orchestration has more unreachable error paths.
  • [group.foundations] — parsers, formats, helpers (cli.rs, error.rs, keyseq.rs, main.rs, profile.rs, runmeta.rs, tape.rs, util.rs). Expected to stay highly covered.
[group.orchestration] files = [ "crates/doctor_frankentui/src/capture.rs", "crates/doctor_frankentui/src/doctor.rs", "crates/doctor_frankentui/src/report.rs", "crates/doctor_frankentui/src/seed.rs", "crates/doctor_frankentui/src/suite.rs", ] lines = 88.0 branches = 66.0 functions = 81.0

Per-module floors

Named [group.module_<name>]. These exist so that a group average can’t mask a single module’s regression. Example:

[group.module_capture] files = ["crates/doctor_frankentui/src/capture.rs"] lines = 78.0 branches = 69.0 functions = 75.0 [group.module_cli] files = ["crates/doctor_frankentui/src/cli.rs"] lines = 99.0 branches = 50.0 functions = 100.0

Every module in the crate has a floor; they are tight for the ones that should stay near 100% and looser for inherently branchy code.

Reading coverage_gate_report.json

{ "schema_version": "coverage-gate-v1", "passed": true, "totals": { "lines": { "actual": 92.1, "threshold": 89.0, "verdict": "pass" }, "branches": { "actual": 72.4, "threshold": 69.0, "verdict": "pass" }, "functions": { "actual": 88.3, "threshold": 86.0, "verdict": "pass" } }, "groups": { "orchestration": { "lines": { "actual": 90.1, "threshold": 88.0, "verdict": "pass" }, "branches": { "actual": 67.4, "threshold": 66.0, "verdict": "pass" }, "functions": { "actual": 82.7, "threshold": 81.0, "verdict": "pass" } }, "module_cli": { "lines": { "actual": 99.1, "threshold": 99.0, "verdict": "pass" }, "branches": { "actual": 52.3, "threshold": 50.0, "verdict": "pass" }, "functions": { "actual": 100.0, "threshold": 100.0, "verdict": "pass" } } } }
  • passed — the overall gate verdict.
  • totals, groups — per-scope breakdowns.
  • verdict is "pass" or "fail" per metric.

On failure the txt report prints the failing groups with per-metric deltas:

[coverage] FAIL group.module_capture: lines 76.2 < 78.0 (delta -1.8) [coverage] FAIL group.module_report: branches 78.9 < 81.0 (delta -2.1)

Typical debug loop

Run the gate locally

./scripts/doctor_frankentui_coverage.sh

Read the .txt report. Identify the failing scope (group or module) and metric (lines / branches / functions).

Generate an HTML report to drill in

cargo llvm-cov -p doctor_frankentui --all-targets --branch --html open target/llvm-cov/html/index.html

Navigate to the failing file. Red-highlighted lines / branches are uncovered.

Add tests or remove dead code

Whichever is appropriate. The no-mock policy  still applies — coverage earned by mocking counts as cheating.

Re-run the gate

./scripts/doctor_frankentui_coverage.sh

Iterate until passed: true.

Tightening a threshold

When you add a test that materially lifts coverage, the right move is often to tighten the floor so a future regression trips the gate.

Establish the new baseline

./scripts/doctor_frankentui_coverage.sh cat target/doctor_frankentui_coverage/coverage_gate_report.json | jq .

Note the actuals for the affected groups.

Bump the threshold table

Edit crates/doctor_frankentui/coverage/thresholds.toml. Round down by ~0.5 percentage points — the actual value has run-to-run variance.

Rerun to confirm the new floor holds

./scripts/doctor_frankentui_coverage.sh

Deliberately regress to confirm the gate trips

Comment out a test, rerun, confirm the gate fails. Revert. This is the “deliberate regression” check from the coverage playbook .

Aligning with the wider coverage matrix

The crate-level gate lives in parallel with the workspace-wide coverage matrix (docs/testing/coverage-matrix.md). The two are independent but coordinated:

  • Workspace-level LCOV gate: per-crate thresholds across the whole repo, enforced against lcov.info.
  • doctor_frankentui gate: tighter thresholds for a single crate because it is on the hot path for CI certification.

See the coverage playbook for the workspace gate.

Pitfalls

Don’t lower thresholds without documentation. If a PR genuinely must reduce a floor (deprecated module, branch moved to a different file), call it out in the PR description. The threshold table is a log of what we consider “covered enough”.

Branch coverage is sensitive to refactoring. Moving an if into a match or vice versa can move branch counts without changing behaviour. If branch coverage drops after a pure refactor, the fix is usually a couple of targeted tests for the new branch shape, not a threshold change.

Python version mismatch is the #1 setup failure. If tomllib/tomli is missing, the script exits early. Install Python 3.11+ or pip install tomli.