Commit Graph

5 Commits

Author SHA1 Message Date
ruv 01f7209f19 feat(dashboard): close ADR-093 P2.4 + P2.6 — light-theme AA + keyboard scene nav
## P2.4 — light-theme contrast
- --ink-3 from #6b7684 (3.7:1 on bg-1) → #54606e (~5.4:1, AA-compliant)
- --ink-4 from #9ba4b0 → #7a8390 (better incidental-text legibility)
- --line/--line-2 firmed (#d8dde3 / #c1c8d1) for clearer panel edges
- Dark-theme palette unchanged

## P2.6 — keyboard arrow-key scene navigation
nv-scene now listens for keydown on window:
- Tab from document body → selects first draggable
- Tab / Shift-Tab cycles through draggables
- Arrow keys nudge selected item by 8 px
- Shift+Arrow nudges by 32 px
- Esc deselects
- Position changes persist via scenePositions signal

ADR-093 §2/§3 updated to mark P2.4 and P2.6 resolved. Iteration N
added to §5 plan. Status header updated to Implemented (21/21 gaps
closed).

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-27 12:35:23 -04:00
ruv 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>
2026-04-27 12:02:35 -04:00
ruv 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>
2026-04-26 22:08:49 -04:00
ruv 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>
2026-04-26 21:36:45 -04:00
ruv 1c922ed4ab feat(dashboard): live Ghost Murmur WASM demo + ADR-093 gap analysis
## 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>
2026-04-26 21:21:27 -04:00