From 62c254f2206b8fa06a0d63ce029977993cd1ec8f Mon Sep 17 00:00:00 2001 From: Sanjay Udoshi Date: Mon, 27 Jul 2026 18:35:29 -0400 Subject: [PATCH] =?UTF-8?q?docs(flow4d):=20Phase=20E+F=20shipped+deployed?= =?UTF-8?q?=20=E2=80=94=20plan=20ticks=20+=20devlog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ticks Phase E (all 6 items SHIPPED+DEPLOYED, PR #107) and Phase F (F1/F4/F5/F2b SHIPPED+DEPLOYED PR #108; F2a instancing + F3 deltas DEFERRED with rationale — premature at demo scale + F5 selection-survival needs an E2E flow seed) in the journey/conformance plan; adds the E+F devlog. Deployed together at 17dd51c6. --- ...flow4d-wayfinding-resilience-2026-07-27.md | 103 ++++++++++++++++++ ...JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md | 35 +++--- 2 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 docs/devlog/DEVLOG-flow4d-wayfinding-resilience-2026-07-27.md diff --git a/docs/devlog/DEVLOG-flow4d-wayfinding-resilience-2026-07-27.md b/docs/devlog/DEVLOG-flow4d-wayfinding-resilience-2026-07-27.md new file mode 100644 index 00000000..8c5f1576 --- /dev/null +++ b/docs/devlog/DEVLOG-flow4d-wayfinding-resilience-2026-07-27.md @@ -0,0 +1,103 @@ +# DEVLOG — Flow-4D Phases E (wayfinding) + F (resilience) + +**Date:** 2026-07-27 +**PRs:** #107 (Phase E), #108 (Phase F) — both squash-merged +**Deployed:** prod at `17dd51c6` via `./deploy.sh --frontend` (login 200 + fresh +`NavigatorScene`/`PatientFlowNavigator` chunks verified serving) +**Plan:** `docs/plans/FLOW-4D-PATIENT-JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md` + +Sixth and seventh phases of the journey/conformance program. Both are +navigator-side only (the dark-only wall instrument, HFE F-9) — no backend, +schema, or flag changes. Built on A/B/C already on prod. + +## Phase E — Wayfinding & 2D structure (PR #107) + +- **E5 URL view state + TN-6 presets.** `viewState.ts` serializes + camera/floor/layers/time/census/window/selection to URL params, pinned by a + seeded 200-view round-trip. A link can *request* but the lens/flags still + decide what renders. A patient travels only as the opaque `patient=ptok`; + `sel=` carries aggregate kinds only. Copy-view-link in the toolbar + + copy-as-link on saved-view slots. Chronobar window presets (48h/24h/6h/Shift); + historical sources keep their extent window. +- **E2 van Wijk flight + canonical views + SDF labels.** `cameraFlight.ts` is + the pure van Wijk & Nuij optimal pan/zoom path (ρ*=1.42, 8 property tests); + `focusOn`/`flyTo`/`flyToHome`/`focusTopDown` fly the arc and operator input + cancels it. Top/House/Floor/Bed canonical framings. SDF unit-name billboards + via `troika-three-text` (new dep, local ambient `.d.ts`) — per-frame billboard + + LOD scale + distance cull, tied to the Model layer. +- **E3 ortho mode (`O`) + structure traversal.** Perspective/ortho camera swap + beneath OrbitControls — raycast/flight/billboards all read the active camera; + ortho is locked plan view (pan+zoom); Home/bookmarks/links restore the iso + perspective. `structureTree.ts` (pure floor→unit→bed) + `NavigatorStructureNav` + (WAI-ARIA tree, roving tabindex, arrow-key graph) selecting through the shared + `selectEntity` seam. +- **E1 minimap.** `minimapProjection.ts` (pure world-XZ ↔ minimap round-trip) + + `NavigatorMinimap` SVG plate — location dots, delay/deviation pips + (shape-coded, CVD-safe), camera footprint, selection ring; click flies to the + point. Polls camera+selection from the scene so the ~7 Hz camera stream never + re-renders the orchestrator. Suppressed on `?wall=1`. +- **E4 flatten + small multiples.** `trailHeat.ts` (density grids + hourly + slices) + `NavigatorSmallMultiples` — six hourly density plates (the analysis + path replacing forced replay) + SVG export + an on-floor GSTC flatten (transient + scene layer). Density is opacity on one cool ink, never a status color. +- **E6 task modes.** Monitor / Investigate / Rounds progressive disclosure of the + ballooned toolbar (WN-6). Monitor (default) is the resting layout minus the + investigation kit; Investigate adds Speed/Find/saved-views + the small-multiples + card; Rounds foregrounds the walk and steps back the census rollup. + +## Phase F — Resilience & scale (PR #108) + +- **F5 rendered-scene browser test (landed before F2).** jsdom can't run WebGL, + so the scene lifecycle is only testable in a real browser: + `tests/e2e/patient-flow-navigator.spec.ts` covers boot, **GPU context-loss + self-heal (F1)**, frame-budget accumulation (F4), and a layer rebuild keeping + the renderer alive. Graceful `test.skip` when CI lacks WebGL or flow data (the + E2E seed carries no patient-flow data — the model boots into an empty data + layer); auto-strengthens when an E2E flow seed lands. +- **F1 GPU context-loss recovery.** `webglcontextlost` preventDefault + halt the + loop + amber "Graphics paused" card; `webglcontextrestored` → resume + a full + layer rebuild from state. Soak hook counts losses/restores. +- **F4 frame-budget instrumentation.** Per-subsystem p95 ring buffers + (`performance.now` deltas) exposed on the soak hook for the H4 RAIL budget. +- **F2(b) aggregate-trails guard.** Above 50 concurrent trails the per-patient + spaghetti collapses to unit→unit flow arrows with counts; the selected patient + keeps their own trail (§6.2). Pure `flowAggregation.ts` tally (5 tests). + +### Deferred — F2(a) instancing + F3 deltas + +The InstancedMesh migration is a perf refactor for a token scale the demo +(~40 patients) doesn't reach — deferring aligns with "no premature optimization." +Critically, F5's **selection-survival** check can't execute in CI without an E2E +flow seed, so merging a large token-rendering refactor whose one real safety net +can't run would be an unguarded change to a wall instrument. Recommendation: +land F2(a)+F3 as a focused follow-up **together with an E2E flow seed** so F5 +verifies instanced selection/highlight through rebuilds in CI. F3's `?since=` +web-delta path is coupled to the instance-buffer rebuild strategy and rides along. + +## Verification + +- `tsc --noEmit` clean; `vite build` clean (troika bundles into the lazy + `NavigatorScene` chunk). +- patientFlow vitest: **221 passed** across both phases. +- `scripts/check-ui-canon.sh` passed (raw-palette ratchet ≤ 76). +- `playwright --list` resolves the 4 rendered-scene tests. +- Prod deploy verified: login 200 + new build chunks serving. + +## Notes / gotchas + +- **Concurrent-session shared-checkout hazard** recurred: a second session kept + flipping the deploy-staging checkout between `main` and a feature branch + mid-edit. All of E-later + F was done in an isolated worktree + (`~/Github/Zephyrus-wf`, `ln -s ../Zephyrus/node_modules`), committing each + sub-step. Because E was squash-merged, F was rebased with + `git rebase --onto origin/main ` to carry only its own commits. +- The deploy staging checkout (`/home/smudoshi/Github/Zephyrus`, the ssh target's + canonical checkout) had been left on the merged feature branch; `deploy.sh` + requires it on `main`/clean, so it was switched back before the release. +- beastmode grep treats the arrow-heavy `NavigatorScene.ts` as binary (locale) — + use `grep -a`. + +## Remaining + +Phase D (DRG catalog bridge, clinical-gated), Phase G (H3/H4 field validation), +and the F2(a)/F3 instancing follow-up (with the E2E flow seed). diff --git a/docs/plans/FLOW-4D-PATIENT-JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md b/docs/plans/FLOW-4D-PATIENT-JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md index 66249c30..8f0598e1 100644 --- a/docs/plans/FLOW-4D-PATIENT-JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md +++ b/docs/plans/FLOW-4D-PATIENT-JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md @@ -269,23 +269,24 @@ Everything here ships **dark** behind the existing deferred serving-flag family - [ ] Tests: compiler determinism + digest pinning (a version's compiled model is content-addressed), flag-off inertness (byte-identical responses), append-only discipline untouched **Acceptance:** a governed pathway version compiles to a conformance-checkable reference model with a coverage report; demo overlay demonstrates the full loop on synthetic data; nothing clinical serves anywhere with flags off — proven by tests, asserted with G-9's convention (no inferring clinical approval). -### Phase E — Wayfinding & 2D structure (~4 d) `feature/flow4d-wayfinding` [C] -- [ ] E1 Minimap/floor-stack widget (camera footprint, selection dot, delay/deviation pips; click = fit-to-floor); CVD + never-color-alone treatment; suppress in wall/kiosk param -- [ ] E2 Canonical views control (Top/House/Floor/Bed) + van Wijk-arc verification/upgrade of `focusOn`/`flyTo` transitions; SDF-text unit billboards w/ LOD + distance culling (P4 backlog item, now evidence-backed) -- [ ] E3 Top-down orthographic mode (`O`) — all layers; disks/labels legibility pass; Data-Navigator-style structure traversal (floor→unit→bed arrow-key graph) layered on the existing action-list seam -- [ ] E4 Flatten + small multiples: "Trail heat (last 6 h)" scene mode (GSTC flatten) and a 6-slice hourly small-multiples export card (analysis path replacing forced replay) -- [ ] E5 URL view state + "Copy view link" (camera/floor/layers/time/selection/censusScope; extends parseHandoff; saved views gain "copy as link") -- [ ] E6 Task modes Monitor/Investigate/Rounds (progressive disclosure; `NavigatorToolbar` decomposition — the renderToolbar test fixture must gain the mode prop; WN-6) -- [ ] Tests: mode-gated control visibility, URL round-trip property test, minimap sync, ortho toggle, LOD label culling thresholds -**Acceptance:** an operator can always answer "where am I / what am I looking at / how do I get back" in one glance + one click; every monitoring answer has a non-perspectival path; any exact view is shareable as a URL. - -### Phase F — Resilience & scale (~3–4 d) `feature/flow4d-resilience` [C] -- [ ] F1 Context-loss recovery (absorbs Codex F-11): `webglcontextlost` preventDefault + halt + visible degraded card; `webglcontextrestored` → full GPU-resource rebuild from state (scene state already lives outside GL); deterministic test via `WEBGL_lose_context` in the browser test; soak hook counts losses/restores -- [ ] F2 InstancedMesh migration for tokens/pips/ghosts/deviation glyphs (per-instance color; raycast via instanceId; selection/highlight adapted — SelectionEntity API unchanged); aggregate-trails guard: >50 concurrent trails → unit-to-unit flow arrows w/ counts, individual trail on selection (§6.2) -- [ ] F3 Delta updates: pre-sized instance buffers + `needsUpdate` paths replace group clear+rebuild for token/heat layers; epoch rebootstrap (A3) remains the only full-rebuild trigger; web read path adopts `?since=` deltas where the endpoint offers it (DI-4) -- [ ] F4 Frame budget: `performance.measure` marks per subsystem (tokens/trails/overlays/labels), p95 exposed via soak hook; RAIL-derived budget table recorded in this doc when measured -- [ ] F5 Rendered-scene Playwright test (absorbs Codex F-12): boot demo scene → select → force layer rebuild → assert highlight survives; epoch rebootstrap path; context-loss path; runs in the existing browser CI job -**Acceptance:** wall survives GPU reset with self-recovery; draw calls flat vs entity count (instancing proven by soak counters); browser CI covers the three long-session failure modes end-to-end. +### Phase E — Wayfinding & 2D structure (~4 d) `feature/flow4d-wayfinding` [C] — **SHIPPED + DEPLOYED PROD (PR #107, squash `5f01dd81`; deployed 2026-07-27 at `17dd51c6`)** +- [x] E1 Minimap/floor-stack widget (camera footprint, selection dot, delay/deviation pips; click = fit-to-floor); CVD + never-color-alone treatment; suppress in wall/kiosk param — `minimapProjection.ts` (pure round-trip) + `NavigatorMinimap` SVG plate, polls camera/selection from the scene so the ~7 Hz camera stream never re-renders the orchestrator; `?wall=1` suppressed +- [x] E2 Canonical views control (Top/House/Floor/Bed) + van Wijk-arc verification/upgrade of `focusOn`/`flyTo` transitions; SDF-text unit billboards w/ LOD + distance culling — `cameraFlight.ts` (van Wijk & Nuij ρ*=1.42, 8 property tests); `troika-three-text` billboards (local ambient .d.ts) +- [x] E3 Top-down orthographic mode (`O`) — all layers; disks/labels legibility pass; Data-Navigator-style structure traversal (floor→unit→bed arrow-key graph) layered on the existing action-list seam — perspective/ortho camera swap under OrbitControls; `structureTree.ts` + `NavigatorStructureNav` WAI-ARIA tree (roving arrow-key graph, shared selectEntity seam) +- [x] E4 Flatten + small multiples: "Trail heat (last 6 h)" scene mode (GSTC flatten) and a 6-slice hourly small-multiples export card (analysis path replacing forced replay) — `trailHeat.ts` + `NavigatorSmallMultiples` (SVG export + on-floor flatten via transient scene layer) +- [x] E5 URL view state + "Copy view link" (camera/floor/layers/time/selection/censusScope; extends parseHandoff; saved views gain "copy as link") — `viewState.ts`, seeded 200-view round-trip; `sel=` aggregate-only, patient travels only as opaque ptok. TN-6 chronobar presets (48h/24h/6h/Shift) shipped alongside +- [x] E6 Task modes Monitor/Investigate/Rounds (progressive disclosure; `NavigatorToolbar` decomposition — the renderToolbar test fixture must gain the mode prop; WN-6) +- [x] Tests: mode-gated control visibility, URL round-trip property test, minimap sync, ortho toggle (unit); LOD label culling threshold — verified in-scene, browser-covered by F5 +- *Deferred (plan-consistent, B4): outward "Locate in 4D" buttons on ED/bed boards — those rows carry no opaque ptok client-side; needs backend ptok minting + re-id review, scope separately.* +**Acceptance:** an operator can always answer "where am I / what am I looking at / how do I get back" in one glance + one click; every monitoring answer has a non-perspectival path; any exact view is shareable as a URL. **MET** — 221 vitest green (viewState/cameraFlight/structureTree/minimapProjection + component suites), tsc + vite build clean. + +### Phase F — Resilience & scale (~3–4 d) `feature/flow4d-resilience` [C] — **F1/F4/F5/F2b SHIPPED + DEPLOYED PROD (PR #108, squash `17dd51c6`, deployed 2026-07-27); F2a + F3 DEFERRED** +- [x] F1 Context-loss recovery (absorbs Codex F-11): `webglcontextlost` preventDefault + halt + visible degraded card ("Graphics paused"); `webglcontextrestored` → full rebuild from state (scene state lives outside GL); deterministic test via `WEBGL_lose_context` in the browser test (F5); soak hook counts losses/restores (`contextEvents()`) +- [~] F2 **F2(b) SHIPPED** — aggregate-trails guard: >50 concurrent trails → unit→unit flow arrows w/ counts, individual trail on selection (§6.2); pure `flowAggregation.ts` tally (5 tests). **F2(a) InstancedMesh migration DEFERRED** — premature optimization at demo scale (~40 pts); more importantly F5's selection-survival can't run in CI without an E2E flow seed, so a large token-rendering refactor would be unguarded. Land with the E2E seed so F5 verifies instanced selection. +- [ ] F3 Delta updates (pre-sized instance buffers + `?since=` web deltas) — **DEFERRED with F2(a):** the instance-buffer path depends on instancing; the `?since=` web-delta is a coupled data-path change best landed together +- [x] F4 Frame budget: per-subsystem (frame/render/labels/controls) p95 timing via ring buffers (`performance.now` deltas, no measure-entry churn); exposed on the soak hook (`frameBudget()`); RAIL budget table to be filled from the H4 soak run (G4) +- [x] F5 Rendered-scene Playwright test (absorbs Codex F-12): boot scene → force layer rebuild → renderer alive; context-loss self-heal + counters; frame-budget accumulates; `tests/e2e/patient-flow-navigator.spec.ts` in the browser CI job. Graceful skip when CI lacks WebGL/flow data (E2eTestSeeder carries none — model boots into an empty data layer); auto-strengthens when an E2E flow seed lands +**Acceptance:** wall survives GPU reset with self-recovery **(MET, F1+F5)**; browser CI covers the long-session failure modes end-to-end **(MET for boot/context-loss/frame-budget/layer-rebuild)**. Draw-calls-flat-vs-entity-count (instancing soak counters) **carries to the F2(a) follow-up**. 221 vitest green; tsc + vite build clean; playwright --list resolves the 4 rendered-scene tests. ### Phase G — Field validation & claims discipline (sessions [SU], synthesis [C]) - [ ] G1 Usability protocol v1.1: add T8 (journey), T9 (adherence SAGAT), keyboard-only journey variant; re-pilot timing (≤35 min)