Supporting subsystems (this run) — design
Class:
design· Function axis: four independent subsystems that landed alongside the ⚒ brokkr and 🎬 saga headliners, grouped because each supports making the whole system observable, tracked, and gated green:Athe funnel project-flow simulator ·Bthe job-ledger's new op-family coverage ·Cthe robot-UI test matrix (every surface a scenario) ·Dthe warehouse / release / SCIP read + gate surfaces. Strict: this file states only what these are. How-to:supporting-subsystems-guide.md; zero-knowledge:supporting-subsystems-for-idiots.md. The headliner still here:saga-design.md. (The brokkr forge design MOVED todwarves/.nornir/brokkr-design.mdon 2026-07-22 — move=delete: brokkr's code isdwarves::brokkr, so its docs live with it. nornir keeps only the read-only viz Brokkr tab.)
A — funnel: the project-flow time-stepper (simulate_flow)
funnel-core::simulate_flow (topo.rs:147) time-steps the whole project DAG to completion: repeatedly take topo_ready (topo.rs:40) and advance every ready node to Done, recording each stage transition. This is distinct from the LLM task-decomposition decompose::simulate: it advances nodes through statuses over time, which topo_ready alone never does. A DAG whose nodes all start Pending reaches all-Done in N waves, where N is the longest dependency chain.
simulate_flow_with(topo.rs:155) is the policy variant: a caller closure decides each ready node's fate; onlyFailedis honoured as failure (aFailednode stalls its dependents, exactly as a real blocked stage would).simulate_flowissimulate_flow_with(|_| Done).- It returns a
FlowSim(topo.rs:116):waves,done/failed/remaining/total, and aFlowTransition(topo.rs:105) list (wave,plan_id,node_id, targetstatus).remainingcounts nodes that can never become ready (dependents of aFailednode).
Viz surface. The 🗂 Funnel tab's ▶ Simulate flow button (src/viz/funnel_tab.rs) calls run_simulate_flow (funnel_tab.rs:751): it replays the funnel from disk, time-steps every plan's DAG to completion, and stores the FlowSim at state_json["funnel"]["sim"] (funnel_tab.rs:110). Local-mode only (funnel_tab.rs:687). A simulate_flow_for_test seam drives it headless; tests/robot_funnel_journey.rs exercises intake + demo + simulate-flow as one journey.
B — job-ledger coverage: every long op is a tracked job
The jobs ledger (see jobs-design.md) gained op-family kind constants so this run's new long operations self-register as tracked jobs. Each is a one-line pub const in crates/nornir-jobs/src/jobs.rs's kind module — a new op needs no schema change (the kind is a free-form string):
| kind | const | the op it wraps |
saga_capture |
jobs.rs:169 |
the viz ⏺Record/⏹Stop drain → saga_events (one job per Stop) |
monitor_fetch |
jobs.rs:172 |
the nornir monitor poll loop's per-tick workspace fetch sweep |
ci_build |
jobs.rs:177 |
a brokkr pipeline run (the parent; a start_child per step) |
security_scan |
jobs.rs:111 |
the local (fat) SBOM/security scan — tracked at nornir.rs:6094 |
docs_render/_export/_book/_book_svg |
jobs.rs:91–94 |
the docs render/export/book op family |
The lifecycle invariants they inherit are the ledger's, not each op's: a JobHandle writes a running row on start, a terminal done/failed on finish/fail, and the Drop guard (jobs.rs:482) records failed ("dropped without finish/fail") so a panic or early ? never leaves a zombie running row. The saga ⏹ Stop path (test_tab.rs:931) is the canonical example: it opens a SAGA_CAPTURE job, and any early return before finish Drop-fails it.
C — the robot-UI test matrix: every surface is a scenario
The headless robot harness (nornir-robotui + RobotSession) drives the real egui viz through AccessKit and asserts on state_json. This run's charter: every viz surface has a scenario, so a silently-broken pane is a RED matrix row.
The +7 surfaced tabs. TAB_PANE_BLOCK (uiplane_plan.rs:49) is the plane→state_json-block resolver the uiplane walk iterates. Seven tabs were reachable but surfaceless — their panes emitted a top-level state_json block that no surface declaration pointed at, so the walk skipped them (silent holes): Metro, Helix, Bloodstream, WarehouseDeck, Build, Claude, Models (uiplane_plan.rs:73–79). Declaring each surface makes tests/viz_uiplane_walk.rs run a real row for it.
The multi-tab robot journeys (added this run):
| test | what it drives |
tests/robot_full_nav.rs |
clicks through ~25 tab headers, asserts each renders |
tests/robot_funnel_journey.rs |
funnel intake + demo + ▶ Simulate flow as one journey |
tests/robot_misc_tabs.rs |
the miscellaneous / uncovered tabs |
tests/robot_ops_journeys.rs |
arch / ops operator journeys |
tests/robot_arch_metro.rs |
the Architecture / Metro surface |
tests/viz_brokkr_robot.rs |
the ⚒ Brokkr tab — click Run, assert green across all five facets |
The pattern is uniform: build a Harness over the app, RobotSession::run_scenario (or click_label + assert state_json), and every surface's block is a matrix row that is GREEN when it renders and RED when it doesn't — the same inject-assert LAW the saga replay uses.
D — warehouse / release / SCIP: read surfaces + gates
Three write-only or half-wired tables/graphs got their reader + CLI + gate completed so the data is usable, not just recorded.
D1 — warehouse readers for write-only tables.
airgap_events: the airgap-op DAG. Writerappend_airgap_events(airgap_events.rs:89) already existed; this run added the readerquery_airgap_events(airgap_events.rs:141) + the CLInornir airgap events(AirgapOp::Events,src/bin/nornir.rs:4496) so the recorded DAG is readable back.feature_gate_facts: the#[cfg(...)]-gated-item view was WRITE-ONLY (populated insideappend_symbol_scan). This run added the read twinquery_feature_gate_facts(iceberg.rs:2939, async at:2881) + the CLInornir knowledge query <repo> feature-gates(nornir.rs:4937).dep_graph:record_dep_graph(dep_graph.rs:735) now preserves unresolvable edges — ato_repowith no justifyingviacrate is written as a single placeholder row with an emptyvia_crate(dep_graph.rs:756) instead of being dropped, so the edge survives the round-trip and keeps itskindlabel (dep_graph.rs:1324guards it).
D2 — release gate: lower-is-better + ELF cross-check.
release::gate: the no-regression gate (no_regression_against,gate.rs:166) now handles latency-shaped metrics:metric_lower_is_better(gate.rs:227) classifies_ms/p99/latencykeys, and for those a rise is the regression, not a drop (gate.rs:187) — the opposite branch from throughput-shaped_mbs/higher-better metrics.release::doctor:graph_from_binary(doctor.rs:2151) extracts the ELF/binary symbol-dependency graph and projects it into the SAMEgraph_mathspace the source manifests declare; the source-vs-binary cross-check gate (doctor.rs:2186) compares their cycle partition (Tarjan SCC) so a linked-binary cycle the source graph missed is caught. Non-ELF input is skipped, never fatal.
D3 — SCIP resolved find-usages. nornir scip (ScipOp, nornir.rs:1429; feature scip) ingests a rust-analyzer scip index into scip_occurrences (SHA-keyed to HEAD). This run added the resolved find-usages query surface: nornir scip query <repo> usages <symbol> (run_scip, nornir.rs:5000) does IDE-grade resolved find-usages over the latest persisted SCIP map (defs and the resolved-vs-syn delta are the sibling kinds). The knowledge graph full-wiring prefers the RESOLVED SCIP map when present (nornir.rs:4888), falling back to the syn map otherwise. tests/cli_scip_find_usages.rs covers the resolved query; tests/ra_ingest_coverage_wiring.rs is the coverage-wiring tripwire.
Feature flags
| flag | subsystem | effect |
viz |
A (funnel tab), C (robot harness) | compiles the ▶ Simulate flow button + the uiplane surfaces + the robot tests |
robot |
C | the nornir-robotui headless driver |
scip |
D3 | nornir scip ingest + query (SCIP scip_occurrences) |
ra-ingest |
D3 | in-process rust-analyzer (--in-process), no subprocess |
net-scan |
B (security_scan) |
lets the SBOM scan clone missing path-dep siblings; drop it for an airgapped scan |
Subsystems A, B, D1, D2 are always present in the nornir binary — no gate.