# Phase 5: Routing + Route Display - Context **Gathered:** 2026-10-24 **Status:** Ready for planning — **with a roadmap amendment** (SC3 rewording, below) ## Phase Boundary The route EXPERIENCE ships end-to-end in fixture world: a `RoutingSource` interface (mirroring `SearchSource`), a fixture backend whose geometries for 10–20 real KL pairs were precomputed at build time, Rams route rendering (orange casing on the grayscale map), up-to-3 alternatives as a bottom card + chips, selection redraw with camera refit, and an honest "router pending" state for arbitrary origins. The `guidance_sessions` envelope is designed as a types-only frozen contract for Phase 6. **No VPS, no Worker route proxy, no third-party routing calls** — the engine itself is deferred to the WASM-router OSS track (client-side routing, backend #2), with Valhalla-on-VPS documented as backend #3. **ROADMAP AMENDMENT (planning must apply):** SC3 is reworded from "routes cleanly through self-hosted Valhalla … behind the Worker proxy" to: "the golden-route corpus (10–20 real KL pairs) routes cleanly through the fixture RoutingSource backend with bundle-size and type gates holding; the RoutingSource interface carries the documented backend ladder (fixtures → client-side WASM router → Valhalla-on-VPS) and the 'router pending' state is deliberate and designed." RTNG-01's "calculated route" is satisfied by fixture calculation; the arbitrary-origin router is a documented deferral, not an omission. ## Implementation Decisions ### Routing infrastructure — THE PIVOT (user decision, supersedes roadmap SC3 v1) - **D-01:** **No VPS this phase. No third-party routing API ever at runtime** (origin+destination to a third party is a core-value privacy leak). Routing is a **`RoutingSource` interface** with a backend ladder: **#1 fixtures (this phase) → #2 client-side WASM router (OSS track, deferred) → #3 Valhalla-on-VPS (documented, provision scripts as a documented path, not shipped)**. - **D-02:** Backend #1 = **fixture routes for the 10–20 golden KL pairs**, geometries precomputed at BUILD time by a repo script hitting the public OSRM demo server once (build machine, fixed public coordinates — no user data, no runtime dependency), committed as compact encoded polylines. Each pair pins expected distance/duration tolerances. - **D-03:** Arbitrary origin/destination combinations return an honest **"router pending"** state — a designed, deliberate Rams surface (UI-SPEC scope), never a blank map, never a fake route, never a silent failure. - **D-04:** The **WASM-router track** (client-side routing in the webview — origin+destination never touch a server; static-hosted; fork-perfect) is captured as the post-v1 OSS project that becomes RoutingSource backend #2, exactly parallel to the search backend #2 pattern. The user's static-hosting/fork-model intent is preserved as the architectural direction, not abandoned. - **D-05:** The Worker gains **no `/api/route`** this phase. Fixtures ride in the web bundle (gazetteer precedent). The interface stays backend-shaped so a server backend could slot in later without client rework. ### guidance_sessions envelope (Phase 6's contract, frozen now) - **D-06:** **Types-only contract**: `guidance_sessions` envelope = { id, chat ref, route snapshot (inline), created_at } with a **~2h TTL** — shipped as code types + docs, **no KV namespace, no service, no runtime writes**. Phase 6 builds the service on the frozen shape. - **D-07:** Route snapshot lives **inline in the envelope record — one key, one TTL**; nothing outlives 2h, nothing reconstructable after expiry. Consistent with the location-pin TTL precedent (Phase 3). The route model itself carries **step geometry + snapped-progress support fields from day one** (GUID-05/06 upstream obligation). ### Route UX surface (UI-SPEC scope; phase-terminal) - **D-08:** Alternatives present as a **bottom card + chips**: one card for the selected route (time, distance, one-line difference vs best) with compact alternates as selectable chips — tap → redraw. Map stays dominant. - **D-09:** Route line = **signal-orange casing** (dark under-line for grayscale contrast) + orange center line — Phase 1's reserved accent, per the navigate_map_overview mockup. Destination pin keeps venue semantics; origin = subdued ring. Camera **fits route bounds** on draw. - **D-10:** **Display-only terminal surface**: selection redraws + camera refits; a restart/search-again affordance returns to the destination state. **No start-guidance button, no turn instructions, no progress UI** — Phase 6. No placeholder-shaped furniture (Phase 3 D-08 law). ### Gates replacing SC3's latency/subrequest budgets - **D-11:** **Bundle budget**: fixture geometry + route-model code ≤ ~200KB gzipped added to the web bundle, enforced as a BA build-artifact gate. **Type gates**: route model readiness (step geometry + snapped-progress fields) pinned by tests. "Router pending" state covered by UI-SPEC + captures. Latency/subrequest budgets are not applicable to fixture world (documented as such, not faked). ### Privacy (carried + made specific) - **D-12:** Fixture geometries are public-data routes between public places — no user coordinate ever enters a fixture. The build-time OSRM fetch happens on the maintainer's machine against fixed public points; the script documents this boundary. Runtime: origin stays in memory only (Phase 3 law); no route persists (envelope is types-only this phase). ### OpenCode's Discretion - Fixture encoding format (encoded-polyline vs simplified GeoJSON) within the ≤200KB budget - RoutingSource interface shape (async, backend-tagged results, tolerance metadata) - Which Phase 4 corpus destinations become the 10–20 pairs; realistic origins list - One-line-difference copy per alternative card (en/ms, clinical tone); "router pending" copy - Chip/card visual details within the Rams system (UI-SPEC will bind exact geometry) ## Specific Ideas - The route experience should feel like the mockup distilled: the orange line is THE accent on a grayscale world — nothing else competes. - "Router pending" copy should be honest and calm: the app knows what routing is, states it isn't wired yet, offers search again. Same clinical register as the denied card. - Fixture pairs double as the demo script: the 10–20 pairs are the routes a reviewer walks to see the product work. ## Canonical References **Downstream agents MUST read these before planning or implementing.** ### Phase scope + decisions - `.planning/ROADMAP.md` §Phase 5 — goal, success criteria **+ this file's SC3 amendment (planning applies it)** - `.planning/REQUIREMENTS.md` §RTNG-01/02 (+ GUID-05/06 upstream obligations) - `.planning/phases/04-destination-search-kl-gazetteer/04-CONTEXT.md` — SearchSource pattern to mirror; backend-ladder pattern; fork-model law (GPL-3.0) - `.planning/phases/03-mini-app-shell-geolocation/03-CONTEXT.md` — map boot, marker/follow controller, memory-only law, location-pin TTL precedent ### Codebase - `web/src/map/` — MapLibre boot (style never re-set — route layers must be addLayer, not style mutation), marker, follow controller - `web/src/search/resolve-destination.ts` — the preloaded destination this phase routes TO - `search/` — the interface+backend+ladder pattern to mirror - `web/scripts/capture.mjs` + mock profiles — the capture harness the route display must extend (`?dest=` profiles exist) - `bot/src/i18n/strings.ts` pattern — copy discipline for card/difference strings ## Existing Code Insights ### Reusable Assets - `search/` workspace: interface + ladder + loader + corpus-gate patterns to mirror for `routing/` - Phase 4 corpus gate (D-13/D-14) — the fixture-pair tolerance gate follows its shape - Mock harness profiles (`?dest=`) — route-state capture profiles extend these - BA build-artifact gates — the bundle-budget gate joins them ### Established Patterns - npm workspaces + delegate scripts; TDD RED→GREEN; hand-rolled runners - Interface + documented backend ladder (SearchSource → RoutingSource) - Types-only frozen contracts (session envelope) with TTL-only storage law - UI-SPEC gate applies this phase (UI hint: yes) — planning routes through it ### Integration Points - Destination resolver → route destination input (Phase 4 hands off where Phase 5 begins) - Route model step geometry + snapped-progress fields → Phase 6 guidance consumes - `guidance_sessions` envelope types → Phase 6 bot service implements ## Deferred Ideas - **WASM-router OSS project** — client-side routing in the webview (RoutingSource backend #2); static-hosted, privacy-maximal, fork-perfect; its own post-v1 project - **Valhalla-on-VPS** — RoutingSource backend #3; docker-compose + Caddy + provision scripts as a documented path (Oracle Always Free candidate; full-MY extract chosen if provisioned) - **Worker `/api/route` proxy** — only meaningful with a server backend - **Real routing for arbitrary origins** — arrives with backend #2/#3; the "router pending" state is the honest interim - Turn instructions/progress UI — Phase 6 (GUID-*) - Toll-avoidance / costing knobs — v1.x --- *Phase: 05-routing-route-display* *Context gathered: 2026-10-24*