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

nordisk/facett

.nornir/mega-matrix-reasoning.md

facett-demo mega matrix (D5)

The showcase app facett-demo shipped with one golden snapshot PNG (tests/snapshot.rs). That proves one themed layout renders to pixels — it does not prove every component behaves. The mega matrix (tests/mega_matrix.rs) is the functional, multi-dimensional sibling: it crosses every component against the data-state and palette axes and asserts the real rendered data of each cell (LAW 1 — never "didn't panic").

The axes

axis values source of truth
component every facet the demo wires (19 Facets in the deck + the menu CommandPalette) = 20 the demo's LEGEND (coverage-gated)
data state Populated (real representative data injected) · Empty (clean empty render) = 2 DataState::ALL
palette all of facett_core::Theme::ALL (default · sci-fi · nordic-aurora · cyberpunk-neon · amber-crt · deep-space · hugin-noir) = 7 Theme::ALL

Total: 20 × 2 × 7 = 280 cells. Every cell is rendered headless and asserted; the test prints a per-cell pass line and the passed/total tally. (The deck grew from 16 to 19 Facets when syschart, geomap and icebergview were added so the demo is the full robot-drivable showcase — see D5b2 below.)

D5b1 vs D5b2 — the static matrix and the live drive

The mega matrix has two layers, both Facet-coverage-gated against the demo's LEGEND:

What each cell asserts

For each (component, data, palette) cell the matrix injects real values, renders the component headless with the palette pinned active, and asserts:

  1. data signal — the component's readable data (Facet::state_json, or the CommandPalette::state_json for the menu) carries the injected data:

    • Populated → the count signal is > 0 (rows / nodes / series / fields / points / tiles / cells / items / bars / commands).
    • Empty → the signal is 0 with no panic and no garbage (a clean empty shell). Grid/table render an empty-but-well-formed RecordBatch (0 rows, real schema), so "empty" is the empty data state, not a malformed one.

The per-component readable signal asserted:

component readable signal (state_json field)
graph / graph3d / depgraph nodes
pipeline statuses.{idle,running,done,failed} sum
grid rows
table rows + selected[] (a real injected selection)
card card.fields[]
form dirty (populated = edited/dirty, empty = clean) + valid
pop items[] + done
plot / boxplot / violin series / groups
heatmap cells
treemap tiles
candles bars
map points
syschart nodes[]
geomap points (+ hotspots[])
icebergview snapshots[]
menu commands[] + hits[]
  1. palette consumption — switching palette must change the palette the component emits in its readable data, not a private default. Proven two ways (a cell passes if either holds for the active palette):

    • probe — facett_core::theme::theme(ui) is the single universal palette read for every custom-painted facet. It records the palette name it hands out into the facett_core::theme::probe thread-local. The test reset()s the probe, renders one component, then reads painted() — a facet that painted with the active palette returns Some(palette_name); one painting a default returns Some("default") and fails for the six non-default palettes.
    • visuals — for facets that theme purely through egui Visuals (plot / table / pop, and the egui-widget parts of card/form), the active Visuals::override_text_color must equal the palette's text. Different palettes have different text, so this discriminates per palette.

The menu reads the palette from the context's facett_theme data directly (it has no ui to call theme(ui) on), so its consumption is asserted on the ctx palette it painted from.

The readable surface that was added

The components consumed the active palette at paint time but did not emit which palette they painted with — there was no readable witness for the palette axis. The minimal surface added (LAW 6) is the facett_core::theme::probe thread-local: theme(ui) records the palette it returns, and tests read it back. It is the single universal consumption point, so instrumenting it once gives uniform, per-component palette-consumption evidence with no change to any component's own state_json (and no risk to their existing tests). No other readable surface was needed — every component already exposed its data counts.

The coverage gate (like the MCP one)

coverage_gate_every_demo_component_is_in_the_matrix cross-checks the matrix registry (matrix_keys() = the cell builders' keys + "menu") against the demo's LEGEND (legend_keys()):

So adding a component to facett-demo without adding a matrix cell fails the test — the gate forces the matrix to track the demo, exactly like the MCP tool-coverage gate. coverage_gate_catches_a_fake is the RED proof: it injects a fake LEGEND key and asserts the gate flags it. (Verified live by temporarily adding a ("fakecomp", …) LEGEND entry → the gate panics with demo components NOT in the mega matrix (add them): ["fakecomp"].)

Screenshot proof across every palette (LAW 4)

The single golden in snapshot.rs is kept. The mega matrix adds screenshot_proof_populated_layout_across_every_palette: it renders the representative populated deck (every Facet, real data, glow on) to real pixels via egui_kittest's wgpu backend once per palette and writes tests/snapshots/palette/facett_demo_<palette>.png (7 PNGs), asserting each is the requested size and non-trivial (> 5 KB). The gallery is thus visually proven across all themes, not just one.

D5b2 — the robot-UI live-drive row

Where D5b1 renders each component in isolation, D5b2 (tests/robot_demo.rs) proves the whole demo works when a robot drives it. It is the dynamic complement: one matrix line answering "fire up the entire app and click every button — does it respond?".

How it works. It builds the demo headless with egui_kittest's Harness over the same render path the eframe binary uses (DemoState::draw_app, so the robot drives exactly what a human sees — CLI/GUI parity), then runs a Scenario of (Action → Assert) steps. Each step performs a real AccessKit interaction (click a labelled tab / button / selectable, drag, scroll, press a key) and then asserts a real value read out of the app's state_json (inject-assert LAW). A failed action or assertion is a red cell, never a panic. Every step becomes one robot-ui TestResultRow (aspect = "robot-ui", one run_id per run), so the live drive fills the matrix; the rows are persisted to a JsonFileSink (facett-demo/target/robot-matrix.jsonl), round-tripped, and printed as a grid via nornir_testmatrix::render_matrix.

Every component is driven (a D5b2 coverage gate asserts each LEGEND key appears in some driven step):

component what the robot drives asserted response (state_json)
graph / graph3d / depgraph / pipeline click the tab header deck.active switched; the facet's nodes present
grid / table click tab + scroll the virtualized view …/rows == 48
card / form click tab card.fields mention Name; form Drew
pop / plot click tab pop.items mention feed; plot.series mention north
boxplot / violin / heatmap / treemap / candles click tab tab switched; heatmap.cells == 16
map click tab map.points == 48
syschart click tab + node-select toolbar (node:Ingest, node:Score) syschart.selected becomes / toggles off the picked node
geomap click tab + drag-pan + scroll-zoom the canvas viewport responds, points == 48 held
icebergview click tab + pager (next ▶ / ◀ prev) + time-travel the snapshot picker page advances/returns; snapshot current 200 → 100 reloads visible_rows
menu toolbar commands opens it; type-free Escape closes it menu.open toggles; menu.hits surface palette.cycle

The reference run drives 48 robot-ui steps, all green. A sibling fidelity test (robot_runner_records_a_wrong_expectation_as_a_red_cell, mirroring robotui's own reference tests) injects a deliberately-wrong expectation and a missing-widget click and asserts both land as red cells, not panics — so D5b2's pass/fail is genuinely assertion-driven.

On nornir-robotui — the port is a TWIN and its justification expired. This row is the documented nornir-robotui consumer but does not depend on the crate: robot_demo.rs carries an in-crate port of robotui's public API (Observable / RobotSession / Scenario / Step / Action / Assert / run_scenario).

⚠ Corrected 2026-09-01. The reason recorded here — "published robotui 0.1.0 pins egui 0.33 … while facett is egui 0.34, two egui majors cannot share one Harness" — is stale in both halves. Measured: nornir-robotui is 0.3.4 on egui/eframe 0.35; facett is egui 0.35. There is no egui-major split left. The stated re-enable condition ("once robotui ships an egui-0.34 build") can never be met, because robotui went from 0.33 past 0.34 to 0.35 without ever stopping there — a drop-condition that can never fire is the same thing as no drop-condition.

The twin cost a real bug, and it had to be fixed twice. Step::assert was a SETTER (self.assert = assert) rather than an append in both copies, so a chained .assert(a).assert(b) silently kept only the last clause and every earlier one was dead code reporting green. Across 30 nordisk repos that was 135 assertion clauses that had never executed. nornir-robotui fixed it at the source in 0.3.4 (Assert::All(Vec<Assert>); .assert() appends) and korp got the fix by bumping its lock — but a version bump cannot reach a twin, so this file's copy stayed broken and had to be repaired separately on 2026-09-01.

facett's one affected chain is the falkor step: /deck/facets/falkor/node_count == 7 had never run, because /deck/facets/falkor/runner came after it. It now runs, and it passes (verified red-first: injecting a false leading clause makes the step fail with clause 1 of 3; before the fix the same injection was silently discarded and the suite stayed green).

TODO (LAW 5 — reuse, do not twin): delete mod robot and depend on nornir-robotui = "0.3". Five other facett crates (facett-app, -graph, -graphnav, -holger, -brokkr) already take the real dep, so the swap is well-trodden. This bug is the argument: one writer cannot drift from itself.

Headless caveats (recorded honestly).

Run

cargo test -p facett-demo --test mega_matrix          # D5b1: the whole static matrix
cargo test -p facett-demo --test mega_matrix mega_matrix_component -- --nocapture   # per-cell table
cargo test -p facett-demo --test robot_demo -- --nocapture   # D5b2: the live-drive row + grid
cargo test -p facett-demo                             # both + the kept golden snapshot

Runs under facett's native cargo test suite, so a nornir test matrix picks it up automatically (no extra wiring).

graph TD
  A[facett-demo LEGEND: 20 components] -->|coverage gate| B[mega matrix registry]
  B --> C{component × data × palette}
  C --> D[populated: signal > 0]
  C --> E[empty: signal == 0, clean]
  C --> F[palette consumed: probe or visuals]
  B --> G[per-palette wgpu screenshots ×7]
  A -.add a component, forget the matrix.-> R[RED: gate fails]
  A -->|D5b2 robot-UI coverage gate| H[robot_demo: drive the WHOLE app headless]
  H --> I[click every tab + every component control]
  I --> J[assert each state_json responds → robot-ui rows]
  J --> K[fill one matrix line: does the demo actually work?]