The 🏛 Architecture viz tab — the EPIC ARCH board, native in egui
Design + guide for the Architecture tab in
nornir viz(nornir-ui): it draws the EPIC ARCH wiring diagram (UI-component ↔ gRPC ↔ core-fn ↔ warehouse-table) natively in egui, inside the running viz.Companion to
.nornir/architecture-wiring.md(the EPIC ARCH model +src/arch/). Laws honoured: #1 inject-assert tests · #2 this doc · #3 facett theme · #4 egui-native wgpu screenshot proof · #6 "see what the user sees" asstate_jsondata +$NORNIR_VIZ_TRACE· CLI/viz parity (the same graphnornir arch svg/ thearch_graphMCP tool emit is now viewable in the app).
TL;DR
- The EPIC ARCH wiring graph already existed three ways: the static circuit-board SVG
nornir arch svgemits (docs/CLI), thearch_graphMCP tool's JSON, and thearchitecture_wiringIceberg table. It could not be viewed in the running viz — that gap is what this tab closes. - New
Tab::Architecture(🏛) + panesrc/viz/arch_tab.rs. It reads the persistedArchGraphand paints it with egui shapes (the DepGraph / CallGraph rendering approach) — nodes coloured byNodeKindvia the facett palette, edges showing the wiring, navigable (pan/zoom, click a node → highlight its downstream wiring, the visual twin ofnornir arch trace). No usvg/resvg — egui-native per LAW #4 (we do NOT load/rasterize the SVG). - Empty state: a clean "run
nornir arch generate" placeholder, never a hard error / TODO sentinel.
How it reads the graph
architecture_wiring is historized per repo (one graph per repo per git sha; see src/arch/warehouse.rs). A workspace's board is the union of its members' latest boards. The merge lives in:
// src/arch/warehouse.rs
pub fn latest_arch_for_repo(wh, repo) -> Result<Option<ArchGraph>>;
pub fn merged_arch_from_warehouse(wh, members)
-> (ArchGraph, /*members_with_data*/ Vec<String>, /*errors*/ Vec<String>);
Nodes dedup by id, edges dedup by (from, to, kind), both deterministically sorted (matching build_graph's ordering → byte-stable). A member with no board contributes nothing (the others still render); a per-member load error is reported, never blanking the board (per-member isolation, like the Knowledge warehouse read).
Source split (mirrors every other pane):
| mode | path |
| local / fat | open the warehouse directly + merged_arch_from_warehouse |
| thin / remote | Viz.Architecture RPC → server runs the same merge (it holds the lock) |
The Viz.Architecture RPC (proto/nornir.proto, handler in src/bin/nornir-server.rs, client viz::remote::fetch_architecture) returns JSON { graph: ArchGraph, members_with_data: [..], errors: [..] } — decoded into ArchPayload. The merge is pure, so thin == fat.
The native render
src/viz/arch_tab.rs lays the merged graph out in layered columns (the same model as arch::layers_of: layer 0 = sources, tables pinned to the rightmost "warehouse rail"), then paints with egui::Painter:
- Chips — one rounded rect per node, coloured by
NodeKindon the active facett palette viakind_color(theme, kind): UI-component → palette accent, gRPC → amber, core-fn → neutral node-fill, table → palettepoint(the rail). - Edges — PCB-style cubic beziers with an arrowhead at the callee;
callson the palette edge colour,reads/writeson amber (the warehouse access traces). - Navigation — drag to pan, scroll to zoom, click a node → BFS its downstream-reachable set and light only that path (un-traced chips/edges dim); click empty / "✖ clear" resets. This is
nornir arch tracelifted to the board.
The pane follows the broadcast_palette / set_palette contract: the app pushes the active palette into arch.set_palette(p) on construction + every switch, so a 🎨 palette change re-skins the board with everything else.
LAW #6 — readable data
state_json["architecture"] (folded in app.rs) exposes exactly what's painted:
{
"source": "local|remote", "workspace": "...",
"node_count": 6, "edge_count": 4,
"components": 2, "grpc": 1, "core_fns": 1, "tables": 2,
"nodes": [{ "id", "label", "kind" }, ...],
"edges": [{ "from", "to", "kind" }, ...],
"members_with_data": ["nornir"], "members": [...], "errors": [],
"load_error": null,
"selected": "ArchTabState", "traced_downstream": ["ArchTabState", ...],
"empty": false, "palette": "default"
}
$NORNIR_VIZ_TRACE carries architecture.load IN (source + workspace) and architecture.render END (counts per kind + selection), edge-triggered on data/selection change (not per frame).
Tests
src/viz/arch_tab.rs#[cfg(test)]— inject a merged board, assertstate_jsoncounts per kind; click a node → assert only its downstream lights; assert the layered layout pins tables right; assert a palette switch reaches the pane.src/arch/warehouse.rs#[cfg(test)]—latest_arch_for_reponewest/None;merged_arch_from_warehouseunions members + dedups shared nodes + isolates a member with no board.tests/viz_arch_pane.rs— LAW #1 inject-assert through the app surface + LAW #4 wgpu snapshot proof (tests/snapshots/viz_architecture.png+viz_architecture_trace.png), plus a click-trace proof.- Surface/drift gates updated:
autonom::VIZ_TABS,Tab::ALL(18),tests/viz_matrix.rs/viz_buttons.rs/viz_surface.rs/robot_viz.rsEXPECTED_TABS / tab-button lists.
Build
cargo build --features viz,mcp,server (green). The Viz.Architecture proto RPC regenerates via build.rs. Re-render snapshots with cargo test --features viz --test viz_arch_pane.