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

nordisk/facett

.nornir/plan-flow-pipelines.md

Plan: facett-pipeline, facett-plandag and facett-brokkr draw through facett-flow

Implementation plan authorized by Andreas Persson, creator of facett-flow, on 2026-10-04. The components USE facett-flow; nothing of theirs moves into it. Every addition to facett-flow is a compile-time feature, off by default, so plain flow stays exactly as it is.

Based on facett main a367f9aa (including PR #2 and the latest Falsett optimizations) and the crates that embed these components (knut, nornir, dwarves, korp, ordning).

Implementation and verification status for this checkout is recorded in flow-pipelines-validation.md. Implemented code, local test evidence and outstanding release/hardware gates are distinguished there; the presence of code does not mark all 29 steps complete.

0. Rules every step follows

Facts that shape the plan

Flow internals

Layered layout exists in 7 copies today

Embedders pin current behaviour

Release-order hazard. knut-pipelines requires facett-pipeline >=0.1.19, <0.3. facett's root [patch.crates-io] facett-pipeline = { path } (facett Cargo.toml, see its comment) only takes effect if that requirement matches 0.3.0. Otherwise the build links two PipelineModel types and knut_facett_matrix breaks.

1. Design rules for the flow features

2. Flow features: what each adds and who needs it

feature adds users (at least 2)
host Public FlowScene (wraps today's scene::Scene); FlowScene::with_key(graph, u64) combines caller identity with a complete graph digest, including positions and ordering; retain the scene between frames to avoid CPU rebuilds as well as GPU uploads; View{scale,offset}, View::from_navigable, Camera::view(rect,bounds); paint(ui, pane: egui::Id, rect, &FlowScene, &View, &PaintOpts) -> Painted; PaintOpts{seconds, moving, clustered, palette: Option<FlowPalette>, cpu: CpuStyle::{Preview, Full{max_edges}}}; gpu::evict_idle(&RenderState, frame) -> usize pipeline, plandag, brokkr, brokkrview. Same shape as facett-graphpan's free draw function against a caller-owned transform (graphpan/src/lib.rs:286-307)
labels Painted.visible_nodes supplies visible geometry independently of labels; Painted.on_screen: Vec<OnScreen{node, rect, px_per_world}> supplies label candidates, bounded by label_budget (default 2 000, the A11Y_BUDGET precedent) and min_px; Painted.truncated; FlowScene::edge_point(i, t) (CPU twin of the WGSL curve()); a label pass in FlowGraph using facett_core::legibility::place_labels pipeline (names, status, rows, a11y, throughput labels at edge midpoints), plandag (metric bodies), brokkr and brokkrview, knut and nornir FlowGraph hosts
boxes attr::Sizes{nodes: Vec<[f32;2]>} drawn as rounded-box SDF without the 14 px clamp; edge ends sit on box sides; bounds include box extents pipeline (160×56), plandag (184×84), knut table cards, nornir step boxes
arrows Arrowhead at the consumer end (+3 vertices per edge, variant pipeline only) pipeline, plandag, brokkr, brokkrview. Reduced-motion users get no packets, so direction must still read
status attr::Status{Ok,Slow,Error} (#[non_exhaustive]); StatusTable{nodes, edges: Vec<Option<Status>>}; StatusPalette::from_theme (Ok→th.point, Slow→look::AMBER, Error→look::RED, None→plain kind colour) with a host override; FlowScene::set_status; Msg::Status pipeline, plandag, brokkrview, brokkr (validation errors), knut, nornir, dwarves
rate attr::Rates{edges: Vec<Option<f32>>} mapped by facett_flowsim::flow::plan_edges (the single existing implementation; new optional dependency with default-features=false). Rate 0 means a dead edge with no packets; None keeps plain behaviour. set_rates; Msg::Rates pipeline (throughput_rows_s), plandag (rows per edge), knut (bifrost rows/s), synthetic-sdp
layered Graph::layered(&mut self, LayeredOpts): positions from facett_core::layered, left to right knut, nornir and dwarves adapters, Falsett demo fixture
compact-state An explicit per-instance compact-state setting makes FlowGraph::state_json report {node_count, edge_count, graph_digest, camera, seconds, running, reduced_motion, clustered, selected, render_counts, lane}; feature unification alone changes no instance; Elm::state() is unchanged; the digest is cached at graph replacement Falsett tab (mega_matrix and robot_demo read it), knut/nornir/dwarves FlowGraph hosts at 100k
hit-test FlowScene::hit(world, tol) -> Option<Hit::{Node,Edge,Group}> (box-aware; edges by distance to the 16-segment CPU curve); hits_in(rect); Painted.hover. Uses flow's existing 64-unit bucket index; interaction is independent of the label budget. A tree-index alternative is deferred to future_improvements.md brokkr (connect, move, delete edge), brokkrview and pipeline/plandag beyond the a11y budget, FlowGraph box-aware selection, knut
lod Retains the existing adaptive 2–16 segment count; adds opt-in packet visibility thresholds without restoring fixed 96-vertex edges FlowGraph at 1M (23.2 ms today), knut/nornir at 100k, 10k pipelines

Which features each user enables

user flow features
pipeline host, labels, boxes, arrows, status, rate
plandag host, labels, boxes, arrows, status
brokkrview host, labels, arrows, status
brokkr host, labels, boxes, arrows, status, hit-test
knut adapter layered, status, rate, labels, compact-state (+ lod)
nornir and dwarves adapters layered, status, labels, compact-state

3. Steps

Each step lists files, API, what is deleted, tests (red first, then green), gates, size and the definition of done. Steps 4–12 and 25–26 are flow pull requests on Codeberg nordisk/facett with Andreas as reviewer (he may author any of them).

Step 1 — Preparation and agreement with Andreas (S)

Depends on: nothing.

Step 2 — facett_core::layered (M)

Depends on: 1.

Files: new facett-core/src/layered.rs (pure Rust, no egui, no serde); facett-core/src/lib.rs gets pub mod layered;.

API:

pub enum Align { Sources, Sinks }                 // as-early-as-possible / as-late-as-possible
pub enum RowOrder { Input, Barycenter { sweeps: u8 } }
pub struct LayeredOpts { pub align: Align, pub rows: RowOrder }
pub struct Layered { pub col: Vec<u32>, pub row: Vec<u32>, pub cols: u32, pub rows_max: u32, pub cyclic: bool }
pub fn ranks(n: usize, edges: &[(u32, u32)], align: Align) -> (Vec<u32>, bool);
pub fn layered(n: usize, edges: &[(u32, u32)], opts: LayeredOpts) -> Layered;
pub fn place(l: &Layered, pitch: [f32; 2], pad: [f32; 2]) -> Vec<[f32; 2]>;
pub fn crossings(l: &Layered, edges: &[(u32, u32)]) -> u64;

Tests (facett-core/tests/layered.rs; red because the module does not exist yet):

Gates: facett/tests/no_module_shadowing.rs, clippy. Add a benchmark row for 100k and 1M on dwarf (CPU).

Step 3 — Move graph, graphview and releaseview onto core (M)

Depends on: 2.

Step 4 — Proof scaffolding for flow features; no feature yet (M)

Depends on: 1.

Step 5 — feature host (M)

Depends on: 4.

Files:

API: as in section 2.

What changes for hosts:

Tests (tests/host.rs):

Proof: R4 triple.

Step 6 — feature labels (M)

Depends on: 5.

Step 7 — feature boxes and the shader-variant machinery (L)

Depends on: 5.

Files:

API: attr::Sizes (validated: length, finite, greater than 0); FlowScene::set_sizes; Msg::Sizes.

Tests:

Proof: R4 triple, plus the row "boxes build, plain scene equals default".

Step 8 — feature arrows (S)

Depends on: 7.

Step 9 — feature status (M)

Depends on: 7.

Step 10 — feature rate (M)

Depends on: 7.

Step 11 — feature layered (S)

Depends on: 2, 4.

Step 12 — feature compact-state (S)

Depends on: 4.

Step 13 — plandag draws through flow (M)

Depends on: 2, 5–9.

Cargo: drop facett-graph; add facett-flow with host, labels, boxes, arrows, status; add wgpu = ["facett-flow/wgpu"].

facett-core change: extract pipeline's private input handling (lib.rs:740-793) into facett_core::nav::gestures(ui, rect, &resp) -> Vec<NavGesture{Pan, Zoom{factor, anchor}, Fit}>. It is used by pipeline, plandag, brokkr and brokkrview; otherwise plandag would copy pipeline's code.

Kept: model.rs unchanged (resolve, bottlenecks, critical path), header controls, detail line, copy, portable_state/load_state, all state_json keys and the a11y labels "{title} · {status}" with heat as value.

State and messages:

Mapping to flow:

plandag's own overlay (CPU, using visible geometry independently of the label budget):

Deleted: lib.rs:393-415 (DepGraphView columns, pos_for, fixed canvas), :430-441 (draw_arrow loop), and the facett-graph dependency.

Behaviour changes:

Tests (tests/flow_lane.rs, red first):

Gates: compliance.rs:233-234 (Caps6 unchanged; add a navigable note) and facett/.nornir/compliance.md; facet_completeness (COVERED); mega_matrix plandag cell; robot_demo :681-685; navgraph_demo ceiling 0.

Step 14 — pipeline draws through flow (L)

Depends on: 2, 5–10, 13 (for the shared gestures).

Cargo: drop facett-graph; add flow with host, labels, boxes, arrows, status, rate; add wgpu = ["facett-flow/wgpu"].

Kept unchanged (all public API):

Replaced:

Mapping to flow:

flowsim overlay:

Deleted: paint_dag edge loop :911-927, and paint_flow_edge / edge_spline / EDGE_CURVATURE / EDGE_SEGMENTS :1097-1194 (about 110 lines).

Tests:

Gates: compliance.rs:120-127 and compliance.md:46; mega_matrix pipeline and synthetic-sdp cells; robot_demo :637-647; navgraph_demo.

Step 15 — brokkrview's DAG through flow (S/M)

Depends on: 5, 6, 8, 9, 13.

This is read-only, so it does not need the editor work.

Step 16 — demo, facet-wrapped and wasm wiring (M)

Depends on: 13–15.

facett-demo:

facet-wrapped:

Also:

Step 17 — verification on loki (M)

Depends on: 16.

Build on dwarf through the container road and never compile on loki. Use flock ~/runs/loki.gpu.lock, keep the GPU under 85 °C, and do not set VK_LOADER_DRIVERS_SELECT.

Step 18 — docs, cards and credit (S, rolling)

Step 19 — knut: widen the facett-pipeline requirement (S)

Must happen before step 20.

Step 20 — release 0.3.0 (M; the owner runs every publish)

Depends on: 17–19.

Step 21 — downstream bumps (M)

Depends on: 20.

Step 22 — knut adapter (M)

Depends on: 9–12 (develop against a git rev; release after 20).

Files: knut-bifrost/src/flow_adapter.rs, behind a new feature facett-flow = ["dep:facett-flow"]. This follows the precedent of knut-pipelines' facett-adapter feature. Flow features used: layered, status, rate, labels, compact-state.

API:

pub struct DeliveryRoute { pub source: String, pub sink: String, pub table: String }
pub struct FlowParts { pub graph: facett_flow::Graph, pub status: StatusTable, pub rates: Rates }
pub fn delivery_graph(routes: &[DeliveryRoute], outcomes: &BTreeMap<String, RoundOutcome>, report: Option<&RoundReport>) -> FlowParts;
pub fn snapshot_chain(table: &str, snaps: &[SnapshotInfo], cursor: Option<i64>) -> FlowParts;

Mapping:

Tests: real RoundOutcome and RoundReport fixtures without network; counts, statuses and rates; validate() passes; positions increase left to right; 100k routes within budget.

Step 23 — nornir adapter (M), plus removing nornir's layout copy (S)

Depends on: 9, 11, 12.

Step 24 — dwarves adapter (M)

Depends on: 9, 11, 12.

Step 25 — feature hit-test (M, ships in 0.3.1)

Depends on: 5.

Step 26 — feature lod (M, 0.3.1)

Depends on: 7.

Step 27 — brokkr converted to Elm, Graph mode drawn through flow (M)

Depends on: 2, 5–9, 13.

Step 28 — brokkr editor, entirely inside brokkr (L)

Depends on: 25, 27.

Gestures:

Why this lives in brokkr and not in flow: these are edits to brokkr's own needs model; flow only supplies generic hit-testing and screen rectangles.

Tests:

Step 29 — release 0.3.1 and verification (M)

Depends on: 26, 28.

How pull requests with Andreas work

4. Risks

  1. Feature unification. One crate (the demo, korp) turns features on for the whole build. Choosing the plain pipeline per pane plus the "all features, plain scene" benchmark row covers this.

  2. Chrome's WGSL compiler may reject a composition that naga accepts (the water.wgsl precedent). Every composition goes through the Chrome gate.

  3. CPU-path regressions. Most pipeline and plandag tests, korp's frame-difference check, and no-GPU hosts all run on the CPU. CpuStyle::Full handles this.

  4. Visible changes:

    • pipeline: arcs and marching ants become flow cubics with packets
    • plandag: straight arrows become curves, the orientation flips, and it gains a camera
    • brokkr and brokkrview: become theme-driven unless palette overrides are passed

Embedders' screenshot tests may notice.

  1. Hosts that rebuild every frame (synthetic-sdp, korp's live stream, nornir-funnel-embed). Persistent scenes prevent repeated CPU work; complete geometry keys prevent unnecessary GPU uploads without hiding position or ordering changes.

  2. The a11y budget of 2 000 is a new cap; selection and editing remain available independently of the label budget. Prioritize focused and selected nodes and verify keyboard behavior.

  3. Publish order and the knut <0.3 requirement — handled by steps 19 and 20. Same-version drift on crates.io: check published sources, not just version numbers.

  4. Andreas's availability and naming preferences. It is his component.

  5. Loki is often off and has thermal limits; never compile there.

  6. 1M nodes on Linux is unverified. Do not claim it.

  7. wasm and wheel size growth, which also forces a PyPI version bump.

5. Summary

# step repo size depends on
1 Preparation, Andreas agreement, baseline facett S —
2 facett_core::layered facett-core M 1
3 graph, graphview, releaseview onto core facett M 2
4 Flow proof scaffolding facett-flow, build-thing, xtask M 1
5 Feature host facett-flow M 4
6 Feature labels facett-flow M 5
7 Feature boxes + shader variants facett-flow L 5
8 Feature arrows facett-flow S 7
9 Feature status facett-flow M 7
10 Feature rate facett-flow M 7
11 Feature layered facett-flow S 2, 4
12 Feature compact-state facett-flow S 4
13 plandag through flow facett-plandag, core M 2, 5–9
14 pipeline through flow facett-pipeline L 2, 5–10, 13
15 brokkrview DAG through flow facett-brokkrview S/M 5, 6, 8, 9, 13
16 Demo, facet-wrapped, wasm facett M 13–15
17 Loki: GPU, Chrome, goldens, benchmarks facett M 16
18 Docs, cards, credit facett S rolling, 17
19 knut-pipelines <0.4 knut S before 20
20 Release 0.3.0 (owner publishes) facett M 17–19
21 Downstream bumps knut, nornir, dwarves, korp, edda, holger, ordning M 20
22 knut adapter knut M 9–12 (release after 20)
23 nornir adapter + its layout copy nornir M 9, 11, 12
24 dwarves adapter dwarves M 9, 11, 12
25 Feature hit-test facett-flow M 5
26 Feature lod facett-flow M 7
27 brokkr Elm + Graph through flow facett-brokkr M 2, 5–9, 13
28 brokkr editor facett-brokkr L 25, 27
29 Release 0.3.1 + verification facett M 26, 28