Files
arah2/.planning/ROADMAP.md
T
araharah-executor a7d8dbf345 docs(07): revise phase 07 plans — wire the safe-area watch, fold the corpora into budget-audit, add the subrequests row, resolve the research open questions
Checker-issue round 1 on the Phase 7 plan set:

BLOCKER (07-01) — watchSafeAreaChanges and watchFullscreenEvents were created
and unit-tested but never called in production: main.ts:74 was the only
safe-area site and was not in files_modified, and the fullscreen events were
never registered. Insets never re-resolved at runtime on a real device while
every gate stayed green.

  - web/src/main.ts joins 07-01's files_modified; task 4 step 8 wires
    watchSafeAreaChanges(getWebApp()) directly after the boot site's
    applySafeArea(resolveSafeInsets(getWebApp())) line.
  - task 4 step 6 registers watchFullscreenEvents once at the guidance-start
    edge in route.ts, with a deliberately no-op callback (events are
    observability, never correctness).
  - New grep acceptance criteria pin BOTH production call sites; two new
    must_haves key_links name them; T-07-06b now names the boot-site call.

WARNING (07-04 / VALIDATION.md) — the golden corpora were attributed to 07-04
t3, which never ran them. Folded into the budget audit: runGoldenCorpusGate()
re-runs `npm run routing:corpus` and `npm run search:corpus` as child
processes (the same shape runCpuLadderGate already uses), so the ONE
budget-audit command enforces SC4.a/SC4.b. VALIDATION.md rows now name 07-04
t2 and the budget-audit command.

WARNING (07-02 t3) — the landscape-leg snippet omitted the required setup.
The action now says to copy the existing corridor guidance leg VERBATIM
(setup stubGuidanceRoute(page, corridorModel) + guideQuery carrying dest +
fixpair + waitMode/expectGuide) and change ONLY the name, the scheme, and the
viewport; two grep criteria pin the setup and the query.

WARNING (07-04) — D-06/SC4 name subrequests but nothing measured beside the
50/request ceiling. Added runSubrequestClassGate(): a per-request-class
inventory derived from the Worker's route table (engine-A /api/route = 0
external subrequests; the cron tick's Telegram send is the only recurring
class; KV ops tracked separately against their own ceiling). Unmeasurable
counts read "not measured", never 0. docs/BUDGETS.md gains a
"## Subrequests per request class" section and the record states that the
inventory is source-derived, not a live Workers Logs metric.

WARNING (07-RESEARCH.md) — "## Open Questions" is now
"## Open Questions (RESOLVED)" and each of the five items carries its adopted
disposition; Q2/Q3 resolve to "answered by measurement at the launch gate per
D-04/D-06".

INFO (07-VALIDATION.md) — nyquist_compliant flipped to true and the sign-off
is approved 2026-10-25 after the map-row correction.

Waves, dependencies, autonomous flags, and the other three plans are unchanged.
2026-10-25 02:44:30 +08:00

31 KiB
Raw Blame History

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)

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):

  1. 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)
  2. 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
  3. A Rams-styled MapLibre basemap (OpenFreeMap liberty style forked to Rams tokens: grayscale grounding, muted land/water, prepared accent for route highlighting) renders in a browser (DSGN-01, map cartography)
  4. Design authority is singular: the duplicate inline Tailwind token blocks and the conflicting root DESIGN.md typography 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.html consuming 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.Identifier artifacts, .gitignore for 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):

  1. A new user sends /start and receives a concise explanation plus an always-available command menu (/help, /privacy) that answers correctly (ONBD-01)
  2. /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
  3. 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)
  4. 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/*); KV chat_sessions (minutes TTL) is the first namespace; the guidance_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):

  1. 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)
  2. 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)
  3. 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)
  4. No coordinate written anywhere persists: storage remains TTL-ephemeral only, spot-checkable at phase exit (GEO-03)
  5. 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 the GET /api/location relay 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 own Fix-shaped abstraction with a pluggable cadence — Phase 6 picks poll vs watchPosition); 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/location is required scope; MapLibre v6 is ESM-only and WebGL2-only (the map-less shell state is a deliberate --color-surface-dim field, 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):

  1. 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)
  2. 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)
  3. Choosing a result sends a native venue card in chat confirming the destination before navigation opens (SRCH-03)
  4. 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)
  5. 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/search workspace: the SearchSource/GazetteerEntry contract, 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.0 LICENSE and 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.md recording the measured result and the escalation lever (SRCH-01, SRCH-02)
  • 04-03-PLAN.md — the /cari command (D-01), the single-message sendVenue card 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_URL config, and the four commands.test.mjs landmines 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_param secondary) with one shared validation, the memory-only resolver, the honest "destination unavailable" state, ONBD-03's minimal addToHomeScreen/checkHomeScreenStatus hooks 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 from location.search, start_param secondary, 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 SearchSource interface as backend #3.
  • Mechanism correction (RESEARCH Pitfall 1, plan 04-03/04-04): a web_app inline button's WebAppInfo carries ONLY a url — there is no start_param field (verified against the installed grammY typings), so the deep link's plain gazetteer id travels as ?dest=<slug> in that URL and the webview reads location.search first, initData.start_param second. 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 + /search are registered together (the Phase 2 D-04 precedent of pairing /start↔/mula, /privacy↔/privasi); /pergi_ke stays 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):

  1. 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)
  2. 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)
  3. 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): the RoutingSource contract 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_sessions envelope as a TYPES-ONLY frozen contract (D-06/D-07): envelope + inline route snapshot types, the 7200 s TTL constant, the guidance:<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 null and 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_sessions KV 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):

  1. 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)
  2. 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)
  3. 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)
  4. 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)
  5. 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):

  1. 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)
  2. 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
  3. 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)
  4. 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:

  • 07-01-PLAN.md — Fullscreen capability layer: the Telegram 8.0-guarded requestFullscreen() wrapper, the grown TelegramWebAppLike contract, the safe-area re-resolution on inset events (WIRED at the web/src/main.ts boot 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-audit tool: 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-04-PLAN.md — The consolidated npm run budget-audit tool: 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 _headers policy, and the checked-in current-vs-free-tier budget record (D-06)
  • 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 /cari re-arm attempt, the A1/A3 closure — plus docs/KNOWN-LIMITATIONS.md and the /help pointer line (D-07, D-08)
  • 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)

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.
  • Wave structure: Wave 1 = 07-01, 07-02, 07-03, 07-05 (four parallel plans, no file overlap) · Wave 2 = 07-04 (shares package.json with 07-03 and imports 07-03's live-probe library) · Wave 3 = 07-06 (the blocking launch gate — depends on all five).
  • 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)

Per research: voice guidance (gated on iOS WKWebView TTS spike), live trip share via sendLocation(live_period), toll vs toll-free toggle (Valhalla use_tolls), speed-limit display where OSM maxspeed exists. These hang off validated routing + guidance — plan a v1.x milestone after user validation, do not pull into the spine above.

Progress

Execution Order: Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7

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