Gunnar

nordisk/nornir

.nornir/coverage-maven.md

<!-- nornir:class: history -->
# Feature N — source coverage, maven-style (design + guide)

> LLVM **source-based line/region coverage** (via `cargo-llvm-cov`) → the warehouse → the docs/PDF
> + the viz 🧪 Test pane. The maven/JaCoCo-report twin of AUT5: where
> [aut5-surface-coverage-gate.md](../../dwarves/.nornir/aut5-surface-coverage-gate.md) answers *"is every testable
> **surface** reached by a test?"*, THIS answers *"what fraction of the **source lines/regions** did
> the test run actually execute?"* — per crate, per file, per function, with a dedicated
> **ui/gRPC/emitter boundary** view. Built 2026-06-18. Plan: [nordisk_plan.md](nordisk_plan.md) (the
> AUT5 follow-up "viz Test-pane coverage VIEW").

## 1. The pipeline

```
 cargo llvm-cov --json   →  parse + demangle  →  CoverageReport  →  warehouse rows  →  docs / viz
   (src/coverage/runner.rs)   (src/coverage/mod.rs)  (overall+crate+file+fn)  (coverage, coverage_fn)
                                      │                                            │
                          name/path boundary tag                       nornir:gen:coverage  +  📐 Test-pane panel
                          (ui · grpc · emitter · core)                 (book/PDF)              (+ boundary view)
```

* **Measure** — `nornir coverage <repo>` shells out to `cargo llvm-cov --json` in the repo dir
  (honours `CARGO_TARGET_DIR`), then the **pure** parser ([`nornir::coverage::parse_llvm_cov_json`])
  reads the LLVM export: per-file `summary.{lines,regions}` for the authoritative totals, and the
  per-function `regions`/`count` for the boundary rows. llvm-cov emits **mangled** symbols, so the
  parser demangles (v0 `_R…` / legacy `_ZN…`) via `rustc-demangle` before classifying. Files under
  `/.cargo/`, `/registry/`, `/rustc/`, `/target/`, or outside the repo root are dropped (first-party
  only).
* **Persist** — historized like `bench_telemetry`: one Iceberg snapshot per run, all rows sharing
  `(run_id, ts_micros)`, partitioned by `repo` — so coverage is **time-travellable** and feeds docs
  + viz. Two tables (see §3).
* **Surface** — the docs `nornir:gen:coverage` marker (this file's §6) renders a maven-style table
  into the book/PDF; the viz 🧪 Test pane's 📐 panel reads the same rows live.

## 2. The CLI verb (parity LAW)

```
nornir coverage [REPO] [--show] [--boundary] [--json] [-- <extra cargo-llvm-cov args>]
```

* `nornir coverage nornir` — measure + persist + print the maven table (overall %, per-crate,
  worst files).
* `--boundary` — also print the **ui / gRPC / emitter** boundary view (task #4): a per-boundary
  green/red tally + every boundary function with a `✓`/`✗` exercised mark.
* `--show` — skip the run; READ the latest persisted run from the warehouse (the viz/docs source of
  truth).
* `--json` — emit the report as JSON.
* `-- --all-features` — anything after `--` is passed verbatim to `cargo llvm-cov`.

If `cargo-llvm-cov` / `llvm-tools-preview` is missing, the verb prints the exact install hint
(`rustup component add llvm-tools-preview` + `cargo install cargo-llvm-cov`).

## 3. The warehouse schema

`coverage` — one row per `(run_id, repo, scope)`, `scope ∈ {overall, crate:<name>, file:<path>}`:

| col | type | meaning |
|---|---|---|
| run_id, ts_micros, repo | str/ts/str | run id + time + repo (partition key) |
| scope | str | `overall` \| `crate:<name>` \| `file:<path>` |
| krate, file | str | crate name / file path (`""` where N/A) |
| lines, lines_covered, regions, regions_covered | long | the counts → `line_pct` / `region_pct` |

`coverage_fn` — one row per function, carrying its **layer boundary**:

| col | type | meaning |
|---|---|---|
| run_id, ts_micros, repo | … | as above |
| name, file | str | demangled fn name + source file |
| boundary | str | `ui` \| `grpc` \| `emitter` \| `core` |
| lines, lines_covered, regions, regions_covered | long | per-fn coverage; `regions_covered>0` ⇒ exercised |

Both schema-evolve additively (`ensure_table_schema` on append); old binaries keep reading.

## 4. Boundary classification — the ui/grpc/emitter view (task #4)

Per the **layer-boundary-discoverability LAW**, a function's boundary is classified by **name +
file path** ([`nornir::coverage::classify`]), so the hard-to-cover seams are discoverable and the
green panel *proves* they're tested:

* **UI** — `ui` / `draw` / `draw_*` / `show` under `src/viz/` (the `impl Facet` render fns).
* **gRPC** — the proto server's named handlers (`test_results`, `bench_telemetry`, `architecture`,
  `run_test_matrix`, …) in `src/bin/nornir-server.rs` — a tonic handler verb IS the mark.
* **Emitter** — `state_json` (the readable-data emitter), `emit` / `emit_in|out|end|event`, and the
  `$NORNIR_VIZ_TRACE` trace points (`viz/trace.rs`, `selftest.rs`).

The 📐 panel + `--boundary` then show, e.g., `ui 2/2 · grpc 2/2 · emitter 1/2` and list each
function with `✓`/`✗` — so an un-exercised `state_json` emitter reds the boundary view by name.

## 5. The viz 📐 panel + emitted data (LAW #6)

The Test pane embeds [`CoverageLive`](../src/viz/coverage_live.rs) (mirrors `bench_live.rs`): a
lazy + `request_repaint_after` warehouse read showing the overall badge, per-crate bars, worst
files, a history sparkline, and the boundary view. Everything it paints is exposed as DATA under
`state_json["test"]["src_coverage"]` (`line_pct`, `region_pct`, `crates`, `worst_files`, `history`,
and `boundary.{total,exercised,green,tally,fns}`) + the `test.coverage.{load,build,…}` trace spans,
so a robot test asserts the picture without pixels. Remote (thin) mode shows the same "no
`Viz.Coverage` RPC yet" split as the Bench-LIVE / Test / Release panes.

## 6. Coverage report (generated)

<!-- nornir:gen:start:coverage worst=8 -->
_(no coverage recorded — run `nornir coverage <repo>`)_
<!-- nornir:gen:end:coverage -->

## 7. Tests (inject-real / assert-real)

* `src/coverage/mod.rs` — `parse_real_llvm_cov_export_shape` (a known JSON blob → asserted %, crate
  rollup, worst files, boundary fns + tally), `classify_finds_ui_grpc_emitter_boundaries`,
  `demangle_unwraps_v0_and_passes_plain_through`.
* `src/warehouse/coverage.rs` — `append_query_round_trip_rollup_and_boundary` (report → rows →
  warehouse → rows back; overall/crate/file + the ui/emitter/grpc fn rows), `latest_picks_newest_run`.
* `src/viz/coverage_live.rs` — `inject_renders_overall_crates_and_boundary_into_state_json`,
  `remote_has_no_rpc_split`.
* `src/docs/sections.rs` — `coverage_marker_fills_overall_and_per_crate`,
  `coverage_marker_empty_is_graceful`.
* `tests/viz_coverage_pane.rs` — app-level inject + `state_json` assertions + a **wgpu golden PNG**
  (`tests/snapshots/viz_coverage_pane.png`) of the populated panel incl. the boundary view.

Validated end-to-end against a real `cargo llvm-cov --json` run of `nornir-testmatrix` (78.2%
overall lines, per-crate rollup, demangled fn names).