195 lines
19 KiB
Markdown
195 lines
19 KiB
Markdown
---
|
||
phase: 06-turn-by-turn-guidance-live-driving
|
||
plan: 05
|
||
subsystem: web-guidance-surface
|
||
tags: [guidance-cockpit, d06-maneuver-banner, d09-confirm-card, d14-trip-end, px03-close-protection, d08-haptics, committed-glyphs, token-only-css, machine-blink, tdd, copy-gates]
|
||
|
||
# Dependency graph
|
||
requires:
|
||
- phase: 06-turn-by-turn-guidance-live-driving/02
|
||
provides: "the GuidanceState union (idle/on_route/off_route/rerouting/resuming/no_reroute) the banner's register words + calm-state swaps render, and the haptic cue vocabulary (approach/maneuver/arrive) fireHaptic dispatches"
|
||
- phase: 06-turn-by-turn-guidance-live-driving/04
|
||
provides: "the engineering-A workspace the guidance card's route/telemetry model flows from (banner/card builders consume view data, never re-derive)"
|
||
- phase: 05-routing-route-display/05
|
||
provides: "the builder law (route-card.ts: actions-interface-first, createElement+textContent only), the shell register classes (.card/.card-header/.card-actions/.route-telemetry/.route-stack), the 64px bottom composite, and RS5/RS9/RS10 gate shapes guidance-surface mirrors"
|
||
- phase: 03-mini-app-shell-geolocation/03
|
||
provides: "the strings-module copy gate pattern (COPY1..COPY7), the machine-blink gantry precedent (cards.ts), the strip the CSS compacts under [data-guidance]"
|
||
provides:
|
||
- "web/src/guidance/glyphs/*.svg + web/src/guidance/glyphs.ts — the 8-glyph committed maneuver set (single source; PNGs rasterize from these exact files in plan 07) with svgHost, the shell's ONE sanctioned innerHTML site (a static import value, gated by GS6)"
|
||
- "web/src/guidance/banner.ts — SEVEN builders: buildGuidanceBanner (five-slot discipline, GUIDE // NEXT|REROUTING|RESUMING|NO REROUTE, aria-expanded toggle), buildManeuverList (tap-out + NEXT/FUTURE/PAST rows), startGantryBlink/stopGantryBlink, buildGuidanceCard (progressbar + ONE btn-secondary), buildConfirmCard (the ONE btn-primary), buildTripEndCard"
|
||
- "web/src/guidance/haptics.ts — fireHaptic: the three D-08 cues behind ?. chains, silent no-op without the API"
|
||
- "web/src/guidance/close-protection.ts — engage/releaseCloseProtection: PX-03 layers gated BEFORE each call (isVersionAtLeast(\"7.7\") body-swipe, (\"6.2\") native-close net)"
|
||
- "web/src/telegram/webapp.ts — the contract grows by OPTIONAL members only: HapticFeedback (6.1+), vertical-swipe guards (7.7+), closing-confirmation pair (6.2+), isActive (8.0+)"
|
||
- "web/src/i18n/strings.ts — the 18 ShellStrings guidance keys, en + ms verbatim (COPY8 pins every value)"
|
||
- "web/src/shell.css — the guidance cockpit block: banner chassis + 1px separator, CSS-only strip compaction under [data-guidance=\"active\"], list panel + row states + lamp, 4px progress bar — token vars only, zero motion/depth/radius/hex"
|
||
- "web/test/guidance-surface.test.mjs — GS1..GS14: five-slot banner, list row states, gantry duty cycle, sanctioned-innerHTML + inline-copy gates, card copy + btn-primary placement, CSS token scan, capability-guard fakes"
|
||
- "web/test/glyph-raw-loader.mjs — the Node-side ?raw hook (tests exercise the committed glyph strings verbatim)"
|
||
affects: [06-06 guidance wiring (composes these builders with the 06-02 state machine + wires the guards), 06-07 turn-card PNGs (rasterize glyphs/*.svg), 06-08 capture legs (the guide-* classes are the capture surface)]
|
||
|
||
# Tech tracking
|
||
tech-stack:
|
||
added: [] # zero new dependencies — Vite ?raw is a built-in import suffix; the Node loader twin is a 20-line test hook
|
||
patterns:
|
||
- "builder law extended to guidance: pure (locale, view, actions) => HTMLElement compositions, Node-tested through a minimal document fake (theme.test stub pattern) — no browser in the suite"
|
||
- "the ONE sanctioned innerHTML site: glyphs.ts svgHost parses a STATIC committed import (GS6 pins the count: 0 in banner.ts, exactly 1 at GLYPHS[kind] in glyphs.ts)"
|
||
- "registers are code-composed instrument constants: `${loc.guideRegister} // ${stateWord}` — the state words NEXT/REROUTING/RESUMING/NO REROUTE/CONFIRM/END and unit formats live in code, never the locale tables"
|
||
- "distance '—' iff distanceM === null (pure view): the UI-SPEC state table decides who passes null — RESUMING legally shows projection numbers"
|
||
- "capability guards: typeof wa.isVersionAtLeast === \"function\" && wa.isVersionAtLeast(\"7.7\") BEFORE the call, then ?. on the member — no-throw proven on gateless fakes and null"
|
||
- "CSS-only strip compaction: [data-guidance=\"active\"] selectors touch layout only; strip.ts is source-gated to never mention data-guidance (GS11)"
|
||
|
||
key-files:
|
||
created:
|
||
- web/src/guidance/glyphs.ts
|
||
- web/src/guidance/glyphs/depart.svg
|
||
- web/src/guidance/glyphs/straight.svg
|
||
- web/src/guidance/glyphs/left.svg
|
||
- web/src/guidance/glyphs/right.svg
|
||
- web/src/guidance/glyphs/slight-left.svg
|
||
- web/src/guidance/glyphs/slight-right.svg
|
||
- web/src/guidance/glyphs/uturn.svg
|
||
- web/src/guidance/glyphs/arrive.svg
|
||
- web/src/guidance/haptics.ts
|
||
- web/src/guidance/close-protection.ts
|
||
- web/test/guidance-surface.test.mjs
|
||
- web/test/glyph-raw-loader.mjs
|
||
modified:
|
||
- web/src/guidance/banner.ts
|
||
- web/src/i18n/strings.ts
|
||
- web/src/shell.css
|
||
- web/src/telegram/webapp.ts
|
||
- web/test/copy-gate.test.mjs
|
||
- web/test/route-surface.test.mjs
|
||
- web/test/build-artifact.test.mjs
|
||
|
||
key-decisions:
|
||
- "Glyph geometry follows the UI-SPEC table verbatim, including the uturn machined staple with NO arrowhead and arrive as the promoted phase-5 pin (12×12 square + 3×8 tail + shaft) — the table is the binding source, not turn-glyph convention."
|
||
- "The banner gantry renders ONLY in rerouting/resuming: NO REROUTE is a designed deferral, not a loading state — no blink (GS1 pins the absence)."
|
||
- "Banner z-index 3 sits above the list tap-out (z 2): while the list is open the banner stays the CLOSE toggle; every other tap (map included) collapses via the full-viewport transparent layer — the UI-SPEC's tap-out law kept whole."
|
||
- "Confirm + trip-end bodies are text-body-sm: the UI-SPEC typography table assigns the state-line role (degradation copy, confirm body) to body-sm; the trip-end card joins that family."
|
||
- "svgHost throws nothing and parses via <template> per the plan; the caller sizes the host (32 banner / 24 list) — the committed SVGs carry no size of their own."
|
||
|
||
patterns-established:
|
||
- "guidance-surface gate family (GS*): behavioral cases through the document fake + structural comment-stripped scans (sanctioned-innerHTML count, inline-copy ban with an explicit instrument-word allowlist, CSS token purity, btn-primary placement)"
|
||
- "the ?raw loader twin pattern: node:module register hook in web/test/glyph-raw-loader.mjs makes Vite-?raw modules Node-testable byte-verbatim — reusable for any future raw-asset import"
|
||
- "copy-gate growth: COPY8 pins all 18 guidance values verbatim in en + ms; COPY1's floor moves 40→58 so the surface cannot silently shrink"
|
||
|
||
requirements-completed: [GUID-01, GUID-04, PX-03] # the surface/guard halves: builders render GUID-01's cockpit, fireHaptic dispatches GUID-04's cues, the guards implement PX-03 — the live composition wiring is plan 06's (state writes are orchestrator-owned)
|
||
|
||
# Metrics
|
||
duration: 16min
|
||
completed: 2026-10-24
|
||
---
|
||
|
||
# Phase 6 Plan 5: The Guidance Cockpit Surface — Glyphs, Banner, Cards, Guards Summary
|
||
|
||
**The guidance cockpit exists as seven pure, Node-tested DOM builders + two capability-guarded modules: the five-slot banner that never blanks across all six machine states, the honest collapsible maneuver list with the 8×8 NEXT lamp, the evolved guidance card with a stepping progress bar and exactly one secondary CTA, the ONE confirm surface carrying the verbatim privacy copy, the trip-end card, and the 8-glyph committed set imported ?raw as the single source — every word from the strings module in en+ms, every pixel from tokens, zero motion, zero depth, zero red.**
|
||
|
||
## Performance
|
||
|
||
- **Duration:** 16 min
|
||
- **Started:** 2026-10-24T06:05:55Z
|
||
- **Completed:** 2026-10-24T06:21:45Z
|
||
- **Tasks:** 4/4 (tasks 2–4 RED→GREEN; task 1 atomic with its COPY gate)
|
||
- **Files:** 21 (13 created, 8 modified)
|
||
|
||
## Accomplishments
|
||
|
||
- **The 8-glyph committed set is real once** (GUID-01): 8 hand-authored 32×32 SVGs per the UI-SPEC geometry table — 3px stroke, square caps, miter joins, orthogonal/45° segments only (the uturn staple is the arc-free proof case; GS7 greps every file for the arc-free law and pins each raw string byte-equal to its committed file). `glyphs.ts` imports them `?raw` (single source) and exposes `svgHost` — the shell's ONE sanctioned innerHTML site, parsing a static import value, counted by GS6 (0 in banner.ts, 1 in glyphs.ts at `GLYPHS[kind]`). PNGs for the chat turn-cards rasterize from these exact files in plan 07.
|
||
- **The banner renders the machine's every mood** (D-04/D-06): GS1 drives all five banner-bearing states and proves the five slots — register (`GUIDE // NEXT|REROUTING|RESUMING|NO REROUTE`), 48×48 plate with the 32×32 committed glyph, distance (`250`+`M` / `1.2`+`KM` / honest `—`), street (verbatim, `streetUnnamed` fallback) or the calm-state copy — never a blank slot mid-swap. The root is one full-width `<button>` with `aria-expanded`/`aria-controls`→`guide-maneuver-list`/label; LIST↔CLOSE is the real-word toggle. The machine-blink gantry rides only rerouting/resuming; GS5 proves the ~500 ms duty cycle, idempotent restart (no stacked timers), and stop-clears.
|
||
- **The list is honest and collapsible** (D-06): labelled `role="region"` card panel (id = the banner's aria-controls target) + a transparent full-viewport tap-out (GS4 proves one tap dismisses); NEXT/FUTURE/PAST rows carry real text, 24×24 aria-hidden glyphs, the 8×8 orange lamp on NEXT only, and right-aligned `{n} M`/`{n.n} KM` distances.
|
||
- **The route card evolved** (D-06): GUIDE register, verbatim destination headline, the live telemetry row (`18 MIN · 7.8 KM · ETA 18:42` — instrument formats identical under ms), a `role="progressbar"` 4px bar with the full aria value set and instant inline width, the no-reroute state line ONLY when degrading (GS8b), and ONE `.btn-secondary` END TRIP — GS12 pins `.btn-primary` at exactly one occurrence in banner.ts: the confirm card (D-09), whose `YES, ABANDON`/`CONTINUE` carry the verbatim en+ms privacy copy (GS9, both locales, both actions). The trip-end card (GS10) renders `GUIDE // END` with the deletion copy and zero buttons.
|
||
- **The guards degrade to honest no-ops** (GUID-04/D-08/PX-03/T-06-06): `fireHaptic` routes approach/maneuver/arrive to `selectionChanged()`/`impactOccurred("medium")`/`notificationOccurred("success")` behind `?.` chains (GS13/GS13b — no-throw with no API and with null); close protection gates BEFORE each call — body-swipe off at `isVersionAtLeast("7.7")`, the native-close net at `("6.2")`, release inverting both, gateless fakes and null proven silent (GS14/GS14b/GS14c). The webapp contract grew by OPTIONAL members only; every existing fake ran untouched.
|
||
- **Copy is gated, not promised**: the 18 guidance keys exist in en+ms with verbatim UI-SPEC values (COPY8 pins all 36 strings); COPY1's floor is 58; GS6b bans every copy value as a literal in banner.ts while allowlisting the instrument words; the theme audit stayed CLEAN (0 off-token values).
|
||
- **The shell.css block dresses the cockpit in tokens only** (GS11): zero `transition`/`animation`/`box-shadow`/`border-radius`, zero hex literals; strip compaction is pure CSS under `[data-guidance="active"]` and strip.ts is source-gated to never know about it.
|
||
- **Gates grew with the family**: RS10 floor 16→24 (36 src files found), BA9's input list += `web/src/guidance` printing **69507 / 204800 gz bytes** — the budget held without touching the ceiling.
|
||
- **Suite state:** web 124 passed / 0 failed (100 pre-existing untouched-green), bot 103, routing 65; all workspaces green; typecheck clean everywhere.
|
||
|
||
## Task Commits
|
||
|
||
1. **task 1: the 8-glyph committed set + glyphs.ts + strings keys** (atomic)
|
||
- `0023ab3` feat(06-05): the 8-glyph committed maneuver set + glyphs.ts + the 18 guidance strings keys
|
||
2. **task 2: banner + maneuver-list builders + gantry blink** (TDD)
|
||
- `a60443a` test(06-05): failing guidance-surface suite GS1-GS7 (RED)
|
||
- `432f33a` feat(06-05): the maneuver banner + collapsible list builders + the machine-blink gantry (GREEN)
|
||
3. **task 3: guidance/confirm/trip-end card builders + shell.css block** (TDD)
|
||
- `f0931d6` test(06-05): failing guidance-card suite GS8-GS12 (RED)
|
||
- `79836f0` feat(06-05): guidance/confirm/trip-end card builders + the token-only shell.css guidance block (GREEN)
|
||
4. **task 4: capability guards + webapp members + gate bumps** (TDD)
|
||
- `47b1524` test(06-05): failing capability-guard suite GS13-GS14 (RED)
|
||
- `da42c77` feat(06-05): capability guards — haptics + close protection + optional webapp members + RS10/BA9 gate bumps (GREEN)
|
||
|
||
## Files Created/Modified
|
||
|
||
- `web/src/guidance/glyphs/*.svg` (8) — the committed 32×32 set (single source)
|
||
- `web/src/guidance/glyphs.ts` — GLYPH_KINDS/GLYPHS (`?raw`) + svgHost (the one innerHTML site)
|
||
- `web/src/guidance/banner.ts` — the seven builders + MANEUVER_LIST_ID + the blink timers
|
||
- `web/src/guidance/haptics.ts` — fireHaptic + HapticCue
|
||
- `web/src/guidance/close-protection.ts` — engage/releaseCloseProtection (version-gated)
|
||
- `web/src/telegram/webapp.ts` — 6 optional members (HapticFeedback, 2 swipe, 2 closing-confirmation, isActive)
|
||
- `web/src/i18n/strings.ts` — +18 keys × 2 locales, values verbatim
|
||
- `web/src/shell.css` — the guidance cockpit block (banner, compacted strip, list, progress bar)
|
||
- `web/test/guidance-surface.test.mjs` — GS1..GS14 + helpers (document fake, recording fakes)
|
||
- `web/test/glyph-raw-loader.mjs` — the Node `?raw` load hook
|
||
- `web/test/copy-gate.test.mjs` — COPY1 floor 58 + COPY8 (36 verbatim pins)
|
||
- `web/test/route-surface.test.mjs` — RS10 floor 24
|
||
- `web/test/build-artifact.test.mjs` — BA9 input list += guidance family
|
||
|
||
## Decisions Made
|
||
|
||
See key-decisions: verbatim glyph geometry (no uturn arrowhead), gantry only in the two wait states, banner-above-tap-out z-order, body-sm for confirm/trip-end bodies, sizeless committed SVGs sized by the caller.
|
||
|
||
## Deviations from Plan
|
||
|
||
### Auto-fixed Issues
|
||
|
||
**1. [Rule 3 - Blocking issue] Node could not import the `?raw` module family the plan requires testing**
|
||
- **Found during:** task 2 RED authoring
|
||
- **Issue:** the plan mandates `?raw` imports (the committed SVGs are the single source) and Node tests asserting `GLYPHS` content — but plain Node cannot load `.svg` modules, so `guidance-surface.test.mjs` could not even load banner.ts.
|
||
- **Fix:** a 20-line `node:module` load hook (`web/test/glyph-raw-loader.mjs`) registered before the suite's dynamic imports: any `?raw` specifier exports the real file's text byte-verbatim — tests exercise exactly what Vite ships. Test infrastructure only; zero product code.
|
||
- **Files modified:** web/test/glyph-raw-loader.mjs (new), web/test/guidance-surface.test.mjs (register call)
|
||
- **Commit:** a60443a
|
||
|
||
### Test-side fixes during GREEN (not deviations — executor test bugs caught by verification)
|
||
|
||
- GS7 compared `raw` to `committed.trim()` while the committed files carry a trailing newline — both sides trimmed (commit 432f33a).
|
||
- GS11's banned-token scan originally ran over raw CSS including the block's own prose comments — now strips comments first (prose is not a call site, the GEO3 pattern; commit 79836f0).
|
||
- GS12 assumed two `btn-secondary` literals; the shared `secondaryButton` helper centralizes the class (better shape) — assertion updated to pin the helper (commit 79836f0).
|
||
- The close-protection version gates were first hidden behind a `versionGate(wa, version)` helper, which could never satisfy the plan's acceptance grep `isVersionAtLeast("7.7")` — gates inlined so the literals exist verbatim (commit da42c77).
|
||
|
||
## Known Stubs
|
||
|
||
None. Every export is a fully wired implementation; the guidance chrome renders only over live data (the plan's empty-state law: guidance renders only over a route). The blink intervals are wired for caller teardown (stopGantryBlink) with plan 06 composing the swaps — the API makes a leak visible by design.
|
||
|
||
## Threat Surface Scan
|
||
|
||
No new surface beyond the plan's `<threat_model>`: no network endpoints, no auth paths, no storage, no logging (the builders never touch console/fetch/storage; banner.ts's only DOM writes are createElement/textContent + the gated static innerHTML). T-06-06 is implemented and test-proven: version gates BEFORE every call, `?.` member chains, no-throw on gateless fakes and null (GS13b/GS14). The gantry DoS disposition holds: one WeakMap-keyed interval per element, idempotent start, stop clears — GS5 pins no-post-stop flips. The confirm card is plain DOM in the app's own register (the spoofing disposition's accept stands). No threat flags.
|
||
|
||
## Issues Encountered
|
||
|
||
- Node lives under nvm; every shell command sourced `~/.nvm/nvm.sh` first (all prior plans' precedent). No project impact.
|
||
- The `theme/dist/rams.css` base layer already resets `border-radius: 0` on every button — the banner button needed no literal in the guidance block, keeping the GS11 scan clean without a sanctioned-exception debate.
|
||
|
||
## User Setup Required
|
||
|
||
None — builders, guards, tokens, and tests; zero new dependencies, zero env vars.
|
||
|
||
## Next Phase Readiness
|
||
|
||
- **06-06 (wiring)**: everything to compose is exported and tested — `buildGuidanceBanner(locale, {state, glyph, distanceM, street, listOpen}, {onToggle})` consumes `GuidanceState` directly; the machine's `reroute-requested`/effects map onto the guards; `data-guidance="active"` on the shell root triggers the CSS compaction; engage/release close protection bind guidance start/trip end.
|
||
- **06-07 (turn-cards)**: rasterize `web/src/guidance/glyphs/*.svg` (stroke `#EB8008` on `#1A1A1A` plate) — the committed geometry is the single source; the web side will not change.
|
||
- **06-08 (captures)**: the `guide-*` classes (`guide-banner`, `guide-list`, `guide-progress`, `guide-confirm`) are the capture surface; `?drive=` profiles can assert `GUIDE // NEXT|REROUTING` registers verbatim via the COPY8-pinned strings.
|
||
|
||
---
|
||
*Phase: 06-turn-by-turn-guidance-live-driving*
|
||
*Completed: 2026-10-24*
|
||
|
||
## Self-Check: PASSED
|
||
|
||
- All 20 created/modified files verified present on disk (8 SVGs, 4 guidance modules, 2 test files, strings/shell.css/webapp.ts, 3 gate suites).
|
||
- All 7 plan commits verified in git log: 0023ab3 (task 1), a60443a→432f33a (task 2 RED→GREEN), f0931d6→79836f0 (task 3 RED→GREEN), 47b1524→da42c77 (task 4 RED→GREEN) — TDD gate sequence intact for every tdd task.
|
||
- `npm test -w web`: 124 passed / 0 failed (100 pre-existing untouched-green); bot 103, routing 65; web typecheck clean; theme audit CLEAN (0 off-token values).
|
||
- Acceptance greps re-run at close: `isVersionAtLeast("7.7")`/`("6.2")` present in close-protection.ts; `btn-primary` exactly once in banner.ts; innerHTML once (glyphs.ts at GLYPHS[kind]); zero functional-red in the guidance block; zero TODO/FIXME/placeholder markers.
|
||
- RS10 ≥24 green (36 files found); BA9 printed 69507 / 204800 gz and green.
|
||
- STATE.md, ROADMAP.md, REQUIREMENTS.md untouched by this executor (their working-tree diffs are the orchestrator's own concurrent writes, present before execution started).
|
||
|