Files

128 lines
10 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 6: Turn-by-Turn Guidance (Live Driving) - Context
**Gathered:** 2026-10-24
**Status:** Ready for planning — **with a phase-scope addition** (engine A workstream, D-01)
<domain>
## Phase Boundary
araharah behaves like real navigation: prominent next-maneuver banner + collapsible list, silent self-healing rerouting powered by a REAL router (engine A: Worker-side A\* over an R2-hosted KL road graph — RoutingSource backend #2, built this phase), honest ETA progression with smoothing, haptic cues, full BackButton state graph with PX-03 close protections, and trip-end data deletion (PRIV-01 in-app half). While minimized, the bot pushes schematic glyph+text turn-cards via a Worker Cron Trigger over the Phase 5-frozen guidance envelope. Verified via scripted drive replays plus one real drive.
**PHASE-SCOPE ADDITION (user decision):** the "defer WASM-router" deferral from Phase 5 is superseded — **engine A is built IN this phase** as a first-class workstream. The fixture backend remains RoutingSource #1 (golden pairs demo), engine A becomes #2 (arbitrary-origin routing), and GUID-02's rerouting is REAL from day one.
</domain>
<decisions>
## Implementation Decisions
### Routing engine — THE PIVOT #2 (user decision; supersedes the phase-5 deferral)
- **D-01:** **Engine A ships this phase**: OSM → compact road-graph build script (KL: ~30–80MB binary, typed arrays; one-ways + speed weights; turn restrictions deferred), graph hosted in **R2** (upload script committed — forks upload their own), **Worker-side A\*** (JS typed arrays, WASM optional) exposed as **RoutingSource backend #2** via a JSON routing API on the Worker. Satisfies every law: $0, no VPS, fork-friendly, privacy (the Worker is our infrastructure).
- **D-02:** **Valhalla-to-JS port ruled infeasible** (C++/Boost/mmap tile stores) — recorded so it is never re-proposed. geojson-path-finder rejected (demo-grade: no alternatives/one-ways). Engine B (fully client-side webview router — graph on the phone) is the documented evolution of A, deferred post-v1.
- **D-03:** Fixture backend remains #1 (golden-pairs demo path); engine A is #2 and **becomes the guidance engine**: arbitrary current positions route for real. Router-pending state remains for graph-edge cases (e.g. R2 graph absent — a designed degradation, not an error).
### Reroute semantics (GUID-02)
- **D-04:** The reroute trigger is a **thresholds + grace state machine**: `ON_ROUTE → OFF_ROUTE` (deviation > threshold with a grace window + N consecutive off-route fixes before acting) `→ REROUTING` (engine A call; banner stays calm, no blame) `→ ON_ROUTE` (new route snapped in, list refreshed). Storm guard: minimum time between recalculations. Missed-turn detection = off-route past the maneuver point. All thresholds are named constants, replay-tested.
- **D-05:** **The router snaps** raw GPS fixes to the graph (engine A owns snapping — one authority, no client/server drift). The watch fix streams raw; the router does the rest.
### Guidance UX surface (UI-SPEC scope)
- **D-06:** Layout: **top maneuver banner** (distance + arrow + street) replacing the status strip's top register during guidance (location register compacts); **collapsible full maneuver list** (collapsed default, expands over the map, tap-out collapses); **bottom guidance card** (the route card evolves): ETA + remaining distance + progress.
- **D-07:** **ETA smoothing: EMA + deadband** — recompute from route model + snapped progress (never raw fix-to-destination distance); display updates only past a named threshold (±1 min / ±5% remaining); constants replay-tested. No visible whiplash.
- **D-08:** **Haptics**: `HapticFeedback.selectionChanged()` at maneuver-approach threshold (~300m), impact at the maneuver point. Graceful no-op where unsupported.
- **D-09:** **PX-03**: closing during guidance requires the **"abandon route?"** Rams confirm card (en/ms); `disableVerticalSwipes()` while guidance is active; safe-area insets respected. **PX-01**: BackButton walks the full graph — search → routes → guidance → back to search — never dead-ends (extends the phase-5 two-state walk).
- **D-10:** **Camera**: locked follow (center ≈65% down-screen, pitch 35°) easing along the snapped line; user pan unlocks follow → the Phase 3 recenter control returns with its semantics.
### Backgrounded turn-cards (GUID-05)
- **D-11:** Minimize during guidance → app POSTs the guidance session (the frozen envelope contract goes LIVE: KV writes begin, `guidance:<id>`, 7200s TTL). A **Worker Cron Trigger (60–120s)** scans active guidance keys, projects progress along the snapshot, and pushes a chat turn-card when the next maneuver changes (or every N minutes). App re-open resumes foreground guidance; cron pushes pause while foregrounded.
- **D-12:** The card is a **glyph + text card**: committed arrow-glyph icon set (8 cardinal/turn PNGs, token colors) + ETA/distance/street text — no map bitmap, no renderer dependency, fully $0. Bitmap snapshots deferred to v1.x (Workers cannot rasterize; honest best-effort framing).
- **D-13:** "Best-effort" is honest: cron lag, Telegram delivery variance, and TTL expiry are documented behaviors, not failures; the card copy never promises what the mechanism can't (copy table in UI-SPEC).
### Trip end (PRIV-01 in-app half)
- **D-14:** Ending a trip **deletes the guidance session** (KV record gone — completing the TTL law's promise actively) and shows **"trip ended, data deleted"** messaging (en/ms). Arrival detection = snapped progress ≥ end-of-route threshold; abandon = the PX-03 confirm path. Both delete.
### Watch cadence + verification (GUID-02/03)
- **D-15:** Battery cadence: `watchPosition` high-accuracy, but the **app throttles by distance** — guidance work runs on ≥10m moved or ≥5s elapsed, whichever first. D-05's gated-streaming law finally flips its watch ON here.
- **D-16:** **Scripted drive replays** verify the state machine: recorded JSON position streams (corridor follow, off-route excursion, missed-turn excursion, GPS jitter) replayed through the machine in tests, asserting every transition, the storm guard, and ETA deadband. **Plus one real drive** as the device gate (phase 3/4 protocol).
### OpenCode's Discretion
- Graph build toolchain choice (osmium/planetiler-graph/custom overpass extract), binary layout, A\* variant (bidirectional), alternatives generation via penalty runs
- Deviation/storm/ETA constants' initial values (replay-tuned; named, not magic)
- Cron interval within 60–120s; "N minutes" repeat cadence; glyph icon production method (hand SVG → PNG)
- Banner/list micro-layout within the Rams system; exact copy wording within the tone law
- Envelope write timing (on guidance start vs first minimize — either honors the contract)
</decisions>
<specifics>
## Specific Ideas
- The guidance screen is the product's cockpit: banner + guidance card are instruments, the map is the world — nothing decorative, everything reading-bearing.
- Reroute messaging follows the clinical-honest law: the banner never blames, never asks — it recalculates, states the new route, moves on.
- The 15 fixture pairs remain the demo script; engine A pairs with them (same destinations, arbitrary origins) for the real-drive gate.
</specifics>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### Phase scope + decisions
- `.planning/ROADMAP.md` §Phase 6 — goal, 5 success criteria, research-recommended framing
- `.planning/REQUIREMENTS.md` §GUID-01..06, PX-01, PX-03 (+ PRIV-01 trip half)
- `.planning/phases/05-routing-route-display/05-CONTEXT.md` — the envelope contract being activated; RoutingSource ladder; D-05 cadence law
- `.planning/phases/03-mini-app-shell-geolocation/03-CONTEXT.md` — memory-only law, follow/recenter semantics, capture harness
- `.planning/phases/05-routing-route-display/05-UI-SPEC.md` — shell conventions the guidance UI extends
### Codebase
- `routing/` — RoutingSource interface engine A implements
- `web/src/location/` — the chain; guidance watch extends it (D-05 flip)
- `web/src/map/follow.ts` + `recenter.ts` — camera semantics reused
- `web/src/route/` — phase 5 route surface evolving into guidance chrome
- `bot/src/kv/` — TTL-only patterns; the envelope service lands beside them
- `bot/wrangler.jsonc` — Cron Trigger wiring surface
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- RoutingSource + polyline6 + fixture backend (phase 5) — engine A plugs beneath the same seam
- GuidanceSessionEnvelope types (phase 5, frozen) — the service implements, not redefines
- Follow controller + recenter control (phase 3) — locked-follow camera reuses them
- Mock-Telegram harness + capture profiles — replay fixtures + guidance captures extend them
- glyphs: none yet — the 8-arrow set is new, committed once, reused everywhere
### Established Patterns
- Interface + backend ladder (now 3 real stages); hand-rolled runners; TDD RED→GREEN
- TTL-only KV with keyed namespaces; category-only logging
- Device gates: real hardware at exit (this phase adds the real drive)
### Integration Points
- Cron Trigger = new Worker surface (first scheduled handler in the repo)
- R2 = new storage surface (first binding) — upload script + fork docs
- HapticFeedback/BackButton/vertical-swipes = Telegram WebApp APIs behind capability guards
</code_context>
<deferred>
## Deferred Ideas
- Engine B: fully client-side webview router (graph on the phone; privacy-maximal endgame)
- Turn restrictions in the graph; contraction hierarchies; CH preprocessing
- Bitmap map snapshots in turn-cards (needs a rasterizer we don't have — v1.x)
- Voice guidance, live trip share, toll toggle, speed limits (v1.x, REQUIREMENTS deferred table)
- Traffic-aware ETAs, incident-aware rerouting (v2)
</deferred>
---
*Phase: 06-turn-by-turn-guidance-live-driving*
*Context gathered: 2026-10-24*
### Post-planning ratification (2026-10-24)
- **A1 CPU decision: c3 now** (short routes in-budget; cross-metro → honest router-pending), **c2 (ALT landmarks) committed as the next-milestone upgrade lever**. c1 (Workers Paid) rejected — $0 law.
- **D-12 ratification: street-bearing turn-cards confirmed** (default stands).
- R2 activation pending user dashboard click (code 10042); non-blocking for cron/captures.