Files

195 lines
19 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: 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).