The multi-dimensional viz matrix test (tab × mode × data)
Rickard's explicit ask: "test all variants — tab × mode × data." This doc is the contract for tests/viz_md_matrix.rs — the axes it crosses, every cell, and which cells are asserted vs. logged known-gaps.
Why it exists — the gap it closes
tests/viz_live_data.rs proves each data-bearing tab renders NON-EMPTY on the embedded (fat) path — a SINGLE axis. The live audit that motivated this file found the hole: the thin / remote path is what a real user runs against the server, and several tabs rendered EMPTY there even though the embedded path was green. A single-axis test can never catch that.
This file crosses three axes so each (tab, mode, data) crossing is its own assertion. The whole point: it FAILS if a tab is empty-with-data in EITHER mode — catching the remote-path emptiness the single-axis test missed.
The axes
| Axis | Values | Count |
| Tab | Timeline · DepGraph · TimeTravel · Test · Release · Knowledge · Funnel · LiveRun · Leaderboard · Warehouse | 10 |
| Mode | fat (Source::Local + seeded Iceberg warehouse + *_for_test injectors) · thin (Source::Remote against an ephemeral in-process nornir-server whose builds/ warehouse is pre-seeded) |
2 |
| Data | present (assert NON-EMPTY rendered rows) · absent (assert a clean empty-placeholder — never a crash / garbage / false-green) |
2 |
10 × 2 × 2 = 40 cells.
How a cell is read — the CORRECT state_json keys
Each cell reads the load-bearing rendered-row count for its tab, with the right key:
| Tab | state_json key read |
"present" means |
| Timeline | timeline.releases |
releases > 0 |
| DepGraph | dep_graph.node_count |
nodes laid out from the lanes |
| TimeTravel | fat: timetravel.graph.node_count (pinned) · thin: timetravel.release_count |
time-pinned graph / cursor populated |
| Test | test.grid.rows (len) |
grid rows rendered |
| Release | release.rows (len) |
op-log rows rendered |
| Knowledge | knowledge.crates |
crate bubbles rendered (client-side local-disk scan) |
| Funnel | funnel.plan_count |
plan DAG rendered |
| LiveRun | live.events |
hydrated event lines rendered |
| Leaderboard | leaderboard.leaderboard (len) |
ranked board rendered |
| Warehouse | scanned release_events row count (the schema table list is always present, so the data axis is rows, fetched via IcebergWarehouse::scan_preview locally / Warehouse.Scan RPC remotely) |
data table has rows |
For the still-un-wired thin Test stub, "clean" additionally requires the Test.Results RPC TODO note in state_json.test.source. The now-wired thin Release / Leaderboard cells use the same clean signal as every other wired tab — a null state_json[tab].error.
Seeding (real data in — LAW 1)
- fat: a temp Iceberg warehouse seeded with
release_lineage+dep_graph(the tablesViz.Timelineis built from) +release_events+test_results+agent_model_runs, driven through the embeddedinject_*_for_testhooks and the warehouse read path. The Knowledge cell lays a tiny real crate per repo on disk and runs the scan. - thin: an ephemeral
nornir-serverserving a monitored workspacematrixwswhose<home>/.nornir/workspaces/matrixws/builds/warehouse is pre-seeded the same way before the server opens (locks) it, so the remoteViz.*/Warehouse.*RPCs return real data — the exact path the GUI drives. A second, un-seeded server provides the data-absent column. The thin Knowledge cell lays a local checkout on the client (Knowledge is a client-side local-disk scan even in remote mode).
The seeded warehouse is always a temp dir — never committed (warehouse-never- committed LAW).
The cell table — asserted vs known-gap
37 cells asserted (20 fat + 17 thin), 3 cells known-gap (logged #[ignore]-with-reason, all thin × present).
Fat column (20 cells — all asserted)
Every tab × {present, absent}. Present asserts NON-EMPTY rendered rows; absent asserts the clean empty placeholder (0 rows, no crash). The driver sets NORNIR_VIZ_NO_DURABLE_ACTIONLOG=1 so N5's durable action-log does not hold the catalog's exclusive redb lock — otherwise the Funnel cell dead-locks opening the same warehouse via the funnel Store.
Thin column (17 asserted + 3 known-gap)
| Tab | thin · present | thin · absent |
| Timeline | ✅ asserted (releases > 0) | ✅ asserted (0) |
| DepGraph | ✅ asserted (nodes > 0) | ✅ asserted (0) |
| TimeTravel | ✅ asserted (release_count > 0) | ✅ asserted (0) |
| Knowledge | ✅ asserted (crates > 0, client-side scan) | ✅ asserted (0) |
| Warehouse | ✅ asserted (release_events rows > 0) | ✅ asserted (0) |
| Release | ✅ asserted (op rows > 0 over Viz.ReleaseEvents) |
✅ asserted (clean empty, null error) |
| Leaderboard | ✅ asserted (board rows > 0 over Viz.BakeoffResults) |
✅ asserted (clean empty, null error) |
| Test | 🟠 known-gap | ✅ asserted (clean RPC-TODO stub) |
| LiveRun | 🟠 known-gap | ✅ asserted (clean empty pane) |
| Funnel | 🟠 known-gap | ✅ asserted (clean empty) |
Known-gap cells (why, and what flips them to required)
| Tab (thin · present) | Reason | Flips to required when |
| Test | no Viz.TestResults READ RPC — TestTabState::remote is still a stub (Test.Results RPC TODO note); only the SubmitTestResults WRITE RPC exists, the read path is unwired |
a Viz.TestResults read RPC lands → drop #[ignore] on md_matrix_thin_test_present_known_gap |
| LiveRun | remote pane's durable source is the Release.Progress STREAM only; a static seeded warehouse doesn't surface without an active run (no remote warehouse-hydrate RPC) |
a remote warehouse-hydrate RPC lands → drop #[ignore] on md_matrix_thin_liverun_present_known_gap |
| Funnel | Funnel.Show IS wired, but seeding the server's home-derived funnel store over the wire is out of this harness's scope (present-data proven in fat mode) |
the harness gains server funnel-store seeding (or a thin funnel-seed RPC) |
Flipped to asserted (2026-06-13): Release (Viz.ReleaseEvents) and Leaderboard (Viz.BakeoffResults) remote reads are now wired — their thin × present cells assert real rows over the wire and their *_known_gap tests are un-ignored (now plain regressions on the wired RPC).
Each remaining known-gap cell has a dedicated #[ignore]-with-reason #[test] that asserts the INTENDED contract (real rows over the wire) — so flipping it on is a one-line change, and it never asserts false-green in the meantime.
The tests
md_matrix_fat_all_tabs_present_and_absent— the 20-cell fat column.md_matrix_thin_wired_tabs— the 17-cell thin column (two ephemeral servers: one seeded, one empty). This is the test that catches the live remote-path emptiness.md_matrix_shape_and_known_gaps— guards the cell table itself (40 cells; exactly 3 known-gaps — Test/Funnel/LiveRun — allthin × present, each carrying a reason; Release + Leaderboard are NOT gaps).md_matrix_thin_{release,leaderboard}_present_known_gap— now-WIRED, un-ignored, assert real rows over the wire.md_matrix_thin_{test,liverun}_present_known_gap— the remaining#[ignore]-with-reason known-gap cells (flip on when the read lands).
Run
cargo build --features viz --bin nornir-ui
cargo test --features viz,server --test viz_md_matrix -- --nocapture
Runs under nornir test as plain #[test]s (LAW). The server feature is required for the thin column's ephemeral nornir-server.
The workspace axis — tests/viz_workspace_matrix.rs (2026-06-13)
viz_md_matrix.rs crosses tab × mode × data for ONE workspace. The sweep adds the missing workspace axis (Rickard's law: every workspace × every function must retrieve its OWN functional data, and a workspace switch must re-scope every function with no stale leak — the Security re-scope bug that shipped in 0.4.15).
One ephemeral nornir-server monitors THREE workspaces (alpha/beta/gamma), each builds/ warehouse pre-seeded with a DISTINCT count (2/3/4 release_events rows) and a DISTINCT repo set. Two tests:
every_workspace_every_function_retrieves_its_own_data— a thin app scoped to each workspace reads its OWN counts overViz.ReleaseEvents/Viz.BakeoffResults/Viz.Timeline/Warehouse.Scan, and the Security tab lists that workspace's repos. Asserts the EXACT distinct count, so a leak fails.switching_workspace_rescopes_every_function_no_leak— one app drives the picker alpha→beta→gamma→alpha→gamma→beta viaswitch_workspace_for_testand asserts every function follows the current workspace, never the previous.
Heavy is default-ON in the release gate
run_cargo_test now runs --include-ignored (heavy: container/corpus/matrix tests) by default. Opt out with nornir --skip-heavy … (or NORNIR_SKIP_HEAVY=1). nornir's own release runs the feature-gated viz matrix via NORNIR_TEST_FEATURES=viz,server (appended as --features before --).