mirror of
https://github.com/ruvnet/RuView
synced 2026-07-26 18:01:48 +00:00
53f52bbe746170d967fddb74ad2d6b265941fd75
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
dfe1ce8084 |
feat(dashboard): WsClient transport + ADR-092/093 status updates
## WsClient — full REST + binary WebSocket transport
New `dashboard/src/transport/WsClient.ts` implementing the same
NvsimClient interface as WasmClient. Talks to `nvsim-server`:
- REST control plane: /api/health, /api/scene, /api/config, /api/seed,
/api/run, /api/pause, /api/reset, /api/step, /api/witness/{generate,verify},
/api/export-proof
- Binary WebSocket data plane: /ws/stream — parses 32-frame MagFrame
batches and forwards to the same onFrames subscribers WasmClient uses
- Transport-flip awareness: connection events emit log lines, fps signal
is computed from incoming batches at 1-second granularity
## main.ts — transport-aware boot
- Restores `transport` + `wsUrl` preferences from IndexedDB at startup
- Watches `transport.value` and `wsUrl.value` signals; on change, tears
down the active client and re-boots into the selected mode
- Auto-reverifies witness whenever a fresh transport boot completes —
prevents drift in Settings drawer transport switching
- onFrames closure extracted so wireClient() can subscribe it on every
re-boot without re-allocating runtime state
## ADR-092 status header + §11 acceptance table
Status changed from Proposed to "Implemented (2026-04-27)". §11
acceptance table now an explicit pass/fail matrix:
8 ✅ — UI fidelity, determinism (WASM), throughput, bundle size,
offline PWA, REPL parity, shortcut parity, witness UI
4 ⚠ — formal axe scan, multi-browser, mode-switch byte-equivalence
across deployed nvsim-server, full keyboard-only flow
The 4 ⚠ items require external infrastructure (axe-core CI, FF/Safari
test runs, deployed nvsim-server) or auditor sign-off; none are
blocked by the dashboard codebase.
## ADR-093 §5 iteration plan
Status changed from Proposed to "Mostly Implemented (2026-04-27)".
Iterations A through I (the originally-planned alphabet) plus three
new iterations J/K/L/M (UX usability pass, Home view, WsClient,
App Store runtime) all closed. 19 of 21 P0/P1/P2 items resolved;
remaining 2 are P2.4 (light-theme contrast color-system pass) and
P2.6 (keyboard arrow-key scene nav).
Validated end-to-end on https://ruvnet.github.io/RuView/nvsim/ —
transport-aware boot logs `transport WASM · nvsim@0.3.0 · magic=0xC51A6E70`
followed by `witness verified · determinism gate ✓ · transport=wasm`.
Switching to WS in Settings would now connect to a user-supplied
nvsim-server; the same auto-reverify fires after the flip.
Co-Authored-By: claude-flow <ruv@ruv.net>
|
||
|
|
cedb28db83 |
feat(dashboard): App Store runtime — 6 simulated apps emit real events live
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>
|
||
|
|
18c09d3305 |
feat(dashboard): iter G+H+I + P0.10 — modal forms, a11y pass, drag persistence, REPL history
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>
|
||
|
|
c9fbda12fd |
feat(dashboard): iter B+C+D+E+F — sim controls, scene toolbar, seed modal, transport-pill click, sidebar tunables wire-through, SNR, prefers-reduced-motion auto-detect, REPL proof.export
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>
|
||
|
|
39ec05edcb |
feat(dashboard): nvsim Vite+Lit dashboard with WASM transport + App Store [ADR-092]
End-to-end implementation of the operator dashboard for the nvsim
NV-diamond magnetometer simulator. Vite 5 + TypeScript strict + Lit 3,
~93 KB gzipped JS budget, runs the *real* nvsim Rust crate compiled to
wasm32-unknown-unknown inside a dedicated Web Worker.
Validated end-to-end with `npx agent-browser`:
- WASM module boots, build version + magic 0xC51A_6E70 reported
- Reference witness verifies byte-identical to Proof::EXPECTED_WITNESS_HEX
cc8de9b01b0ff5bd97a6c17848a3f156c174ea7589d0888164a441584ec593b4
- Pipeline runs at ~1.88 kHz on x86_64 dev hardware (4500x over Cortex-A53)
- Zero browser console errors; only Lit dev-mode warning (expected)
## nvsim crate (additive)
- New `wasm` feature flag with wasm-bindgen 0.2 / serde-wasm-bindgen 0.6
- src/wasm.rs: WasmPipeline wrapper + referenceSceneJson +
expectedReferenceWitnessHex + referenceWitness + hexWitness exports
- crate-type = ["cdylib", "rlib"] so native + wasm both build
- rand = { default-features = false } drops getrandom OS-entropy path,
preserving the crate's WASM-ready posture
- Native: 50/50 tests still pass, witness unchanged
## dashboard/ (new package)
- Vite 5 + TypeScript strict, Lit 3 elements, signals-based store
- 12 Lit components mirroring the mockup zones (rail, topbar, sidebar,
scene SVG with draggable sources + NV crystal, inspector tabs
Signal/Frame/Witness, console with REPL + filter tabs, settings
drawer, modals, ⌘K command palette, debug HUD, toast, app-store)
- IndexedDB persistence (theme, density, motion, app activations)
- WasmClient → Web Worker → wasm-pack-built nvsim WASM module
- NvsimClient TS interface — same shape covers future WsClient transport
- MagFrame parser (60-byte LE layout matching nvsim::frame)
## App Store (ADR-092 §14a — added during impl)
- Catalog of all 65 wifi-densepose-wasm-edge modules + nvsim
- 13 categories with event-ID-range labels
- Per-app metadata: id/name/category/crate/summary/events/budget/
status/adr/tags
- Fuzzy search, category + status filters, IndexedDB-backed activation
- ADR-092 §14a documents the registry contract and per-app schema
## Build pipeline
- wasm-pack build crates/nvsim --target web outputs to
dashboard/public/nvsim-pkg/ (60 KB pkg, 162 KB unoptimized .wasm)
- npm run build → 93 KB gzip JS, well under 300 KB budget
- ts.config strict, npx tsc --noEmit clean
- Vite worker correctly loads WASM via dynamic import resolving
against worker origin
## E2E validation
- agent-browser open → 4-zone grid renders correctly in dark theme
- Run button → live B-vector trace, |B| readout updates, FPS counter
- App Store → all 66 apps listed with toggles, fuzzy search filters
to "Ghost hunter" on "ghost" query
- Witness verify → green check, console logs "determinism gate ✓"
- Console errors: zero (only expected Lit dev-mode warning)
Co-Authored-By: claude-flow <ruv@ruv.net>
|