Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/autonom/.nornir/autonom-consolidation.md

autonom — the single coverage subsystem (consolidation design)

Decided 2026-07-11. Answers: where does the coverage machinery live? Today it is smeared across nornir/src/autonom/, nornir/src/warehouse/*, three copy-pasted sentinel-ban blocks in nornir/tests/, and the already-extracted engine in edda/crates/nornir-testmatrix. This doc makes autonom (a new edda crate) THE single home for coverage — both TEST coverage and WIRING coverage — and lays out a GREEN-GATED, staged move-map: nothing here breaks a build; risky moves are recorded as staged steps, not shipped.

Companion docs: nornir/.nornir/{autonom.md, aut5-surface-coverage-gate.md, completeness-gate.md, marker-matrix-plan.md, self-emitters.md} (the nornir-side history) and dwarves/.nornir/ui-embedding-and-standalone.md (§4 below).


0. Three coverages — name them, keep them apart

The constellation has three orthogonal things that all say "coverage". autonom is the umbrella, but they must not be conflated:

# Sense Question it answers Pure engine (today)
A Test-matrix per-repo × per-aspect pass/fail (+ metric) nornir-testmatrix (model.rs, aspect.rs, runner.rs)
B Wiring / surface is every testable surface reached by a test? Gap = Surface − Covered − Allowlist nornir-testmatrix (discover.rs + coverage.rs)
C Source-line what fraction of source lines did the run execute? (llvm-cov) nornir-coverage

autonom owns the B gate end-to-end and is the façade for A and C's gate-facing surface. C (nornir-coverage) stays a standalone leaf — it is a different question (line %, not surface reachability) and already documents that split (nornir-coverage/src/lib.rs:5-9). autonom depends on it for the "coverage %" metric a surface may carry, but does not absorb it.


1. Where it lives TODAY (survey, file:line)

B — wiring/surface gate (the heart)

Sentinel-ban — DUPLICATED 3×

Identical const SENTINELS: [&str;4] = ["todo","not wired","not yet","coming soon"] in nornir/tests/viz_surface.rs:60 (+ find_sentinel :64, scan_surface_for_sentinels :86), nornir/tests/viz_functional_matrix.rs:759, and inline in nornir/tests/viz_ops_buttons.rs:168. Single-string runtime guard at nornir/src/viz/coverage_live.rs:595. No shared const — genuine copy-paste.

A — test-matrix; C — source-line (for completeness)


2. Crate boundary: autonom depends on nornir-testmatrix (does NOT absorb it)

Decision: autonom is a façade that DEPENDS on nornir-testmatrix; it does NOT (now) absorb discover.rs/coverage.rs. Recommended, and here is why.

The pure B-engine (discover.rs + coverage.rs) is already a clean extracted leaf and is imported directly by consumers as nornir_testmatrix::discover::* / nornir_testmatrix::coverage::*: nornir's warehouse re-export, facett-matrix, and leaf repos (znippy, skade, …) that run the matrix. Physically moving those two modules into autonom would either (a) break every one of those import paths, or (b) force nornir-testmatrix to re-export from autonom — which inverts the dep (testmatrix is the low-level engine; autonom is the higher-level gate that already needs testmatrix's Surface), creating a cycle. Both violate the green-gate.

So autonom re-exports the B-model as its public façade (autonom::{…}). The re-export is the COMPLETE gate-facing surface — the fact types (SymbolRow, CallEdge, FacetRow, BencherRow, DiscoveredAtom), every Surface enumerator (viz_tabs, cli_commands, mcp_tools, grpc_handlers, facett_components, ui_atoms, benchers, unreached_functions, the covered_* sets, test_reachable), the difference engine (Surface, Gap, compute_gap, the workspace_* helpers, SERVED_WORKSPACES), and the verdict/allowlist/report model + SVG twin (Allowlist, GateReport, CoverageRow, Verdict, coverage_svg, …). autonom's own feeder/report/bench consume the engine through this façade (crate::…), never nornir_testmatrix::… directly — so any gap in the re-export is a compile error, not a silently-bypassed boundary. A downstream consumer needs only autonom:: for the whole gate.

autonom also owns the pieces that had no shared home:

Long-term option (STAGED, not now): discover.rs + coverage.rs migrate into autonom, and nornir-testmatrix re-exports them for back-compat, so testmatrix becomes "matrix + runner" and autonom becomes "surface + gate". That is the wide, cross-repo move; it ships only behind a version bump of both crates with the re-export shim proven green. Until then autonom = façade + the two owned modules.

        depends on                depends on
autonom ──────────► nornir-testmatrix   (+ later, optionally, nornir-coverage for the % metric)
  owns: allowlist file I/O, sentinel-ban, gate façade
  re-exports: Surface, Gap, Allowlist, GateReport, CoverageRow, Verdict …

nornir  ──────────► autonom            (thin consumer: warehouse/knowledge feeder + Iceberg seam)
robotui ──────────► autonom            (knows where surfaces/tests are; drives + reports)
dwarves  ──────────► autonom  (later)   (same gate, its own surface)

3. MOVE MAP — each module → new home → who repoints

Piece (today) New home Repoints Risk / status
allowlist file I/O — nornir/src/autonom/mod.rs:647-680 autonom::allowlist nornir mod.rs delegates (keep same fn signatures/re-export) LOW — DONE (slice 1+2)
sentinel-ban SENTINELS+find_sentinel — nornir/tests/viz_surface.rs:60,64 (×3 copies) autonom::sentinel the 3 test files import autonom::sentinel::* LOW logic / STAGED — test files are read by the parallel wiring-audit; do not edit them mid-audit
Allowlist/AllowEntry/GateReport/Verdict/CoverageRow/compute_gap/Surface* stay in nornir-testmatrix; re-exported by autonom consumers may switch nornir_testmatrix::… → autonom::… opportunistically LOW — DONE (façade)
build_surface_and_covered + SurfaceInputs + canonical VIZ_TABS/MCP_TOOLS/CLI_COMMANDS/BENCH_HOT_PATHS — nornir/src/autonom/mod.rs autonom::feeder (behind a KnowledgeSource trait) nornir impls the trait over Iceberg; robotui impls it over its own registry MED — STAGED: coupled to crate::knowledge/crate::warehouse; needs a trait seam first
megagate.rs (cross-workspace fold) autonom::mega nornir keeps served_specs (its roots); autonom owns the fold MED — STAGED: depends on the feeder move
facett_probe.rs (Elm drive) stays in nornir (or → robotui) — HIGH — STAGED: #[cfg(feature="viz")], pulls 6 facett component crates; belongs with the UI host, not the pure gate
Iceberg seam warehouse/surface_coverage.rs stays in nornir — intentional: warehouse coupling is nornir's
discover.rs + coverage.rs bodily into autonom autonom (testmatrix re-exports) all nornir_testmatrix::{discover,coverage} importers HIGH — STAGED: wide cross-repo; needs paired version bump + shim
nornir-coverage (source-line C) stays a standalone leaf; autonom depends on it for the % metric only — keep separate — different question

4. The test-matrix / coverage UI = a shared facett Elm component

Cross-reference dwarves/.nornir/ui-embedding-and-standalone.md §1 ("no duplicated code, even thin" → every panel is one impl Elm/impl Facet crate, both native hosts embed it, cross-host data rides the thin/fat gRPC split).

The coverage/matrix UI must follow the same rule — one Elm crate embedded by BOTH nornir and dwarves, never reimplemented per host:

facett-coverage is specified, not scheduled here. Building it is a separate slice.


5. Follow-ups / hazards to close (not this slice)


6. Execution log (this session — GREEN-gated)