The 10-step welcome tour was first-run-only (persisted in IndexedDB).
After dismissing, users had no clear path back to it.
Fix:
- Topbar gets a '★ Tour' ghost button next to '?' that fires
CustomEvent('nv-show-tour') any time.
- Help-center Quickstart adds a primary 'Take the interactive 10-step
tour' button that closes help and launches the tour.
- nv-help listens for 'nv-show-help-close' to support the help→tour
hand-off cleanly.
Settings drawer already has 'Replay welcome tour' (added earlier);
this just makes the same action one-click from the always-visible
topbar.
Co-Authored-By: claude-flow <ruv@ruv.net>
Closes the "do the App Store toggles actually do anything?" question:
they now do, for the subset of apps whose algorithms map onto nvsim's
magnetic frame stream as a proxy for their native CSI input.
## New: AppManifest.runtime field
Three values:
- `running` — algorithm genuinely runs in browser (just nvsim today)
- `simulated` — pared-down version against nvsim's B-field stream
- `mesh-only` — needs ESP32-S3 + WS transport (deferred to V2)
Visible in the App Store as a colored badge on every card with hover
tooltip explaining what activation actually does.
## New: appRuntimes.ts — 6 in-browser simulated runtimes
- `vital_trend` — peak-detect on B_z oscillation → 1 Hz HR/BR
events 100/101/102/103/104 + bradycardia/tachypnea
- `occupancy` — variance threshold on |B| → 300/302
- `intrusion` — |B| > 1.5× ambient + 0.5 s dwell → 200
- `coherence` — recent vs baseline z-score → 2
- `adversarial` — log-jump anomaly in |B| → 3
- `exo_ghost_hunter`— impulsive/drift/random anomaly classification → 651
Each receives an AppRuntimeContext (frame, |B|, history, elapsed-time,
per-app scratch state) and emits real i32 event IDs matching the
event_types mod in wifi-densepose-wasm-edge.
## Runtime dispatcher in main.ts
On every MagFrameBatch from the worker, iterate over activeAppIds.
For each id with a registered runtime, call the runtime fn with the
context, push any returned events into appEvents + the console feed.
mesh-only apps no-op silently (their toggle still persists for the
WS transport).
## App Store UI
- Per-card runtime badge (running / simulated / mesh-only) with tooltip
- "Live runtime feed" panel above the grid: shows last 12 emitted
events with timestamp, app id, event name + i32 id, detail
- Active simulated-app counter: "5 simulated apps active"
- Per-card event counter "⚡ N ev" once events arrive
- Toggle log line includes runtime mode: "live runtime engaged" /
"queued (needs ESP32 mesh)"
Validated end-to-end on https://ruvnet.github.io/RuView/nvsim/ — toggled
{vital_trend, occupancy, intrusion, coherence}, pressed Run, the feed
filled with real events: COHERENCE_SCORE z=0.87 stable, VITAL_TREND
HR=40 BPM BR=10, BRADYCARDIA, BRADYPNEA. Console log mirrors with
[appId] prefix. Zero browser errors.
Co-Authored-By: claude-flow <ruv@ruv.net>
Addresses user feedback: "make the UI generally easier to use with more
descriptions, help, settings, and guidance."
## New: nv-help — comprehensive help center
Single dialog with 5 tabs:
- 🚀 Quickstart — 7 numbered steps covering Run/B-trace/Verify/Drag/Tunables/Ghost Murmur/App Store
- 📖 Glossary — 14 jargon terms (NV-diamond, CW-ODMR, MagFrame, Witness,
Determinism gate, Lock-in demod, Shot-noise floor, Biot-Savart,
Multistatic fusion, Scene, Tunables, Transport, App Store, Ghost Murmur),
each with category badge (physics/rust/ui) and a search box
- ? FAQ — 7 frequently-asked questions with answers about determinism,
recovered vs predicted |B|, custom scenes, data privacy, witness
mismatch, Inspector vs right-rail, App Store rationale
- ⌨ Shortcuts — full keymap (12 chords)
- ℹ About — what nvsim is, the Apache-2.0/MIT license, the determinism
commitment, GitHub link
Triggers: ? button in topbar, ? key from anywhere, Settings → Help.
## nv-onboarding — expanded from 6 to 10 steps
Each step now has an icon, body, and an optional 💡 hint. Steps walk
through: Welcome → Scene → Run → Inspector → Witness → Tunables →
Ghost Murmur → App Store → Console+REPL → Done. Each step has a
"Step X of 10" label and improved progress dots (active/done/empty).
## Sidebar panel descriptions
Each panel (Scene, NV sensor, Tunables, Pipeline) gets a 1-2 sentence
explainer paragraph. NV sensor panel includes a "What's NV?" link
that jumps to the Glossary section in nv-help. Each Tunables slider
has a `title` tooltip explaining what it controls.
## Settings drawer rewritten with explanations
Every toggle now has a `desc` paragraph explaining what it changes,
when to use it, and any cross-references (ADRs, defaults). Three new
rows added:
- Open help center
- Replay welcome tour
- Reset all preferences (with confirm + IndexedDB wipe + reload)
About row links into nv-help's About section.
## Inspector empty states
Both Signal and Frame tabs now show a friendly empty state when no
frames have arrived: "No frames yet. Press ▶ Run in the topbar (or
hit Space) to start the live B-vector trace." Witness already had
its own empty state.
## A11y additions
- Topbar `?` button has aria-label="Open help"
- Theme button has aria-label="Toggle theme"
- Settings toggles (motion, auto-update) have role="switch" + aria-checked
- Sidebar slider inputs have aria-label
- Help center modal: role=dialog, role=tablist with role=tab buttons
+ aria-selected, role=tabpanel for body
Validated end-to-end against https://ruvnet.github.io/RuView/nvsim/:
- Welcome modal opens on first visit, "Step 1 of 10", 10 dots
- ? button opens help center, 5 nav sections, Quickstart loads first
- Glossary tab shows 14 term entries
- Sidebar panel intros render correctly
- Inspector shows "No frames yet" empty state when idle
Co-Authored-By: claude-flow <ruv@ruv.net>
The Inspector and Witness rail buttons previously only flipped which
tab was selected in the small right-rail inspector — visually
underwhelming. They now also mount an `expanded` instance of the
inspector in the main area, giving the click a real spatial payoff.
Closes ADR-093 P1.13 (view-overlay full-screen panel — was deferred to
V2 but materially improves the rail click affordance).
## nv-inspector
- New `expanded` reflected boolean property; when set, host gets a
radial-gradient backdrop, larger tabs (16/22 px padding), wider body
(max-width 1400 px, centered), 220 px chart height, 48 px frame
strip, and a 2-column grid layout for the Signal/Frame panes.
- New per-tab header (h1 + lead paragraph) only renders in expanded
mode so the small right-rail copy stays compact.
- Expanded Witness pane gets four metadata cards (Reference scene,
Seed, Sample count, Status) plus a "What this verifies" card
explaining the determinism contract verbatim.
- ARIA: tabs are now `role=tablist`, each `role=tab` `aria-selected`,
body is `role=tabpanel`.
## nv-app
- View routing extended: when view ∈ {'inspector','witness'} the main
area renders <nv-inspector expanded .pinTab=…> and the right-rail
compact inspector continues to mirror the same data for context.
Validated end-to-end on https://ruvnet.github.io/RuView/nvsim/ —
agent-browser confirms Inspector click → "Signal inspector — live
B-vector trace + frame stream" h1, Witness click → "Witness panel —
SHA-256 determinism gate" h1 with 7 cards.
Co-Authored-By: claude-flow <ruv@ruv.net>
Closes ADR-093 P0.10, P1.2, P1.6, P1.7, P2.1, P2.2, P2.3, P2.5.
## Iter G — modal contents (P1.6)
- nv-palette "New scene…" now opens a 5-field form (name, dipole
moment, heart→sensor distance, ferrous toggle, 60 Hz mains toggle).
On Apply: builds a real Scene JSON and pushes via client.loadScene().
- nv-palette "Export proof bundle…" now calls client.exportProofBundle()
and triggers a real blob download with a timestamp filename.
## Iter H — a11y pass (P2.1, P2.2, P2.3, P2.5)
- Skip-to-main-content link at top of nv-app (focus-visible only).
- <main id="main-content" role="main"> wraps the central area; tabindex="-1"
so the skip link can land focus there.
- nv-rail wraps its 5 view buttons in <nav role="navigation"
aria-label="Primary"> with aria-current="page" on the active button
and aria-label on every button. SVGs marked aria-hidden="true".
- nv-console body is now role="log" aria-live="polite"
aria-label="Console output".
- nv-modal auto-focuses first interactive element on open and traps
Tab cycling inside the dialog; nv-onboarding already had a dismiss
affordance covered.
## Iter I — drag persistence (P1.7) + density visual (P1.2)
- scenePositions signal in appStore + IndexedDB key 'scene-positions'.
- nv-scene restores drag positions at connect; persists on pointerup.
- Density visual (CSS body.density-{comfy,default,compact}) confirmed
active — was already wired but flagged as "doesn't change anything"
in P1.2; verified during this iter.
## P0.10 — REPL history persistence
- replHistory + pushReplHistory in appStore, persisted to IndexedDB
key 'repl-history'.
- nv-console arrow-up/down now read from the shared signal so command
history survives view switches and reloads.
Validated end-to-end with `npx agent-browser` on
https://ruvnet.github.io/RuView/nvsim/ — skip-link, main role, console
log role, nav role, aria-current="page", New Scene modal with 5 form
fields all confirmed live. Console errors: zero.
ADR-093 §2/§3/§4 updated to mark these items resolved.
Co-Authored-By: claude-flow <ruv@ruv.net>
Closes ADR-093 P0.5, P0.6, P0.7, P0.9, P1.4, P1.8, P1.10, P1.11.
## Iter B — scene toolbar + sim controls (P0.6, P0.7)
- nv-scene scene-toolbar (top-left): zoom +/-, fit-to-view, layer
toggles for sources / field lines / labels. Zoom drives the SVG
viewBox so the entire scene scales uniformly.
- nv-scene sim-controls (bottom-right): step ⏮ / play ▶ / step ⏭ /
speed cycle (0.25× → 4×). Bound to client.run/pause/step.
## Iter C — topbar pill clicks (P0.5, P1.10)
- Seed pill click opens a "Set seed" modal with a hex-validated input.
Apply propagates via WasmClient.setSeed and toasts the new value.
- Transport pill (wasm/ws) click opens the Settings drawer (Transport
section), letting the user switch modes inline.
## Iter D — sidebar tunables wire-through (P1.8)
- Every slider edge-triggers pushConfigDebounced() (300 ms). The
debounced call forwards { digitiser: { f_s_hz, f_mod_hz }, sensor: {
…, shot_noise_disabled }, dt_s } to the worker via setConfig RPC.
Worker rebuilds the WasmPipeline so the running stream picks up the
new config without restart.
## Iter E — proof.export REPL command (P0.9)
- nv-console adds proof.export → calls client.exportProofBundle() and
triggers a download of the resulting JSON manifest with a timestamp
filename. Listed in `help`.
## Iter F — SNR + prefers-reduced-motion (P1.4, P1.11, P1.3)
- nv-scene now computes SNR per frame as |b| / max(sigma_per_axis) and
publishes to the snr signal. The corner stat-card stops showing "—".
- main.ts honors the system prefers-reduced-motion as the default for
motionReduced when no IndexedDB override is set.
ADR-093 §2/§3 updated to mark these P0/P1 items resolved.
Co-Authored-By: claude-flow <ruv@ruv.net>
## ADR-093 — dashboard gap analysis (new)
Deep review of the deployed dashboard against ADR-092 §4.2 inventory,
the original mockup at assets/NVsim Dashboard.zip, and live behavior.
Catalogues 21 gaps in 3 priority tiers:
- P0 (10 items): broken/missing functional surface — including the
rail buttons fixed in 4483a88b2 and the Ghost Murmur view.
- P1 (13 items): visible mockup features missing — sim-controls
overlay, scene toolbar, density/motion polish, modal contents.
- P2 (8 items): a11y + polish.
§5 ships a 9-iteration plan (A-I), one P0/P1 item per iteration, with
each iteration ending in build → deploy → agent-browser validation.
## Iteration A: Functional Ghost Murmur demo (P0.4)
The Ghost Murmur view was a static document. Now it ships a "Try it
yourself" section that drives the *real* nvsim Rust pipeline via WASM
when the user moves either slider:
- New `runTransient` export on nvsim WASM — accepts scene_json +
config_json + seed + n_samples, returns recovered |B|, per-axis
sigma, noise floor, frame count, and a SHA-256 witness.
- Threaded through worker.ts → WasmClient → NvsimClient interface.
- Demo UI: distance slider (10 cm → 100 km log scale), heart-dipole
moment slider (10⁻¹⁰ → 10⁻⁶ A·m²), live readout of predicted
|B| (closed-form 1/r³) vs recovered |B| (full pipeline) vs noise
floor, per-tier detectability bars (NV-ensemble lab, COTS DNV-B1,
SQUID, 60 GHz mmWave, WiFi CSI) with verdict pills, and an overall
press-physics-vs-real verdict.
- Transient witness shown so users can see byte-equivalent
determinism per (scene, config, seed) selection.
Validated end-to-end:
- agent-browser drove the slider and ran the demo on localhost
- predicted=501 fT, recovered=2.07 nT (ADC quant-floor at 10 cm with
COTS sensor, exactly the physics the spec teaches), 64 frames,
witness 1834ff374b839ec8…
- per-tier bars correctly show "NV-DNV-B1 6.0e+2× too weak" at 10 cm
with cardiac-strength dipole — vindicates the spec's central thesis
Live at https://ruvnet.github.io/RuView/nvsim/ → Ghost Murmur tab.
Co-Authored-By: claude-flow <ruv@ruv.net>
Previously the Inspector and Witness rail buttons did nothing useful.
The Ghost Murmur research spec from
docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md had no
in-dashboard surface at all. Both addressed.
## nv-rail
- Inspector button → view='inspector', pins inspector to Signal tab
- Witness button → view='witness', pins inspector to Witness tab
- New Ghost Murmur button (ghost-shaped svg) → view='ghost-murmur'
- All 5 nav buttons + Settings now functional
## nv-app
- View union extended: scene | apps | inspector | witness | ghost-murmur
- Main area swaps between <nv-scene>, <nv-app-store>, <nv-ghost-murmur>
- nv-inspector receives a `pinTab` prop forcing Signal/Witness tab
when the user clicks the corresponding rail button
## nv-ghost-murmur (new view)
- Full research view summarising the publicly-reported April 2026
CIA NV-diamond heartbeat program and RuView's 3-tier mesh equivalent
- Sections: news context, physics reality check, RuView mapping table,
$165 build BoM + honest performance, privacy/ethics/legal, refs
- Links out to the spec doc, public gist, issue #437, Sci Am article
- Content sourced verbatim from the on-disk research spec
## nv-inspector pinTab
- Implements willUpdate() so parent-driven tab pin happens within the
same render pass, fixing a Lit "update after update" warning
Validated end-to-end with `npx agent-browser` against the live
GitHub Pages deploy at https://ruvnet.github.io/RuView/nvsim/ —
all 5 rail buttons work, Ghost Murmur view renders 7 sections /
9 cards / 4 outbound links, witness verification still passes.
Co-Authored-By: claude-flow <ruv@ruv.net>
Worker was resolving /nvsim-pkg/ against self.location.origin, which
under GitHub Pages stripped the /RuView/nvsim/ prefix and 404'd on the
WASM module. Main thread now reads import.meta.env.BASE_URL and forwards
it in the boot RPC; worker resolves against that.
Verified live at https://ruvnet.github.io/RuView/nvsim/ — boot succeeds,
witness verified, determinism gate ✓.
Co-Authored-By: claude-flow <ruv@ruv.net>
Rounds out the dashboard surface introduced in 39ec05edc with all four
remaining ADR-092 deliverables, plus a deploy workflow that publishes
the SPA to gh-pages/nvsim/ without disturbing the existing observatory
or pose-fusion demos.
## nvsim-server (ADR-092 §6.2)
New crate `v2/crates/nvsim-server`. Axum host fronting nvsim::Pipeline:
- REST control plane (15 routes) — /api/health, /api/scene, /api/config,
/api/seed, /api/run, /api/pause, /api/reset, /api/step,
/api/witness/{generate,verify,reference}, /api/export-proof
- Binary WebSocket data plane at /ws/stream — pushes 32-frame
MagFrame batches at ~60 Hz tick rate
- /api/witness/verify always runs the canonical Proof::generate so the
hash matches Proof::EXPECTED_WITNESS_HEX byte-for-byte across WASM
and WS transports — the determinism contract.
- CORS configurable via --allowed-origin, listens on 127.0.0.1:7878 by
default, single-binary deployment.
## Onboarding tour (ADR-092 §10 Pass 6)
`<nv-onboarding>` Lit component, 6-step welcome:
Welcome → Scene canvas → Run → Witness → App Store → Done.
First-run only — persisted via IndexedDB `onboarding-seen` flag.
Re-triggerable via `nv-show-tour` event for the help menu.
## PWA service worker (ADR-092 §9.3)
vite-plugin-pwa wired with workbox-window. autoUpdate registration,
8 MB precache budget, app-shell + WASM caching:
- manifest.webmanifest with /RuView/nvsim/ scope
- icon-192.svg + icon-512.svg in dashboard/public/
- 16 precache entries / 302 KiB
Verified production build under NVSIM_BASE=/RuView/nvsim/:
dist/index.html → /RuView/nvsim/assets/...
dist/manifest.webmanifest → scope: /RuView/nvsim/
dist/sw.js + workbox-*.js generated cleanly
## GitHub Pages deploy workflow
`.github/workflows/dashboard-pages.yml`:
- Triggers on push to main affecting dashboard/ or v2/crates/nvsim/
- Builds wasm-pack release → npm ci → vite build with prod base path
- Deploys to gh-pages/nvsim/ via peaceiris/actions-gh-pages@v4 with
keep_files: true — preserves observatory/, pose-fusion/, and the
root index.html landing page
After first run, the dashboard will be live at:
https://ruvnet.github.io/RuView/nvsim/
Validated end-to-end with `npx agent-browser`:
- Onboarding modal renders on first visit
- Workspace `cargo check --workspace` clean (1 warning in unrelated
sensing-server, no nvsim-server warnings after dead-code prune)
- Production build passes with correct base path resolution and
PWA manifest scope
Co-Authored-By: claude-flow <ruv@ruv.net>