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

nordisk/edda

crates/nornir-testmatrix/.nornir/testmatrix-uiplane.md

uiplane — UI-PLANE reachability (LAW 9) — design

LAW 9 — UI-PLANE REACHABILITY. A surface that exists but cannot be navigated to on a reachable plane of the running app = RED. The matrix tracks the current plane, walks a navigation plan to reach each surface's plane, and asserts the surface is present + RAN on its plane — going RED when a plane is an orphan, a transition is missing, or a reached surface never ran.

The failure that birthed it

The mega-matrix tests components in isolation — it renders each surface standalone and greens "the Sverige showcase component can render." But the user runs a running app on which that surface lives on some plane (a tab / dialog / view / app-mode), reachable only by navigating there. The shipped app could not even navigate to the plane those buttons were on (orphaned plane, or the shipped wasm differed). The native green was true and worthless: shipped ≠ tested (LAW 1), and unreachable ≠ covered (LAW 9).

uiplane models the UI as planes + a navigation plan and walks it on the REAL app, so the matrix asserts the thing the user actually does: reach every surface and prove it ran there.

Model (src/uiplane.rs)

UiSurface { id, label, must_run }              // a button/slider/pane on a plane
UiPlane   { id, label, surfaces: BTreeMap }     // a tagged view-state (tab/dialog/view)
Transition{ from, to, action }                  // a named action that moves plane A→B
UiPlan    { planes, transitions, start }         // the navigation GRAPH (nodes + edges + root)

The driver seam (PlaneDriver)

The seam that lets the SAME UiPlan run against different backends — a native robot-UI (egui_kittest / nornir-robotui) or a deployed-wasm headless browser (LAW 1: drive the SHIPPED artifact). The walk only ever talks to this trait:

pub trait PlaneDriver {
    fn current_plane(&mut self) -> String;                 // from emitted state, never pixels
    fn apply(&mut self, action: &str) -> Result<(), String>; // perform a transition
    fn surfaces_present(&mut self) -> BTreeSet<String>;     // surfaces present on the current plane
    fn surfaces_ran(&mut self) -> BTreeSet<String>;         // surfaces that FIRED their emitter
}

A driver reads PRESENT/RAN from the app's emitted data (state_json / window.__facett_state()), never from pixels (LAW 6). apply only performs the action; the walk (not the driver) verifies the landing plane equals the transition's to — so a transition that exists in the plan but fails to land (the control is missing in the shipped app) is caught as Unreachable.

MockDriver is the in-memory test double (and the reference shape): a transition table + per-plane present/ran sets. It is how the harness's OWN tests prove the four verdicts without standing up egui.

The walk + the LAW 9 verdict (RobotPlan::walk)

RobotPlan::walk(&plan, &mut driver) -> WalkReport:

  1. Compute the BFS route + orphan set + dangling edges.

  2. For each route step: if it has a transition action, apply it and verify current_plane() now equals the target — a mismatch / apply error marks the plane (and its surfaces) unreachable.

  3. On a reached plane, read surfaces_present() + surfaces_ran() and emit one WalkRow per declared surface:

    • present && (ran || !must_run) → ReachableAndRan (GREEN);
    • present but not ran (and must_run) → ReachableNotRan (RED — a button that exists but whose handler never fires; ties RAGNARÖK / LAW 2);
    • absent → ReachableAbsent (RED — on the plane but the surface isn't there; the wasm-parity catch).
  4. Every surface on an unreachable / orphan plane → Unreachable (RED — the Sverige-buttons catch), and the plane is named in unreachable_planes.

Reachability completeness. The walk emits exactly one row per declared surface (report.rows.len() == plan.surface_count()), reachable or not — no surface is silently skipped. WalkReport::is_green() ⟺ no unreachable plane, no dangling edge, and every (plane, surface) row is ReachableAndRan.

Each WalkRow::detail() names what's missing on a RED (the unreachable plane + the need for a transition, the absent surface, or the non-running emitter), so the verdict is actionable.

Composition (does NOT duplicate)

Layering — framework primitive ⟂ nornir's dogfood

  1. FRAMEWORK (crates/nornir-testmatrix/uiplane) — UiPlane / UiPlan / PlaneDriver / RobotPlan::walk, GENERIC + UI-AGNOSTIC. No viz/facett/egui types leak in. Works for a headless library (empty plan = trivially green).
  2. NORNIR's APPLICATION (nornir/src/viz/uiplane_plan.rs) — nornir-viz modelled as a UiPlan (plane per Tab, tab-strip click per transition) + the native VizPlaneDriver over the live UrdrThreadsApp + the wasm driver seam. The worked example that proves the harness. facett-demo's deck (27 tabs + the tab-bar plan) is the second example the facett agent wires later.

The wasm / deployed seam (LAW 1)

src/viz/uiplane_plan.rs::wasm DEFINES (does not implement) the deployed-artifact backend: a BrowserBridge trait over two JS calls the shipped wasm exposes — window.__facett_state() (the state_json) and window.__facett_click(action) (perform a transition) — and a WasmPlaneDriver<B: BrowserBridge> that runs the SAME UiPlan against the LOADED bundle. The present/ran extraction reuses the SAME pane_block_ran logic as native, so native↔wasm verdicts are computed identically — only the transport differs. The facett-demo agent supplies the JS hook + a concrete BrowserBridge (chromedriver / wasm-pack test --headless); the Rust side is ready the moment it lands.