docs(06-08): complete the automated half — captures + replay knob; the ONE REAL DRIVE is the BLOCKING gate

- task 1 done: the ?drive= replay knob, the seven guidance legs, the
  DSGN-02 guidance gallery — all committed and green (web 148/148,
  bot 125/125, routing 65/65, captures 20/20)
- two device-gate bugs fixed before the road (G7b confirm survival,
  G7c puck+freshness)
- task 2 = checkpoint:human-verify gate=blocking: the executor STOPPED
  per protocol — 06-DEVICE-GATE.md carries the real-drive checklist
  (Parts A/B/C); device results are never faked
- STATE.md and ROADMAP.md intentionally untouched (orchestrator-owned,
  per the dispatch)
This commit is contained in:
araharah-executor
2026-10-24 21:41:06 +08:00
parent 5eeccdfe38
commit e546bcb2e0
2 changed files with 271 additions and 0 deletions
@@ -0,0 +1,203 @@
---
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.