36 KiB
Roadmap: araharah
Overview
araharah turns Telegram into a privacy-respecting turn-by-turn navigator for KL: a bot handles
identity, search, and launch; a Telegram Mini App renders Rams-styled route guidance; Valhalla
routing, self-hosted Photon lookup, OpenFreeMap tiles, and MapLibre GL JS v6 supply the OSM
geodata layer; one Cloudflare Worker (static assets + webhook + /api/*) serves it all with
KV-only TTL storage. The roadmap follows the research's strict dependency funnel — design tokens
first (design fidelity is the flagged #1 sink for v1), then the bot/identity security floor,
then the Mini App shell with geolocation, then KL destination search, then routing and route
display, then live turn-by-turn guidance including backgrounded chat turn-cards, and finally a
hardening pass that earns the private launch. Two phases deliberately surface the project's
biggest unknowns early: WebView geolocation quirks (Phase 3) and KL search quality (Phase 4).
Phases
Phase Numbering:
- Integer phases (1, 2, 3): Planned milestone work
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
Decimal phases appear between their surrounding integers in numeric order.
- Phase 1: Design Tokens + Rams Foundation - Distill the Telemetry Rams system into a single token package and Rams-styled map basemap, with a mockup-vs-implementation approval artifact — before any screen is built
- Phase 2: Bot Skeleton + initData Security - grammY bot on one Cloudflare Worker: /start, command menu, /privacy, and HMAC-validated identity with TTL-only KV storage
- Phase 3: Mini App Shell + Geolocation - Telegram WebApp shell that boots, authenticates, and reads a reliable live location across the Android/iOS/Desktop client matrix (completed 2026-10-23)
- Phase 4: Destination Search + KL Gazetteer - Free-text KL search that resolves landmarks despite Malay abbreviations and typos, venue cards, and Mini App launch with destination preloaded (completed 2026-10-23)
- Phase 5: Routing + Route Display - Valhalla-backed route calculation shown as a Rams route line with quiet alternative cards; the full search→route→render loop lands (completed 2026-10-23)
- Phase 6: Turn-by-Turn Guidance (Live Driving) - Client guidance state machine: maneuver banner, missed-turn auto-recalculation, honest ETA, haptics, seamless resume, and backgrounded chat turn-cards (completed 2026-10-24)
- Phase 7: Hardening + Private Launch - Device-matrix QA, graceful failure states, privacy-by-construction audit, and golden-route regression before real drivers (planned 2026-10-25) (completed 2026-10-25)
Phase Details
Phase 1: Design Tokens + Rams Foundation
Goal: The Telemetry Rams design system exists as one centralized, importable token set — including map basemap cartography — with a user-approved mockup-vs-implementation gallery proving the fidelity bar before any product screen is built. Depends on: Nothing (first phase) Requirements: DSGN-01, DSGN-02 Success Criteria (what must be TRUE):
- Every rendered Rams component derives exclusively from the centralized token set (exacted palette, 8px grid, radius-0, typography) — an audit sweep finds zero off-token color/spacing/typography values in the component sheet (DSGN-01)
- A side-by-side gallery comparing the rendered token/component sheet against the
stitch_minimalist_route_navigator/mockup screens exists and is approved by the user — establishing the DSGN-02 approval artifact mechanism reused by every later UI phase - A Rams-styled MapLibre basemap (OpenFreeMap
libertystyle forked to Rams tokens: grayscale grounding, muted land/water, prepared accent for route highlighting) renders in a browser (DSGN-01, map cartography) - Design authority is singular: the duplicate inline Tailwind token blocks and the conflicting root
DESIGN.mdtypography are superseded by the token package as the one source of truth (resolves the CONCERNS.md design-source conflict) Plans: 4 plans
Plans:
- 01-01-PLAN.md — Repo hygiene + Tailwind v4 token package (
theme/) + self-hosted fonts + DESIGN.md supersession (D-01/02/04/05/06/07/16) - 01-02-PLAN.md — Component sheet:
theme/src/components.css+sheet.htmlconsuming tokens only (D-03/D-11) - 01-03-PLAN.md — Rams basemap: token-derived OpenFreeMap liberty fork + KL preview (D-08/09/10)
- 01-04-PLAN.md — Off-token audit (D-13) + Playwright captures (D-14) + DSGN-02 approval gallery (D-12), blocking user checkpoint
Notes:
- Repo hygiene lands here first: scrub
*:Zone.Identifierartifacts,.gitignorefor OS junk/node_modules/secrets (pre-dates any credential handling). - Project skill
.agents/skills/design-taste-frontend/applies to all UI phases under its existing brand/design system special case: keep the Rams palette/type, app-screen house rules for dashboard-like surfaces, real-browser inspection for the gallery artifact. Telemetry Rams tokens are binding where skill defaults would differ. - Standard patterns (no research phase needed): mockups are the spec; token extraction from the mockup Tailwind config. UI hint: yes
Phase 2: Bot Skeleton + initData Security
Goal: Users can reach araharah through Telegram with a working command surface, and the backend's identity + privacy architecture is provably sound from the first deploy — the initData validator is the first backend artifact. Depends on: Phase 1 Requirements: ONBD-01, PRIV-01, PRIV-02 Success Criteria (what must be TRUE):
- A new user sends /start and receives a concise explanation plus an always-available command menu (/help, /privacy) that answers correctly (ONBD-01)
- /privacy gives an honest, visible statement of what araharah stores and does not store (PRIV-01) — the in-app "trip ended, data deleted" messaging half completes with guidance in Phase 6, riding this requirement's message design
- Tampered, stale, and replayed initData payloads are rejected with CI test vectors and known-good HMAC cases passing; only validated identities receive a one-time session token (security gate — the phase's exit criterion)
- A storage spot-check shows KV with TTL-only namespaces and zero persistent coordinates or user profiles — privacy by construction holds from the first line of backend code (PRIV-02; re-audited in Phase 7) Plans: 3 plans
Plans:
- 02-01-PLAN.md — bot workspace scaffold + Wave-0 test harness (RED initdata suite)
- 02-02-PLAN.md — initData HMAC validator (rejection classes) + TTL-only KV sessions (GREEN)
- 02-03-PLAN.md — command surface (en/ms, aliases) + Worker wiring (/telegram/webhook, /api/session) + deploy checkpoint
Notes:
- One Cloudflare Worker deployable (static assets + webhook +
/api/*); KVchat_sessions(minutes TTL) is the first namespace; theguidance_sessions(~2h TTL) envelope is only designed with its consumer in Phase 5. - Standard patterns (no research phase needed): grammY + Workers hosting is first-class documented; verify exact grammY initData typings at phase start (research gap). UI hint: no
Phase 3: Mini App Shell + Geolocation
Goal: The Mini App opens inside Telegram, establishes a validated session, and obtains the user's live location reliably — with every fallback path that keeps location flowing when the primary path dies, verified on real devices. Depends on: Phase 2 Requirements: GEO-01, GEO-02, GEO-03, PX-02 Success Criteria (what must be TRUE):
- On a real Android phone and iOS phone, the user grants location and sees their live position rendered on the Rams basemap; the permission grant persists across session restarts via LocationManager (version-gated) (GEO-01)
- When in-app location fails (Desktop Telegram without geolocation, or denied permission), the chat "send location" keyboard fallback recovers the origin and the app proceeds with it (GEO-02)
- Location denial shows a deliberate denied state (never a blank map), and the app renders correctly under Telegram light and dark themes while the map keeps Rams cartography constant (PX-02, platform themes)
- No coordinate written anywhere persists: storage remains TTL-ephemeral only, spot-checkable at phase exit (GEO-03)
- Device-matrix exit pass: Android + iOS + Desktop + plain browser each demonstrate boot → session → position, exercising the full fallback chain (pitfall research device-matrix criterion) Plans: 4 plans
Plans:
Wave 1 (baseline):
- 03-01-PLAN.md —
web/workspace + Wave-0 test runner; TTL-only location-pin KV, GEO-02 chat keyboard handler, and theGET /api/locationrelay route
Wave 2 (blocked on Wave 1 completion):
- 03-02-PLAN.md — Location chain: three source adapters normalizing to one
Fix, the D-04 state machine with freshness as a separate axis, memory-only session store, Telegram platform wrappers, Mock-Telegram harness, and the GEO-01/02/03 + PX-02 gates
Wave 3 (blocked on Wave 2 completion):
- 03-03-PLAN.md — Rams shell: UI-SPEC copy module, token-only stylesheet, MapLibre v6 bootstrap with the WebGL2 gate, marker/follow/recenter, status strip and state cards, and the map-first boot choreography
Wave 4 (blocked on Wave 3 completion):
- 03-04-PLAN.md — Worker
[assets]binding + one-command deploy + D-14 build-artifact gate; Playwright capture harness with the GEO-03 runtime probe; blocking device-matrix exit gate
Cross-cutting constraints (truths appearing in 2+ plans, binding at every wave):
- denied/blank is forbidden — every dependency-failure state renders a deliberate Rams surface (03-02, 03-03, 03-04)
- zero coordinate persistence — TTL-ephemeral KV only; grep gate + runtime probe (03-01, 03-02, 03-03, 03-04)
- Rams token-only styling — Telemetry Rams tokens binding over skill defaults (03-02, 03-03, 03-04)
Notes:
- Highest platform-integration uncertainty in the project — research recommends
/gsd-research-phase(Android LocationManager stale-fix workaround,getLiveData()wrapper, WebView permission bridges, browser testing harness). - BackButton wiring and safe-area groundwork are built here as shell infrastructure; the observable PX-01/PX-03 behaviors are claimed by Phase 6 where the full state graph exists.
- Research findings that shaped the plan:
getLiveData()does not exist (D-05's gated streaming is our ownFix-shaped abstraction with a pluggable cadence — Phase 6 picks poll vswatchPosition); the Android stale-fix inverts the naive D-04 chain reading, so freshness is a separate axis from availability; Desktop Telegram has no geolocation at all (issue #77), making the chat-keyboard path the only Desktop origin;GET /api/locationis required scope; MapLibre v6 is ESM-only and WebGL2-only (the map-less shell state is a deliberate--color-surface-dimfield, not a crash); Bot API 10.2's origin restriction is enforced as of 2026-07-20. - 03-04 task 3 is a blocking human checkpoint: real Android, iOS, and Telegram Desktop are listed in RESEARCH as required with no fallback. If no device is available, Success Criterion 5 is recorded UNVERIFIED and the phase is held open rather than silently closed. UI hint: yes
Phase 4: Destination Search + KL Gazetteer
Goal: Users type a destination the way locals actually write it and get the right KL-metro landmark back, then step into the Mini App already aimed at their destination. Depends on: Phase 3 Requirements: SRCH-01, SRCH-02, SRCH-03, ONBD-02, ONBD-03 Success Criteria (what must be TRUE):
- Typing "KLCC" — plus the golden query set ("Sunway Pyramid", "DUKE", mall and kampung-style queries) — returns the right KL landmark in a shortlist (SRCH-01)
- Abbreviated, typo'd, and missing-space queries ("Jln Ampang" ↔ "Jalan Ampang", "Tmn", glued words) resolve to the same result as the canonical spelling (SRCH-02)
- Choosing a result sends a native venue card in chat confirming the destination before navigation opens (SRCH-03)
- Tapping through from the search result launches the Mini App with the destination preloaded — no re-entry in the webview (ONBD-02); adding araharah to the phone home screen and launching from there lands in the app ready to search (ONBD-03)
- The self-hosted Photon KL bake-off is concluded against the real-query acceptance corpus with the decision (and any gazetteer/fallback escalation) documented — the search-quality experiment closes, it doesn't stay open Plans: 4 plans
Plans:
Wave 1 (the shared foundation — a fourth npm workspace, search/):
- 04-01-PLAN.md —
@araharah/searchworkspace: theSearchSource/GazetteerEntrycontract, the JUPEM-derived abbreviation table as editable DATA, the symmetric normalizer, the hand-rolled OSA distance, the disjoint-tier scorer, the curated KL gazetteer with locals' aliases, the lazy three-environment loader, plus the root GPL-3.0LICENSEand the ODbL-1.0 data notice (D-04/05/06/07/08/09/15)
Wave 2 (parallel — the measurement instrument and the chat surface):
- 04-02-PLAN.md — the pinned golden corpus (D-13), the D-14 bake-off runner + the in-suite Nyquist gate, the Overpass top-up tool (D-06), the JSON→SQLite converter (D-05), and
docs/SEARCH-QUALITY.mdrecording the measured result and the escalation lever (SRCH-01, SRCH-02) - 04-03-PLAN.md — the
/caricommand (D-01), the single-messagesendVenuecard with 2-4 inline alternates (D-02/D-10), the honest out-of-area refusal, the OPEN deep link carrying a plain gazetteer id with NO crypto (D-11),MINI_APP_URLconfig, and the fourcommands.test.mjslandmines defused in the same change (SRCH-03, ONBD-02)
Wave 3 (the webview hand-off + the blocking device gate):
- 04-04-PLAN.md — the two-carrier destination reader (
?dest=primary,initData.start_paramsecondary) with one shared validation, the memory-only resolver, the honest "destination unavailable" state, ONBD-03's minimaladdToHomeScreen/checkHomeScreenStatushooks behind a Bot API 8.0 guard, the build-artifact drift gate, the destination-preload capture, and the blocking Android/iOS/Desktop device gate (ONBD-02, ONBD-03)
Cross-cutting constraints (must_haves.truths asserted by 2+ plans):
- The deep-link carrier correction (plain id via
?dest=URL param read fromlocation.search,start_paramsecondary, NO crypto) — plans 03, 04 - The D-14 corpus gate as an executable in-suite assertion (top-3 ≥ 90% on positives, negatives honestly refused) — plans 01 (admission rule), 02 (runner + suite)
- Category-only logging / no query text persisted (D-15) — plans 01 (no logging in the module), 03 (category-only literals), 04 (state-token capture signal)
Notes:
- Budgeted bake-off: research recommends
/gsd-research-phase(gazetteer sourcing/structure ~200 entries, Malay normalizer, photon-vs-fallback decision; funds a GraphHopper MY dump on the VPS). - D-03 supersedes the roadmap's "VPS provisioning begins here" note — the search backend is the in-Worker curated gazetteer ($0 infrastructure); Photon-on-VPS is deferred to Phase 5 (Valhalla needs the VPS anyway) and stays behind the
SearchSourceinterface as backend #3. - Mechanism correction (RESEARCH Pitfall 1, plan 04-03/04-04): a
web_appinline button'sWebAppInfocarries ONLY aurl— there is nostart_paramfield (verified against the installed grammY typings), so the deep link's plain gazetteer id travels as?dest=<slug>in that URL and the webview readslocation.searchfirst,initData.start_paramsecond. The D-10/D-11 intent (venue card → one tap → Mini App preloaded with a plain id, no crypto) is unchanged; a structural gate asserts no crypto in the deep-link path. - The bilingual alias scheme:
/cari+/searchare registered together (the Phase 2 D-04 precedent of pairing/start↔/mula,/privacy↔/privasi);/pergi_kestays reserved for Phase 5 and its future-stub ban is narrowed to/pergi_ke, not removed. UI hint: no
Phase 5: Routing + Route Display
Goal: The product exists end-to-end: current location to Rams-drawn route with quiet alternative choices, on routing infrastructure fast enough for the guidance phase to build on. Depends on: Phase 4 Requirements: RTNG-01, RTNG-02 Success Criteria (what must be TRUE):
- A user sees their calculated route drawn on the Rams-styled map — grayscale basemap grounding with the accent route line — from their current location to the chosen destination (RTNG-01)
- The user can choose among up to 3 alternatives presented as quiet Rams cards (time, distance, one-line difference) and the selection redraws (RTNG-02)
- 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 (SC3 amended 2026-10-24 per 05-CONTEXT.md — the fixture-backend pivot; RTNG-01's "calculated route" is satisfied by fixture calculation, the arbitrary-origin router is a documented deferral) Plans: 5 plans
Plans:
Wave 1 (parallel foundations — the fifth npm workspace + the frozen Phase 6 contract):
- 05-01-PLAN.md — the
routing/workspace (@araharah/routing): theRoutingSourcecontract with the documented backend ladder (D-01/D-04), the route model's day-one step-geometry + snapped-progress fields (D-07), the polyline6 codec (external-vector-tested), the geo math, the fixture backend with the 200 m matching law + honest-null pending semantics (D-03), the committed empty fixture schema with ODbL header, and the three-environment loader proof (RTNG-01) - 05-03-PLAN.md — the
guidance_sessionsenvelope as a TYPES-ONLY frozen contract (D-06/D-07): envelope + inline route snapshot types, the 7200 s TTL constant, theguidance:<id>key scheme, and seven GE gates proving no KV namespace, no runtime writes, no premature consumer (Phase 6 builds on the frozen shape)
Wave 2 (blocked on Wave 1 completion — the data product and the visible surface, in parallel):
- 05-02-PLAN.md — the golden-pair corpus: 15 pinned public KL pairs (D-02), the maintainer fetch tool against the public OSRM demo (D-12 boundary in its header), the committed populated fixture dataset, and the RT1–RT10 offline tolerance gate as CLI + in-suite Nyquist gate — public-origin proof, geometry↔summary consistency, codec round-trip, size budget, alternatives floor (RTNG-01, RTNG-02)
- 05-04-PLAN.md — the web route surface per the approved 05-UI-SPEC: the four-layer orange casing stack added once after building-3d (D-09), origin ring + destination pin, the bottom route card + alternates chips (D-08), the designed router-pending card (D-03), fit-bounds camera law, the auto-consult wiring, the two-state BackButton walk (D-10), and the RS1–RS10 gates (RTNG-01, RTNG-02)
Wave 3 (blocked on Wave 2 completion — the phase's closing gates):
- 05-05-PLAN.md — the verification close: BA9 bundle-budget gate (≤ 200 KB gz, measured + printed) + BA10 fixture-parity over the production build (D-11), the five
?fixpair=-driven route capture legs + the DSGN-02 route-gallery artifact, and the Worker-side no-route-proxy gates (D-05) (RTNG-01, RTNG-02)
Cross-cutting constraints (must_haves.truths asserted by 2+ plans):
- Honest-null pending semantics (an unknown destination / uncovered origin resolves to
nulland renders the designed pending card — never a blank map, never a fake route, D-03) — plans 01, 04, 05 - No logging / no storage / no runtime network in the routing path (D-12/D-15 analogs; the Worker gains no
/api/route) — plans 01 (module zero-laws), 03 (types-only envelope), 04 (state-token signals), 05 (RP gates) - The capture signal is a STATE TOKEN, never coordinates (
ARAHARAH-ROUTE state=drawn|pending, paired with DOM text checks) — plans 04 (RS8 source law), 05 (capture legs) - Fixtures are committed PUBLIC data — every pinned origin provably equals a gazetteer landmark coordinate; the bundle carries every pairId; the harness knob accepts pairIds only — plans 02 (RT2b), 05 (BA10, ?fixpair)
Notes:
- GUID-05/06 upstream design obligations land here deliberately: the route model carries step geometry + snapped-progress support from day one, and the ephemeral
guidance_sessionsKV envelope (id, chat ref, route snapshot ref, ~2h TTL) is designed here so the backgrounded turn-card service in Phase 6 has its session contract already defined. - Standard patterns (no research phase needed — light pass only): fixture precompute against the public OSRM demo server at BUILD time (maintainer machine, fixed public coordinates) + committed encoded-polyline storage; no exotic APIs. (The "documented preprocess pipeline for Valhalla + graph/tiles build" note is superseded by the fixture-backend pivot — 05-CONTEXT.md D-01/D-04; Valhalla-on-VPS is RoutingSource backend #3, documented, not shipped.) UI hint: yes
Phase 6: Turn-by-Turn Guidance (Live Driving)
Goal: While driving, araharah behaves like real navigation — prominent maneuvers, silent self-healing recalculation, honest ETAs, haptic cues, seamless resume — and guidance survives the app being minimized via best-effort chat turn-cards. Depends on: Phase 5 Requirements: GUID-01, GUID-02, GUID-03, GUID-04, GUID-05, GUID-06, PX-01, PX-03 Success Criteria (what must be TRUE):
- During a drive, the user sees a prominent next-maneuver banner (distance + arrow + street name) with a collapsible full maneuver list (GUID-01), ETA and remaining distance progressing with each location fix without visible whiplash (GUID-03), and a haptic cue at maneuver points (GUID-04)
- After a missed turn, the app silently recalculates from the current position — no asking, no blaming — and reroute storms are held back by the debounce/hysteresis state machine (GUID-02, verified via recorded-drive replays plus a real drive)
- Minimizing the app mid-drive switches the bot to pushing chat turn-cards (map snapshot + next maneuver + ETA, best-effort projection) for the remainder of the trip (GUID-05, served by the Phase 5-designed ephemeral guidance session); re-opening mid-trip resumes live guidance seamlessly at the progressed position (GUID-06)
- The Telegram BackButton walks the full app states (search → routes → guidance → back to search) and never dead-ends (PX-01 — full state graph only exists now); closing during guidance requires passing the "abandon route?" confirmation, with safe areas respected and vertical-swipe-close disabled during guidance (PX-03)
- Ending a trip deletes the ephemeral guidance session and shows the "trip ended, data deleted" messaging — completing the in-app half of PRIV-01's promise at the trip boundary Plans: 8 plans
Plans:
Wave 1 (parallel foundations — the pure cores + the envelope going live):
- 06-01-PLAN.md — Engine A pure core: pinned CSR binary codec (with street-name table), GPS-fix snapping, typed-array A* + step/turn derivation, proven against a committed mini-graph by A*≡Dijkstra equivalence (D-01/D-05, GUID-02)
- 06-02-PLAN.md — Guidance pure core: the D-04 reroute state machine with named constants, D-07 EMA+deadband ETA, D-15 distance throttle, four committed D-16 drive fixtures + replay/eta suites (GUID-02/03/04/06)
- 06-03-PLAN.md — The frozen envelope goes LIVE: mint/get/delete TTL-only service + Bearer-gated /api/guidance POST/GET/DELETE (claim-only chat), GE4–GE7 re-pinned in the same change (GUID-05, D-14 server half)
Wave 2 (blocked on Wave 1 — the real graph + the cockpit chrome, in parallel):
- 06-04-PLAN.md — Engine A server surface: Overpass→CSR build tool + real KL graph, R2 bucket/upload/fork docs, isolate-memoized worker-router behind Bearer-gated /api/route, client http-source backend #2, RP1/RP2 re-pins, A*-CPU measurement gate (D-01/D-03, GUID-02)
- 06-05-PLAN.md — Guidance chrome per 06-UI-SPEC: committed 8-glyph set, banner/list/guidance-card/confirm/trip-end builders, close-protection + haptics capability guards, strings keys en+ms, shell.css guidance rules, guidance-surface suite + RS10/BA9 bumps (GUID-01/04, PX-03)
Wave 3 (blocked on Wave 2 — integration):
- 06-06-PLAN.md — Guidance integration: D-10 locked-follow camera, the fix pump (banner/ETA/haptics/reroute), PX-01 full BackButton state graph, minimize-POST/resume-GET/trip-end-DELETE envelope flows, RS6 re-pin (GUID-02/05/06, PX-01/03, D-14 web half)
- 06-07-PLAN.md — Cron turn-cards: pure projectTurnCards + the repo's first scheduled() handler + triggers.crons, street-bearing cards by default via graph reconstruction (street-less only as graph-absent degradation per D-03; D-12 with 06-08 Part C ratification), KV-write-budget pin, committed 8-glyph PNG set via sharp (GUID-05, D-11/12/13)
Wave 4 (the phase's closing gates):
- 06-08-PLAN.md — Verification close: ?drive= replay knob + seven guidance capture legs + DSGN-02 gallery, then the ONE REAL DRIVE device gate (blocking checkpoint) (D-16, all reqs)
Cross-cutting constraints (shared invariants enforced in multiple plans; no verbatim-duplicated truths):
- Claim-only chat identity (T-06-02): envelope chat_id derives from the Bearer claim server-side — never a request body — in 06-03 (mint/read/delete), 06-06 (POST composition), 06-07 (cron push target)
- Fail-honest degradation (D-03): absent/tampered graph → null → router-pending (06-01 decode, 06-04
/api/route, 06-07 street reconstruction); never a throw, never a wrong answer - TTL-only KV (GE1): every envelope write carries
expirationTtl: 7200; active DELETE on trip end (06-03 service, 06-06 flows, 06-07 zero-write cadence) - Privacy logging: category-only logs, never coordinates/chat ids in output (06-03, 06-06, 06-07, 06-08 capture assertions)
Notes:
- Highest behavioral uncertainty (reroute triggers, ETA smoothing, battery) — research recommends
/gsd-research-phase(backgrounded turn-card "best-effort projection" framing + static map snapshot generation; product decision needed for what "good enough" means minimized). - Schedule slack budgeted here per research; backgrounded turn-cards are folded into this phase per roadmap instructions with the session contract pre-designed upstream (Phase 5). UI hint: yes
Phase 7: Hardening + Private Launch
Goal: Real KL drivers get an honest, robust experience: every failure state designed, the privacy promise holding under audit, performance within free-tier budgets, and a known-limitations document that tells the truth — then the launch. Depends on: Phase 6 Requirements: PX-04 Success Criteria (what must be TRUE):
- Guidance supports fullscreen and remains usable in landscape (driving posture) across the full real device matrix — Android, iOS, Desktop, browser (PX-04) — folded into the phase's device-matrix QA pass including the battery drain target (<10%/hr during guidance)
- Every empty/error/timeout/off-road/no-geometry state renders gracefully (no blank screens, no broken layouts); version-gating fallbacks behave sanely on older Telegram clients
- Privacy verification passes: an architecture audit shows no persistent per-user location history or profile anywhere — coordinates exist only in TTL-expiring KV, and logs carry no coordinate data (re-verifies PRIV-02, GEO-03 end-to-end)
- The golden-route regression pass is green, Worker budgets audit clean (subrequests/CPU, cache hit-rate), and the "known limitations" doc (OSM data gaps, no traffic confidence, no lane guidance) ships before the first real drivers Plans: 6 plans
Plans:
Wave 1 (four parallel plans — no file overlap):
- 07-01-PLAN.md — Fullscreen capability layer: the Telegram 8.0-guarded
requestFullscreen()wrapper, the grownTelegramWebAppLikecontract, the safe-area re-resolution on inset events (WIRED at theweb/src/main.tsboot site), the route controller's guidance-start/trip-end wiring + the fullscreen-event watcher, and the FS1–FS9 suite (PX-04, D-03) - 07-02-PLAN.md — Landscape geometry as CSS-only media queries (tokens binding), the FM1–FM7 failure-state matrix (empty/error/timeout/no-geometry/off-road/old-client/location-timeout), and the landscape capture legs (PX-04, SC2)
- 07-03-PLAN.md — The consolidated
npm run privacy-audittool: source gates over ALL src trees, the KV TTL gate, the category-only log gate, the positive fetch-host allowlist, the production-bundle scan, the live KV expiry probe, and the signed audit record (D-01, D-02) - 07-05-PLAN.md — The parked debts paid: IN-01's dead export + intent comments, IN-02's stale wrangler comment, IN-03's exact-900s boundary pin, the
/carire-arm attempt, the A1/A3 closure — plusdocs/KNOWN-LIMITATIONS.mdand the/helppointer line (D-07, D-08)
Wave 2 (blocked on Wave 1 completion — shares package.json with 07-03 and imports 07-03's live-probe library):
- 07-04-PLAN.md — The consolidated
npm run budget-audittool: the re-run A* CPU ladder, the bundle-byte budgets, the KV write classes with the cron's zero-write law, the per-request-class subrequest inventory, the re-run golden corpora, the static-asset_headerspolicy, and the checked-in current-vs-free-tier budget record (D-06)
Wave 3 (blocked on Waves 1–2 — the blocking launch gate):
- 07-06-PLAN.md — The launch gate: the consolidated 4-client × 7-interaction walk, the recorded Android battery measurement, the six-item D-09 checklist, and the milestone completion record (D-04, D-05, D-09)
Cross-cutting constraints (must_haves.truths asserted by 2+ plans, binding at every wave):
- Category-only logging — no coordinate, chat id, or route payload in any log statement, including the fullscreen path (07-01, 07-03)
- TTL-only KV write law — every write carries a named-constant
expirationTtl; the cron tick writes zero times (07-03, 07-04) - Old-client sanity — missing Bot API 8.0 methods are a designed silent no-op, never an error surface (07-01, 07-02)
- Honest measurement — every recorded number is measured from reality (CPU ladder, battery run, device legs), never synthesized or assumed (07-04, 07-06)
Notes:
- No exclusive feature requirements beyond PX-04 — this phase hardens the outcomes of Phases 1–6 and is the last chance to catch the "looks done but isn't" failures for a navigation tool whose empty states ARE the polish surface.
- The two manual behaviors (the physical device walk and the battery run) live in the 07-06 blocking LAUNCH-GATE checkpoint — never synthesized. NOT-RUN legs are recorded honestly and keep the checklist unsigned. UI hint: no
Requirements Coverage
| Phase | Requirements |
|---|---|
| 1 | DSGN-01, DSGN-02 |
| 2 | ONBD-01, PRIV-01, PRIV-02 |
| 3 | GEO-01, GEO-02, GEO-03, PX-02 |
| 4 | SRCH-01, SRCH-02, SRCH-03, ONBD-02, ONBD-03 |
| 5 | RTNG-01, RTNG-02 |
| 6 | GUID-01, GUID-02, GUID-03, GUID-04, GUID-05, GUID-06, PX-01, PX-03 |
| 7 | PX-04 |
Coverage: 22/22 v1 requirements mapped — no orphans, no duplicates. (PRIV-01's in-app trip-boundary messaging and PX-01/PX-03 groundwork are noted where they're staged, but each requirement maps to its owning phase exactly once.)
v1.x Candidates (post-v1, not roadmap phases)
Milestone v1.x — Routing Engine Upgrades + Extras (post-v1)
Goal: Pay the measured engineering debts of the v1 spine: routing that no longer degrades cross-metro, the routing extras the requirements name (toll, speed limits, live share, voice), the deferred battery record, and the client-side routing endgame. Depends on: v1 milestone (complete 2025-10-25). Ordering: engine first (user decision) — battery is cheap but follows the engine work.
Phase 8: ALT Landmark Preprocessing (Routing Backend v2)
Goal: Cross-metro routes route in-budget on the free plan: ALT (A*, Landmarks, Triangle-inequality) preprocessing over the CSR graph upgrades engine A's worst case while staying $0 — the c2 rung of the measured ladder, executed. Depends on: Phase 6 (engine A). Requirements: the routing CPU class (SC3/SC4 carry-over; no new REQ ids — the budget record's numbers improve). Success Criteria:
- Longest KL-metro routes route within the free-plan CPU budget (p95 under the ceiling, measured by the existing CPU gate)
- Route optimality unaffected (ALT is admissible — the corpus re-run stays green with identical tolerance verdicts)
- The budget record's c3 degradation class retires; KNOWN-LIMITATIONS updated Notes: graph-layout v2 with landmark distances precomputed at build time; upload script unchanged.
Phase 08.1: Contraction Hierarchies (Routing Backend v3) — the lever that closes SC-1: build-time node ordering + contraction + shortcut edges, simple uphill bidirectional query, optimality by construction (INSERTED)
Goal: [Urgent work - to be planned] Requirements: TBD Depends on: Phase 8 Plans: 5/5 plans complete
Plans:
- TBD (run /gsd-plan-phase 08.1 to break down)
Phase 9: Routing Extras (Toll / Speed / Share / Voice)
Goal: The REQUIREMENTS extras go live on the engine-A stack. Depends on: Phase 8. Requirements: RGNG-01 (toll toggle), RGNG-02 (speed-limit display where OSM maxspeed exists — "show nothing rather than wrong values"), TRIP-01 (live trip share via sendLocation(live_period) + editMessageLiveLocation with ETA; user-initiated, expires), GUID-07 (voice guidance via Web Speech API; mute-by-default; iOS webview TTS spike gates the sendVoice fallback). Success Criteria: per requirement, plus a device check per added surface; privacy laws hold (a live trip share is EXPLICITLY user-initiated and expires — no silent tracking). Notes: iOS TTS spike FIRST (GUID-07 gated on it).
Phase 10: Battery Record + Launch-Checklist Completion
Goal: The deferred launch items close: the <10%/hr battery record (measured, one real ≥30min guidance drive, method + caveats documented) and any remaining Part A/B cells. Depends on: Phase 9 (extras present in the driven surface). Success Criteria: the battery record signed in the launch gate file; KNOWN-LIMITATIONS' battery pointer retires; the launch checklist completes 6/6. Notes: smallest phase in the project; closes the loop on the honest-deferral protocol.
Phase 11: Engine B — Client-Side WASM Router + Turn-Card Snapshots
Goal: The static-hosted endgame: routing computed in the webview (graph downloaded + HTTP-cached on device; origin+destination never touch a server), RoutingSource backend #3; bitmap map snapshots in minimized turn-cards ride the same graph asset. Depends on: Phase 8 (ALT benefits the client engine too). Success Criteria:
- Routing works offline/in-webview with no Worker round-trip for the route consult (device-verified)
- Turn-cards gain real map snapshots rendered client-side before minimize
- Payload/memory budget documented honestly (download size + phone memory), privacy law re-audited (a client-side router means coordinates never leave the device — the audit's best outcome) Notes: the OSS-track project formalized; the repo's biggest single upgrade.
(Scoped into the milestone above by the user, 2025-10-25 — the v1.x candidates line is superseded.)
Progress
Execution Order: Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 (v1.0, complete) → 8 → 9 → 10 → 11 (v1.x)
| Phase | Plans Complete | Status | Completed |
|---|---|---|---|
| 1. Design Tokens + Rams Foundation | 0/TBD | Not started | - |
| 2. Bot Skeleton + initData Security | 0/TBD | Not started | - |
| 3. Mini App Shell + Geolocation | 0/4 | Ready to execute | - |
| 4. Destination Search + KL Gazetteer | 0/4 | Ready to execute | - |
| 5. Routing + Route Display | 0/TBD | Not started | - |
| 6. Turn-by-Turn Guidance (Live Driving) | 0/TBD | Not started | - |
| 7. Hardening + Private Launch | 0/6 | Ready to execute | - |
Roadmap created: 2026-10-22