9.2 KiB
Phase 5: Routing + Route Display - Context
Gathered: 2026-10-24 Status: Ready for planning — with a roadmap amendment (SC3 rewording, below)
## Phase BoundaryThe 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 DecisionsRouting 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
RoutingSourceinterface 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/routethis 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_sessionsenvelope = { 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)
- 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_refs>
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 controllerweb/src/search/resolve-destination.ts— the preloaded destination this phase routes TOsearch/— the interface+backend+ladder pattern to mirrorweb/scripts/capture.mjs+ mock profiles — the capture harness the route display must extend (?dest=profiles exist)bot/src/i18n/strings.tspattern — copy discipline for card/difference strings
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
search/workspace: interface + ladder + loader + corpus-gate patterns to mirror forrouting/- 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_sessionsenvelope types → Phase 6 bot service implements
</code_context>
## 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/routeproxy — 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