# DEMO_GUIDE — walking the two mandatory stories

Last walked 2026-10-17 11:14 +08 on this machine, in the same order as the browser journeys in
`tests/e2e/run.mjs`. Every number below is what a clean seeded store produced here and what the
journeys assert; nothing is quoted from a spec.

Screenshots of these steps live in `docs/screenshots/` (named by journey and step, e.g.
`docs/screenshots/14-b-commercial-zero-shortfall.png`).

## 0. Start from a known baseline

```bash
cd "/Users/aquek/Documents/ChatGPT/SPECTRA Team/spectra-3d-atlas"
pnpm demo            # reset + seed both segment packs, then prints the two story scripts
```

`pnpm demo` brings up SurrealDB (18080), the API (8787) and the UI (5173), then resets the store so
the story numbers start at their published baseline. Open **http://127.0.0.1:5173**.

oMLX is yours and is never started or restarted by this project. If it is up, the top bar reads
`AI READY · <model>`; if not, `AI UNKNOWN` and every non-copilot step still works.

## 1. Launcher (first screen)

Two cards, each with its own object counts and a `Local status` panel showing real SurrealDB / oMLX
reachability and latency:

- **Defense** — "Readiness, sustainment, maritime response."
- **Commercial** — "Supply chain, assets, customers, operational risk."

Click `Open workspace`. The top bar then names the scope — `DEFENSE · Republic Navy readiness and
sustainment` or `COMMERCIAL · Regional distribution control tower` — followed by the badges
`LIVE`, `live cursor #N`, `AI READY · <model>`, `⌘K`, and the buttons `Guided demo`,
`Focus mode`, `Launcher`.

Left rail: **Overview · Atlas · Ontology · Operations · Data · Scenarios · Evidence ·
Architecture · Settings**.

`Guided demo` opens a step card in the corner. Every step that can be navigated has a
`Take me there` button, so you can watch the UI move instead of clicking. Use it for either story.

## 2. Story A — Defense readiness disruption (margin +6 → −6 → +6)

| # | Click | What you should see (measured here) |
|---|---|---|
| 1 | Overview → `Response readiness` | Ready vessels **4** of 7 · Available capacity **48 places** · Active demand **42 places** · Capacity margin **6 places**. Each tile prints its own formula, e.g. `SUM(capacity WHERE status = ready)`. |
| 2 | Atlas → search `def-vessel-03`, pick **MV Sentosa Guard** | Inspector opens: `WAREHOUSE`-style header with object type and status, record `Version v1`, trust class, data quality, and its relationships. Buttons: `Focus`, `Expand 1 hop`, `Pin position`, `Reload`. |
| 3 | Overview → *Mandatory story: response readiness disruption* → `1 · Record maintenance failure` | Ready vessels **4 → 3** · capacity **48 → 36 places** · margin **+6 → −6** · a `Shortfall` tile appears at **6 places** (`margin = 36 - 42`). The action was previewed first, so blocking and advisory preconditions were separated before anything committed. |
| 4 | Same tab, mission list (`Mission support` too) | **Operation CORMORANT — medevac and salvage, East Anchorage** drops to **1/2 vessels**, `Unmet: 10 places · 1 vessels`. The broken vessel is no longer credited as ready anywhere in the panel. |
| 5 | Atlas → `COPILOT` tab → ask *"Why is Operation CORMORANT short of platforms now?"* | Grounded answer, clickable citations that each select an object, then `Show supporting graph (N)` re-scopes the Atlas to exactly those records. Footer reads `<status> · <model> · <ms>`. |
| 6 | Atlas → focus `def-vessel-07` (the reserve), then story panel → `2 · Activate reserve vessel` | Ready vessels back to **4** · capacity **48 places** · margin **+6** · `Unmet: 0`. The event tray at the bottom shows the committed action run with its journal cursor, and `live cursor #N` advances — no page reload. |
| 7 | Evidence → the work order / observation records | `Attached artefacts` and `Derivation chain` (source → import run → observation → record) for the change you just made. |

Acceptance target on screen: **unmet places on the short mission fall to 0**. Journey 13 asserts all
seven of these, plus that the reserve vessel is linked to a live mission in stored relation records.

## 3. Story B — Commercial supplier disruption (20 → 220 → 80 → 0 at SGD 1,380.00)

| # | Click | What you should see (measured here) |
|---|---|---|
| 1 | Overview → `Executive overview` | Affected demand **300 units** (open lines due by the deadline shown on the tile) · On hand and usable **80 units** (96 on hand − 16 reserved) · Planned by deadline **200 units** (1 timely arrival; late ones excluded, never counted) · Shortfall **20 units**, printed as `MAX(0, 300 − (80 + 200))`. |
| 2 | Atlas → focus `com-comp-bx4` (Component BX-4001), then the shipment NB2291 | Edges carry evidence counts; the inspector links each figure to a stored source record. `Overview → Supply control tower` shows the same shipment and its hops. |
| 3 | Story panel → `1 · Delay inbound 48 h` (NB2291 from Borealis Components, port congestion) | Shortfall **20 → 220 units**. The planned-supply tile now reads **"0 timely arrival(s); late ones are excluded, never counted"** and names the excluded shipment. |
| 4 | Story panel → `2 · Preview & commit transfer` (Tanjung → Tuas, 140 units, ETA +24 h) | Shortfall **220 → 80 units**. On-hand stays **80** while planned supply rises to **140** — a transfer in transit is planned supply, never stock. |
| 5 | Story panel → `3 · Expedite 80 units` (+18 h from Alvina Components) | Shortfall **→ 0 units** and Intervention cost **SGD 1,380.00** (SGD 420.00 transfer + SGD 960.00 expedite, held as integer minor units). |
| 6 | Optional: `4 · Ingest arrival` | Writes an arrival through the same reviewed action path. Not needed to close the shortfall — journeys 14 and P stop after step 5, so this one is demonstrated but not asserted. |
| 7 | Overview → `Inventory` | Lot table with real quantities: the Tuas lot shows quantity **96**, reserved **16** — current stock is never blended with plan. |
| 8 | Overview → `Order impact` | Demand, allocation and unmet units per order line, so the aggregate shortfall is never a black box. |

The shortfall tile converges to `Shortfall 0 units` in the open tab on its own; journey 14 polls the
tile rather than reloading, and that is what "live" means here.

## 4. Local AI copilot, grounded (journey 17)

Open the right drawer (`CONTEXT ›`), pick `COPILOT`, ask a question, then:

- the answer cites stored objects by uid and label — **60 uids cited** for
  *"Why are orders at risk?"* on the Commercial pack in this run;
- each citation is a button that selects the object in the graph;
- `Show supporting graph (N)` re-scopes Atlas to the cited subgraph.

Measured on this machine: provider probe **695 ms**; answer latency ranged **~15 s to ~27.4 s**
depending on what oMLX was doing with the 128 GB model resident. The footer always prints the real
elapsed milliseconds, and answer *quality* is not asserted anywhere — read it yourself.

Offline (journey 19): with public internet blocked at the browser, the same panel answered in
**27.4 s citing stored objects**, and **zero non-local requests were attempted**. The built bundle
was also scanned: 4 shipped files, zero external hosts.

## 5. What-if branch and history replay (journey 18)

**Scenarios → `What-if branches`**: enter a name and note, click `Create branch`.

**Branch patches**: pick a target object and a patch type — the picker offers only what that object
kind can change (`shipmentEtaHours`, `vesselStatus`, `lotQuantity`, `transferEtaHours`) — set a
value, click `Add patch`. A wrong combination is refused in plain words:

> Patch type "shipmentEtaHours" changes Shipment values, not InventoryLot; for com-lot-002 use lotQuantity

Then:

- `Compare with live` renders **measure | live | branch** columns side by side.
- The panel states its guarantee out loud: *"No live record was written: entity versions are
  unchanged and the journal did not advance."* Journey 18 verifies it — on hand stayed **80**,
  shortfall unchanged, patched object `version 1 → 1`, journal `#18 → #18`.
- `Open branch in Atlas` shows the patched scope; `Back to live` returns.
- `Ask for proposals` sends the branch to oMLX for candidate patches.

**History**: `Capture baseline`, then `Replay` — the top bar switches to a warning badge
`as of journal #N` and `Exit replay` (or `Back to live`) resumes following the live cursor.

## 6. Ingestion, rejects and provenance (journey 15)

**Data → `Import source`**: fixture `inventory-arrivals.csv`, target object type `InventoryLot`,
registered source, business key column → `Validate rows`.

- Validation splits the fixture: **3 accepted / 3 rejected**, and every reject shows its raw row plus
  the reason, e.g. row #3 `quantity: -5` → *"Quantity must be at least 0"*.
- `Commit valid rows` reports a run uid; InventoryLot count goes **50 → 53**.
- Open the imported record's lineage: **7 hops**, 1 source, 2 runs, 1 observation, 1 evidence
  artefact — `source → import run → observation → this record`, on the Evidence screen.
- Re-import the same fixture: `alreadyPresent: 3`; a second commit reports **created 0 / updated 3**
  and the count stays **53**. Nothing duplicates.

## 7. Keyboard-only path, reduced motion, narrow viewport (journey 20)

- `Tab` reaches the Atlas search field from a cold page (measured: 17 tab stops); suggestions are
  `ArrowUp`/`ArrowDown`/`Enter` navigable and Enter opens the inspector on that object.
- Filter chips (`Type`, `Status`, `Freshness`) toggle with Enter and the scope readout changes.
- An action can be armed and `Preview`ed from the keyboard alone; the preview shows each
  precondition as PASS/BLOCK with its reason.
- Settings → `Renderer` prints measured fps (`fps p50 60 · p05 54` on this run) and the
  reduced-motion checkbox sets `data-reduced-motion` on the document, which the graph honours.
- At 900×800 the Atlas scope bar stays honest: `548 of 548 objects · 1265/1265 relationships drawn`,
  footer `400 of 548 nodes shown` with the `TRUNCATED` badge because the `Max nodes` slider is at 400.

## 8. When a number does not move

- `Recompute` (Overview) re-reads metrics; the `live cursor #N` badge tells you whether this tab is
  still following the journal.
- `Reset scope` (Atlas) clears focus, filters and search. A uid typed in the wrong workspace matches
  nothing, and the empty state says so instead of blaming the filters.
- `pnpm demo` restores the baseline story numbers at any time; `node tools/run-tool.mjs status`
  shows what is running and on which port.
