autonom — the self-discovering completeness gate (design + guide)
autonom is the autonomous, self-discovering test-matrix framework. It turns the C6
nornir testresults recorder into a PROVABLE completeness gate: green ⟺ every surface is wired and works. Reusable across all nordisk (engine innornir-testmatrix). Forged 2026-06-14 with Rickard. Plan: nordisk_plan.md EPIC AUTONOM (AUT1…AUT8).
1. The problem it kills
A results matrix only records the tests you happen to run, so an unwired surface ships green — e.g. the 🧪 Test tab showed Test.Results RPC TODO in thin mode while every test passed, and Rickard found it by hand. The #1 recurring failure. autonom makes that impossible: the matrix discovers its own surface from the code (never a hand-maintained list, which drifts) and fails the build when any discovered surface has no covering, asserting test.
2. The completeness equation
Surface = every fn/tab/cmd/tool in {viz(thin+fat), CLI, MCP, core fns} ← discovered, not listed
Covered = every surface reached by an inject-assert test ← from call_edges / tools/list / registry
Gap = Surface − Covered − Allowlist
GATE: Gap == ∅ (else the build/release fails, and the gap is shown in viz + CLI)
3. Discovery — no hand lists (the anti-drift core)
Generalize the proven MCP gate (tests/support/mcp_harness.rs already self-discovers 56 tools over tools/list and fails on any uncovered tool — "no silent gaps") to the whole surface:
| surface | enumerator (read via nornir MCP, never grep) |
| facett components | {T : impl Facet for T} ∩ registry() (the discovery contract) |
| viz tabs | the tab enum × {thin, fat} |
| CLI | clap subcommand introspection |
| MCP tools | tools/list (done — the template) |
| functions | symbol_facts − test-reachable-closure(call_edges) |
4. The thin/fat axis (the key the old matrix missed)
Every data surface has two entrypoints — load() (fat/embedded) and fetch_*() (thin/RPC). Both must be covered, and the invariant is thin == fat parity: the same warehouse must produce an identical state_json through either path. That single property catches the whole Test/Bench-tab "RPC TODO" bug class.
5. Per-surface precondition — the FC contract
A test can only drive+observe a surface if it's structured for it. That's the FC-1…FC-9 component contract: serializable state(), AccessKit labels on every interactive/painted widget, injected clock (determinism), explicit pending quiescence. FC is what makes autonom's assertions possible.
6. Assertions
- inject real data + assert
state()/AccessKit — never "didn't panic" (LAW 1). - thin == fat parity (§4).
- sentinel-ban: no rendered surface (
state_json+ AccessKit labels) may containTODO/not wired/not yet/coming soon— any unwired surface outs itself. - 3 layers: property (
proptestoverupdate), interaction (egui_kittestby label/role), perception (headless PNG, LAW 4).
7. Always-visible matrix (AUT1 — shipped 2026-06-14)
The 🧪 Test grid seeds its axes from the expected surface — workspace repos × ASPECT_DEFAULT (Matrix::build_with_expected + ASPECT_DEFAULT, src/viz/test_matrix_grid.rs; wired in test_tab via set_repos, seeded at construct + workspace switch in app.rs). So the matrix is populated with "not-run-yet" cells before any run, not an empty grid. Data-driven (repos from the workspace, aspects from the standard set) — not a hand list.
8. Coverage → warehouse (schema-evolving)
A new surface_coverage table (one row per surface × mode × workspace, same shape as tests/mcp_tool_coverage.json) added via iceberg/skade schema evolution (no history rewrite, old rows stay valid via serde defaults — exactly how aspect/metric were added in L2). Enforced as a release gate (Gap == ∅), with a nornir test coverage CLI + a viz view + an MCP tool (parity).
9. Freshness
The release-blocking gate runs against the working tree (always fresh). The thin-client staleness badge uses a working-tree content digest (gix, in-process) vs the indexed SHA — because a commit SHA cannot detect uncommitted edits. index_status → {HEAD sha, dirty digest, clean|dirty}.
10. Reusable for all nordisk (AUT8)
The engine lives in nornir-testmatrix (std+serde+anyhow — lean). Each repo plugs its enumerators and ships FC-compliant components; nornir hosts the iceberg-backed sink + the symbol-graph/viz/CLI enumerators. Target: nornir + znippy + skade + facett + korp + knut + holger all run autonom; results land in the warehouse.
See the dead-simple walkthrough in autonom-for-idiots.md.