Files

212 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 06-turn-by-turn-guidance-live-driving
plan: 08
subsystem: web-capture-harness
tags: [capture-harness, d16-device-gate, drive-replay, mock-telegram, guidance-legs, dsgn02-gallery, no-red-law, console-hygiene, blocking-checkpoint, tdd]
# Dependency graph
requires:
- phase: 06-turn-by-turn-guidance-live-driving
plan: 02
provides: "the four committed D-16 drive fixtures (web/test/fixtures/drives/*.json — the replay ground truth ?drive= names) + the D-04 machine the LIVE pump rides"
- phase: 06-turn-by-turn-guidance-live-driving
plan: 03
provides: "POST/GET/DELETE /api/guidance — the envelope lifecycle the confirm/tripend legs' teardown executes (DELETE on abandon)"
- phase: 06-turn-by-turn-guidance-live-driving
plan: 04
provides: "engine A live (deploy 75b0ff14, R2 graph round-trip verified) — the /api/route shape the capture stub answers"
- phase: 06-turn-by-turn-guidance-live-driving
plan: 06
provides: "the LIVE pump (start gate → cockpit → reroute → teardown) — the surface the captures prove; camera.ts's locked pose"
- phase: 06-turn-by-turn-guidance-live-driving
plan: 07
provides: "the cron turn-cards + the committed glyph PNGs — Part C's street-source ratification item"
- phase: 05-routing-route-display
plan: 05
provides: "the capture-leg pattern (one leg = one context + one screenshot, console-token races, the DSGN-02 composer) — extended, never replaced"
provides:
- "web/src/mock/telegram-mock.ts — the ?drive=<fixture> replay knob: navigator.geolocation replaced by a committed-fixture stream (real-time schedule, lazy clock start at the first watch arm, window.__ARAHARAH_DRIVE pause()/resume() freeze), honest unknown-name fallback"
- "web/scripts/capture.mjs — the seven guidance legs (active/list/rerouting/confirm/tripend/dark + guidance-gallery-compare) with the engine-A-shaped /api/route stub (consult = the drive model with real graph street names; reroutes = a merge model held 5s), the D-15 coordinate-hygiene scan, the computed-color NO-RED scan"
- "web/screenshots/{shell-guidance-active,shell-guidance-list,shell-guidance-rerouting,shell-guidance-confirm,shell-guidance-tripend,shell-guidance-dark,guidance-gallery-compare}.png — the seven committed captures (force-added; the DSGN-02 fidelity artifact rides the blocking checkpoint)"
- "web/src/route/route.ts + web/src/main.ts — TWO Rule-1 device-gate fixes: the D-09 confirm card survives a live fix (G7b); the pump's accepted fixes ride the chain's surfaces — puck + freshness (G7c, updatePuck dep)"
affects: [06-08 task 2 (the ONE REAL DRIVE — BLOCKING, awaiting the human), phase-6 verification close, DSGN-02 gallery review]
# Tech tracking
tech-stack:
added: [] # zero new dependencies — Playwright/routing imports only
patterns:
- "fixture-replay through the REAL watch: the ?drive= knob swaps navigator.geolocation, so the shell's unmodified D-15 throttle + D-04 machine consume the committed stream — captures prove the shell, never bypassing validation (the trust boundary's law)"
- "root-absolute import.meta.glob: Vite 8's importGlob matches NO relative pattern escaping src/ (../test/... compiles empty — verified by the dev-server probe); /test/fixtures/drives/*.json works"
- "engine-A-shaped stub by construction: POST #1 = consult (drive model, street names snapToGraph'd off the LOCAL kl-roadgraph.v1.bin at each post-maneuver segment midpoint); later POSTs = merge models held REROUTE_HOLD_MS — the REAL http-source validation still runs on every answer"
- "computed-color NO-RED law: class lists cannot carry the token, so the rerouting/confirm legs scan getComputedStyle for rgb(237, 63, 28) across the banner/confirm subtrees"
key-files:
created:
- web/screenshots/shell-guidance-active.png
- web/screenshots/shell-guidance-list.png
- web/screenshots/shell-guidance-rerouting.png
- web/screenshots/shell-guidance-confirm.png
- web/screenshots/shell-guidance-tripend.png
- web/screenshots/shell-guidance-dark.png
- web/screenshots/guidance-gallery-compare.png
- .planning/phases/06-turn-by-turn-guidance-live-driving/06-DEVICE-GATE.md
modified:
- web/src/mock/telegram-mock.ts
- web/scripts/capture.mjs
- web/src/route/route.ts
- web/src/main.ts
- web/index.html
- web/test/route-surface.test.mjs
key-decisions:
- "The replay plays REAL-TIME (the recorded schedule), never compressed: the machine's grace/storm clocks read wall time (route.ts's now()), so compressed playback would desync every D-04 timing; the ?drive= clock starts LAZILY at the first watchPosition arm (the route draw) so no fix is ever emitted before a consumer exists."
- "The /api/route stub lives in capture.mjs via Playwright interception (not index.html): it keeps the plan's file set, cannot leak into any shipped artifact (it never exists outside the capture run), and the client-side http-source validation still gates every answer."
- "Street names come from the LOCAL road graph (routing/data/kl-roadgraph.v1.bin) snapped at each post-maneuver segment midpoint — the same OSM-derived source engine A serves, so the film reads real KL streets (Jalan Tun Sambanthan, Jalan Petaling, ...); a graph-absent fork degrades to the honest streetUnnamed fallback."
- "TWO device-gate-blocking bugs were fixed rather than papered over: the confirm card evaporating on a live fix (the D-09 exit guard breaking under exactly the drive's step 6) and the puck/freshness freeze (a navigation UI whose position marker never moves and whose strip falsely flips SIGNAL LOST at 45s). Both pinned by new G-gates; both found by tracing the live-drive path, not by capture failure alone."
patterns-established:
- "the guide wait mode: state-token race + DOM text polls + theme-slot honesty + whole-console coordinate scan — the D-15/T-06-04 hygiene law carried into capture legs"
requirements-completed: [GUID-01, GUID-02, GUID-03, GUID-04, GUID-05, GUID-06, PX-01, PX-03] # automated halves proven on film; the ONE REAL DRIVE (the phase's exit gate) is BLOCKING — see Known Stubs / Checkpoint
# Metrics
duration: 48min
completed: 2026-10-24
---
# Phase 6 Plan 8: The Verification Close — Replay Captures + THE ONE REAL DRIVE Gate Summary
**The shell is proven on film: the ?drive= fixture-replay knob streams the committed corridor/missed-turn drives through the LIVE pump and seven capture legs freeze the cockpit — banner with real OSM street names, collapsed-default list with the NEXT lamp, the calm no-red REROUTING state, the bottom-register abandon confirm, the trip-end deletion card, the dark-theme chassis — beside the mockup in the DSGN-02 guidance gallery — while two real device-gate bugs were caught and fixed before the road: the abandon confirm evaporating on a live fix, and the puck/freshness freeze that would have flipped SIGNAL LOST mid-drive; the ONE REAL DRIVE itself is now the BLOCKING human gate (06-DEVICE-GATE.md).**
## Performance
- **Duration:** 48 min
- **Started:** 2026-10-24T12:10:00Z (approx; fresh dispatch after verified-dead prior passes)
- **Completed (automated half):** 2026-10-24T12:58:00Z approx
- **Tasks:** 1 of 2 (task 1 complete + committed; task 2 = the BLOCKING device gate — awaiting the human)
- **Files:** 14 (7 created, 7 modified) + the checkpoint file
## Accomplishments
- **The ?drive= replay knob** (task 1, step 1): `installMockWebApp` reads `drive` from location.search (harness-only module), validates the name through a slug guard + defensive fixture shape check against the committed `web/test/fixtures/drives/*.json`, and replaces `navigator.geolocation` with a replay source. The REAL pump arms its REAL `createGuidanceWatch` against it — the unmodified D-15 throttle and D-04 machine consume the committed stream; no test-only bypass of validation (the threat model's trust-boundary law). The clock starts lazily at the first watch arm (the route draw) and plays REAL-TIME; `window.__ARAHARAH_DRIVE = { pause(), resume(), name }` freezes/resumes emission mid-stream. An unknown name emits one honest token (`ARAHARAH-DRIVE state=unknown`) and falls back — never a crash, never arbitrary data (T-06-08).
- **The seven guidance legs** (task 1, step 2), exactly per the UI-SPEC capture table:
- `shell-guidance-active.png` — `?mock=android&dest=merdeka-118&fixpair=central-market-to-merdeka-118&drive=corridor-follow`: banner `GUIDE // NEXT` + white `85 M` + beige `JALAN TUN SAMBANTHAN` + orange arrow on the plate, compacted strip (`data-guidance="active"`), the guidance card (`GUIDE` register, Merdeka 118, `4 MIN · 1.9 KM · ETA HH:MM`, progressbar, END TRIP), collapsed-default LIST toggle, matte-black chassis `rgb(26,26,26)`.
- `shell-guidance-list.png` — scripted banner tap → `aria-expanded=true`, `CLOSE` toggle, the NEXT row lamp + real text rows.
- `shell-guidance-rerouting.png` — `drive=missed-turn-excursion` captured INSIDE the REROUTING window (the stub holds the merge answer 5s): `GUIDE // REROUTING`, `—` dash, the calm copy, the blinking gantry, the last glyph held — **zero functional-red computed colors** in the banner or card.
- `shell-guidance-confirm.png` — scripted END TRIP → the bottom-register confirm card (`GUIDE // CONFIRM`, `Abandon route?`, `YES, ABANDON` primary, `CONTINUE`) — no red.
- `shell-guidance-tripend.png` — scripted YES, ABANDON → the center-register trip-end card with the visible deletion clause; banner/list cleared; the full-height strip returned.
- `shell-guidance-dark.png` — `scheme=dark`: the theme-slot honesty check + the chassis Rams-constant.
- `guidance-gallery-compare.png` — the DSGN-02 composite beside `active_route_guidance/screen.png` (the composer generalized with mockup params; legs 8/13 byte-identical behavior).
- **The engine-A-shaped route stub**: the shell's route consult rides `/api/route` through the composed ladder; the guidance legs intercept it with an answer built from committed data — the pair's ODbL geometry + the drive's recorded maneuverIdx/kinds + street names snapped off the LOCAL road graph at each post-maneuver segment midpoint (engine A's deriveSteps semantics). Reroute requests get a merge model from the requesting fix (the replay controller's buildRerouteModel semantics), held 5s so the calm window is photographable. The REAL `validatedResult` still runs on every answer.
- **D-15 console hygiene carried into captures**: every guidance leg scans its WHOLE console capture for coordinate-bearing lines (`assertNoCoordinateLines`) — the legs assert `ARAHARAH-GUIDE state=<state>` tokens and the ABSENCE of coordinates; the replay source itself logs nothing per fix (T-06-04: no new logging).
- **Two device-gate bugs caught and fixed BEFORE the road** (see Deviations):
- The D-09 confirm card evaporated on the next accepted fix (renderCockpit rebuilt the guidance card over the ONE exit guard) — G7b pins the survival.
- The pump's accepted fixes never touched the chain's surfaces: the puck froze at the boot fix while the camera rode the snapped line (the UI-SPEC's recorded call: "the puck … carr[ies] progress"), and `lastFix` stayed at boot so the 45s staleness law would falsely flip SIGNAL LOST mid-drive and re-run the chain — one injected `updatePuck` dep composes `lastFix = fix` + `marker.update` (the RAW fix; the camera rides the SNAPPED foot) + `setStale(false)`. G7c pins the call + main.ts's composition.
- **Suite state:** web **148 passed / 0 failed** (146 baseline untouched-green + G7b + G7c), bot 125/125, routing 65/65; all typechecks clean; `npm run web:capture` **20/20** (all 13 prior legs unbroken); zero new dependencies.
## Task Commits
1. **task 1: the replay knob + the seven legs + the gallery** (with the two Rule-1 fixes committed separately first)
- `fca2a90` fix(06-08): the D-09 confirm card survives a live fix — renderCockpit guards the card rebuild (G7b RED→GREEN, atomic test+fix)
- `d0d5a7d` feat(06-08): the ?drive= fixture-replay knob — the D-16 browser leg's fix source
- `17bb950` feat(06-08): the seven guidance capture legs + the DSGN-02 guidance gallery
- `ed86196` art(06-08): the seven committed guidance captures — the shell proven on film
- `b83a88e` fix(06-08): the pump's accepted fixes ride the chain's surfaces — puck + freshness (G7c)
- `5eeccdf` art(06-08): regenerate the guidance captures — the puck rides the line
## Files Created/Modified
- `web/src/mock/telegram-mock.ts` — the ?drive= knob: fixture glob (root-absolute pattern), slug + shape validation, the replay geolocation (real-time schedule, lazy clock, pause/resume), the honest unknown-fallback token
- `web/scripts/capture.mjs` — the guide wait mode, the stub installer, the seven legs, the generalized composer, the coordinate + computed-color scans, the expected list at 20
- `web/src/route/route.ts` — the confirm-card renderCockpit guard (G7b) + the updatePuck dep call per accepted fix (G7c)
- `web/src/main.ts` — updatePuck wiring: lastFix refresh + marker.update + setStale(false), the camera untouched
- `web/index.html` — the runner-page knob comment (docs-only; the knob is read inside the mock module)
- `web/test/route-surface.test.mjs` — G7b + G7c (147→148)
- `web/screenshots/*.png` — the seven committed captures
- `.planning/phases/06-turn-by-turn-guidance-live-driving/06-DEVICE-GATE.md` — the blocking gate's protocol + checklist
## Decisions Made
See key-decisions: real-time replay, the Playwright-side stub, graph-snapped street names, and the two fix-over-paper-over calls.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] The D-09 confirm card evaporated on a live fix**
- **Found during:** task 1's confirm-leg design (traced the live-drive path before filming)
- **Issue:** `renderCockpit` rebuilt the guidance card over the open confirm on every accepted fix — the ONE PX-03 exit guard survived only between fixes; G7 tested confirm without a subsequent fix so the gate never saw it. The real drive's step 6 (END TRIP on a live road, fixes every ~2–5s) would have hit it within one watch tick.
- **Fix:** renderCockpit skips the card rebuild while `confirmOpen`; banner/list still re-render. G7b pins: confirm mounts → live fix → confirm still shown → YES ABANDON still tears down.
- **Files modified:** web/src/route/route.ts, web/test/route-surface.test.mjs
- **Commit:** fca2a90
**2. [Rule 1+2 - Bug/critical gap] The pump's fixes never rode the chain's surfaces (frozen puck + false SIGNAL LOST)**
- **Found during:** task 1's active-leg design (the puck's visual trail on the probe capture) + the 45s staleness trace
- **Issue:** the UI-SPEC's recorded call pins "the puck … carr[ies] progress", but the pump's onWatchFix moved only the camera — the puck froze at the boot fix and the driver would lose it within seconds; and `lastFix` never refreshed during guidance, so the strip's 45s staleness law would flip SIGNAL LOST (red) mid-drive and re-run the chain (a marker jump + a 60s nudge poll on the road).
- **Fix:** one injected dep (`updatePuck`) called per accepted fix; main.ts composes `lastFix = fix` + `marker.update` (raw fix) + `setStale(false)`; the camera untouched (the guidance camera owns the viewport). G7c pins the dep call + the main.ts source-scan composition.
- **Files modified:** web/src/route/route.ts, web/src/main.ts, web/test/route-surface.test.mjs
- **Commit:** b83a88e
**3. [Rule 3 - Blocking] The route consult needed a steps-bearing answer for the legs to exist at all**
- **Found during:** the first capture run (every guidance leg failed: the fixture-backend fallback's `steps: []` model puts the arrive mark at 0m → instant arrival → teardown before any assert)
- **Issue:** the plan's interfaces name only the mock's fix source; but the LIVE pump's cockpit needs maneuver marks the fixture backend never serves (D-10's honesty law — fixture steps stay empty).
- **Fix:** the engine-A-shaped `/api/route` stub in capture.mjs (Playwright interception, installed pre-goto): consult = the drive model (committed geometry + recorded maneuverIdx/kinds + graph-snapped street names), reroutes = merge models held 5s. No index.html behavior change, nothing shipped; the client-side validation still gates every answer.
- **Files modified:** web/scripts/capture.mjs
- **Commit:** 17bb950 (rode the legs commit)
**4. [Rule 3 - Blocking] Vite 8's import.meta.glob matches no relative pattern escaping src/**
- **Found during:** the knob build (the eager glob compiled to `Object.assign({})` — zero fixtures)
- **Fix:** the root-absolute pattern `/test/fixtures/drives/*.json` (verified by the dev-server probe; all four drives inline into the harness bundle, production tree-shake verified clean in both directions).
### Judgment calls within plan laws (not deviations)
- The replay's pause() freezes EMISSION only; the shell's wall clock keeps running (the machine's grace windows inflate across a pause) — legs that pause close before any machine timing matters; documented in the mock's header.
- The unknown-name token (`ARAHARAH-DRIVE state=unknown`) is the knob's ONLY console line (the plan's sanctioned honest token); a successful load is silent — diagnostics ride `window.__ARAHARAH_DRIVE.name`.
## Auth Gates
None — the capture run is offline (harness stubs + Playwright interception); no Telegram/network credentials needed.
## Known Stubs
**Task 2 (the ONE REAL DRIVE) is NOT RUN — by design, never faked.** It is the phase's declared exit gate (D-16) and a `checkpoint:human-verify gate="blocking"` task: physical GPS, real road geometry, and human legibility/haptic judgment are the declared device-gate protocol. `.planning/phases/06-turn-by-turn-guidance-live-driving/06-DEVICE-GATE.md` carries the full checklist (Parts A/B/C). The phase stays OPEN until the drive's per-step verdict lands; a FAIL holds it open (the Phase 3 UNVERIFIED precedent). Everything automatable on the path is green: web 148/148, bot 125/125, routing 65/65, captures 20/20.
## Threat Surface Scan
No new surface beyond the plan's `<threat_model>`: the replay knob feeds the REAL pump with committed, attributed fixtures (unknown names fail honest — the Tampering mitigation); the guidance legs assert state tokens and scan their whole console for coordinate-bearing lines (the D-15 hygiene law carried into captures — T-06-04); no new production logging (the knob is harness-only, tree-shaken from web/dist — verified in both directions this plan); the real-drive protocol rides the product's own privacy posture (no location history exists; the KV spot-check proves the post-trip deletion on real infrastructure). No threat flags.
## Issues Encountered
- The prior 06-08 dispatches died without landing work (verified quiet + clean before starting); this session started fresh and landed the whole automated half.
- Concurrent processes are writing the shared tree again (.planning/*, AGENTS.md, README, mockup code.html files, a bot command file) — every commit staged ONLY this plan's files individually; none of the concurrent files were touched.
- Node lives under nvm; every shell command sourced `~/.nvm/nvm.sh` first (all prior plans' precedent).
## User Setup Required
For the device gate itself: nothing beyond the standing deploy posture (`npm run deploy -w bot`; the R2 graph already uploaded; the R2 dashboard activation possibly still pending — see 06-DEVICE-GATE.md's executor notes).
## Next Phase Readiness
- **The blocking checkpoint** (task 2): Parts A (gallery), B (the drive), C (the street-source ratification) await the user; the verdict rides 06-08-SUMMARY.md's update + 06-DEVICE-GATE.md.
- **The phase verifier**: D-16's three legs now have their evidence — recorded drives (06-02), captures (this plan), the drive pending (the gate). The two new fixes (G7b/G7c) are pinned by gates, not claims.
- **Phase 7**: the gallery artifact + the recorded calls are the DSGN-02 close-out material.
---
*Phase: 06-turn-by-turn-guidance-live-driving*
*Automated half completed: 2026-10-24; the ONE REAL DRIVE awaits the user (BLOCKING)*
## Self-Check: PASSED
- All 7 committed captures verified present on disk + in git (`ed86196`, `5eeccdf`); all 6 plan commits verified in git log (fca2a90, d0d5a7d, 17bb950, ed86196, b83a88e, 5eeccdf) — zero deletions in any of them.
- Acceptance greps re-run at close: `import.meta.glob` over `/test/fixtures/drives/*.json` + the `__ARAHARAH_DRIVE` surface present in telegram-mock.ts; all 7 UI-SPEC leg names present in capture.mjs asserting ARAHARAH-GUIDE tokens.
- `npm test -w web` 148/148 (G7b + G7c included), bot 125/125, routing 65/65, all typechecks clean; `npm run web:capture` 20/20 with the coordinate-hygiene scan green on every guidance leg.
- STATE.md and ROADMAP.md untouched by this executor (per the dispatch's explicit instruction; their working-tree diffs are other processes' concurrent writes).
- The prior dispatch's "quiet tree" note held this time: every commit staged only this plan's files.
## Checkpoint resolution (orchestrator, 2025-10-25)
ONE REAL DRIVE **APPROVED** (Parts A/B/C). Bug round 1 (a69fe31) fixed the
destination card's no-forward gap + localized the header title. Verification:
06-VERIFICATION.md — phase 6 PASSED. Escalation on record: ALT landmarks (c2)
= next-milestone lever.