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

nordisk/nornir

.nornir/saga-guide.md

🎬 saga — record → replay → verdict — guide

Class: guide · audience: a techie recording a journey through the viz, persisting it, and replaying it as a regression test. The what is in saga-design.md; the zero-knowledge walkthrough is saga-for-idiots.md.

f0 — the whole workflow

saga records what you actually do in the viz, saves it, and replays it as a robot test so a later change that breaks that journey turns RED.

  1. Build with capture on — --features saga (off by default; release ships clean).
  2. ⏺ Record in the Test tab → navigate/click the viz → ⏹ Stop to persist.
  3. The journey lands in the saga_events warehouse table as a replayable session.
  4. ▶ Replay it → the recorded path runs as a robot Scenario → GREEN rows if the widgets still exist, a RED row the instant one vanished.
  5. nornir saga path <session> — read the journey back as a route funnel.

f1 — build with capture, then record

Capture is behind the off-by-default saga feature. Build the viz with it:

cargo run --features "viz saga" --bin nornir-ui     # or your viz launch

In the running viz:

  1. Open the 🧪 Test tab, expand 🎬 Saga Sessions (record/replay).
  2. Press ⏺ Record — this clears any stale capture and arms the recorder.
  3. Now use the app: click tab headers (🧪 Test, 🗂 Funnel, 🧵 Timeline…), press buttons. Each tab switch records a nav whose component is the header's AccessKit label; wired on-clicks record a click.
  4. Press ⏹ Stop — the buffer drains, writes to saga_events (wrapped in a SAGA_CAPTURE job), and the session list reloads. You'll see stopped — wrote N event(s) to saga_events.

The whole window's journey groups under one session id (stable per window), so a session is exactly "one window's worth of navigating".

f2 — see what you recorded, replay it

The Saga Sessions table lists every recorded session: id, app, event count, replayable-step count, error count, and a 🚩 stuck flag (rage-click clusters or repeated-error routes float to the top so you triage the painful sessions first).

Press ▶ Replay on a replayable row. saga builds that session's ReplayPlan (its nav/click steps in capture order) and turns it into a robot Scenario; the last-replay line shows N step(s) → N matrix row(s). A replayed step that can't find its recorded widget is a RED matrix row — a real regression caught, never a panic.

f3 — replay as a verdict (the e2e proof)

The tests/saga_e2e.rs proof is the pattern to copy for a scripted regression check:

cargo test --features "saga" --test saga_e2e

It: (1) drives real AccessKit header clicks through the viz and drains the recorded events; (2) persists them through the same IcebergSagaSink the ⏹ Stop toggle uses; (3) reads the session back, builds a ReplayPlan, converts it to a Scenario, and runs it against a fresh app — asserting every step is status::PASS; (4) remaps the first step's label to a non-existent widget and asserts it produces a status::FAIL row. That last assert is the inject-assert LAW: replay must go red when a recorded widget disappears.

f4 — the CLI verbs

# Fold a saga file's movement stream and print the final observable model:
nornir saga view path/to/saga.json
nornir saga view --demo --frames        # the built-in synthetic saga, one state_json per movement

# Replay a saga file. Default = print each frame as JSONL (deterministic):
nornir saga replay --demo
nornir saga replay saga.json --web       # serve a self-contained browser page (needs --features saga-web)
nornir saga replay saga.json --drive http://localhost:4444   # dispatch gestures at a real page (mock unless --features saga-drive)

# The nav-path / funnel for a RECORDED session (read saga_events back):
nornir saga path <session-id>
nornir saga path <session-id> --json

nornir saga path prints the ordered route funnel — route → sessions-reached, per-step drop-off, next-route transitions. An unknown session prints a clear "no events" message (an empty funnel is a valid answer, not an error).

Where the same data shows up

Gotchas