Files
Arah2/.planning/research/SUMMARY.md
T
2026-10-23 22:37:55 +08:00

183 lines
21 KiB
Markdown
Raw 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.
# Project Research Summary
**Project:** araharah
**Domain:** Telegram-first privacy-respecting GPS navigation (bot + Mini App, OSM data, KL metro)
**Researched:** 2026-10-22
**Confidence:** HIGH (stack + architecture core, verified against official docs) / MEDIUM-HIGH (features) / MEDIUM (geolocation client quirks, KL data quality, backgrounded turn-card UX)
## Executive Summary
araharah is a turn-by-turn car navigation assistant delivered entirely inside Telegram: a bot handles search and launch, a Mini App (webview) renders the Rams-designed guidance HUD, and the map/routing data comes from the OSM ecosystem. The research converges on an unusually crisp shape for v1: **grammY on a single Cloudflare Worker** (webhook + `/api/*` + static Mini App assets via Workers static assets — not legacy Pages), **MapLibre GL JS v6 + OpenFreeMap** vector tiles styled from a forked `liberty` style JSON into the Telemetry Rams palette, and **self-hosted Photon + OSRM on one small VPS** fed by a Geofabrik Malaysia extract. Storage is **Cloudflare KV with TTLs only — zero databases, zero queues** in v1. Privacy is not a bolt-on; the client-authoritative guidance architecture (server never tracks position, everything ephemeral) makes the brand promise true by construction. Every public/community OSM service (OSRM demo, photon.komoot.io, public Nominatim) is explicitly non-production and throttles or forbids exactly this traffic pattern — self-hosting a country extract is cheap (tens of GB, ~€5–10/mo) and the only privacy-consistent option.
The recommended build order follows the strict dependency funnel: design tokens first (design fidelity is the user-flagged #1 risk), then bot skeleton, Mini App shell + geolocation, destination search (KL landmark quality is the biggest data experiment), routing, then live guidance, then hardening. Two features define "is this real navigation": **KL-accurate landmark search** ("KLCC" must resolve) and **missed-turn auto-recalculation** — both get dedicated research and test-corpus treatment.
Key risks, in priority order: (1) **design-fidelity death by translation** — the Tailwind-CDN mockups must be distilled into a tokens package with screenshot-diff acceptance against the mockups, never normalized into a generic component library; (2) **Telegram WebView geolocation quirks** — `LocationManager` (Bot API 8.0+) is the primary path, with Android stale-fix bugs coded around and a chat-side `request_location` fallback for Desktop; (3) **turn-by-turn engineering complexity** — snapping jitter, recalculation storms, ETA whiplash, and battery drain all need an explicit guidance state machine with hysteresis and debounce; (4) **KL OSM data quality** — patchy `maxspeed`, weak `turn:lanes`, inconsistent interchange modeling; scope the quality bar honestly and validate against a golden-route corpus; (5) **ops-stack scope creep** — RabbitMQ/PostGIS/Grafana/Prometheus from IDEAS.md are explicitly deferred with trigger conditions.
## Key Findings
### Recommended Stack
The viable 2026 stack is already assembled from cloud-native + OSM parts (full detail in STACK.md). Total infra cost ≈ €5–10/mo VPS + $0 Cloudflare.
**Core technologies:**
- **Telegram Bot API 10.3 + grammY 1.46** (`@grammyjs/conversations`) — webhook mode on Workers, secret-token validation, KV-backed sessions
- **Cloudflare Worker (single deployable, static assets)** — bot webhook + `/api/*` (Hono) + Mini App SPA in one `wrangler.jsonc` unit; Pages is legacy per Cloudflare's own docs (this resolves the Pages mention in earlier project docs)
- **telegram-web-app.js + LocationManager** — initData identity, persistent geolocation grant on mobile, BackButton/theme/haptics/fullscreen platform glue
- **Vite ^8 + TypeScript + MapLibre GL JS ^6.15 + OpenFreeMap** — no SPA framework; <200 KB gzip budget; custom style JSON = the Rams look; requires explicit `setWorkerUrl` wiring in bundlers
- **Self-hosted Photon** (GraphHopper Malaysia dump, ~<5 GB) — the only free geocoder with native typeahead + typo tolerance; behind a Worker proxy with KV caching
- **Self-hosted OSRM v26.10** (GHCR image, not stale Docker Hub) — car routing with `steps=true`, alternatives, single-digit-ms latency on a Malaysia extract
- **Cloudflare KV** (2 namespaces: `chat_sessions` minutes-TTL, `guidance_sessions` ~2h TTL) — the only storage in v1
**What NOT to use:** OSRM demo server, public Nominatim (1 req/s cap, forbids autocomplete), photon.komoot.io in production, Google/Mapbox/HERE, Neon/PostGIS/RabbitMQ/Prometheus/Grafana/Resend in v1, MapTiler as primary (non-commercial free tier + forced logo), heavy SPA frameworks, MapLibre ≤ v4.
### Expected Features
Full prioritization in FEATURES.md. The funnel has two layers: navigator table stakes + Telegram-native table stakes.
**Must have (v1 / table stakes):**
- `/start` onboarding + commands; free-text destination search → inline keyboard results → Mini App launch with destination via `startapp`
- KL-accurate search: curated landmark/alias gazetteer (~200 entries) + Malay abbreviation normalization ("Jln"↔"Jalan") layered over Photon, KL-bounded
- `sendVenue` destination card; geolocation via LocationManager + chat `request_location` fallback
- Route draw + 2–3 alternative cards (Rams-quiet); turn-by-turn HUD (next-maneuver banner + collapsible list)
- **Missed-turn detection + auto recalculation** — the "is this real navigation" bar
- ETA + remaining distance progression (honest, self-paced — no live traffic claims)
- Haptic turn cues; Telegram platform hygiene (BackButton, closing confirmation during guidance, `disableVerticalSwipes`, theme integration, safe areas)
- Visible privacy statement in-product (`/privacy`, "trip ended, deleted" confirmation)
**Should have (v1.x differentiators):**
- Backgrounded chat turn-cards (the marquee differentiator — guidance that lives in the chat; needs ephemeral server-side guidance session + static map snapshots; framed as best-effort projection, never promised as real-time)
- Voice guidance (mute-by-default, iOS webview TTS reliability risk), live-trip share via `sendLocation(live_period)`, toll-free route toggle, speed-limit display where OSM `maxspeed` exists
**Defer (v2+):** opt-in anonymized crowd speeds → traffic-weighted ETA (v1 ships only an architecture stub); incident reports (per PROJECT.md scope); gamification; AES camera layer (legal gray area).
**Anti-features (explicitly rejected):** ads/sponsored pins, gamification, re-engagement pushes, accounts/sign-up (initData is all), fat settings menus (max 3 route choices: fastest / toll-free / shorter), offline maps product, AI-assistant chat.
### Architecture Approach
One Cloudflare Worker serves everything; the Mini App owns the guidance state machine; the server stays stateless and ephemeral (full detail in ARCHITECTURE.md).
**Major components:**
1. **Bot/API Worker** — webhook intake, command router, search orchestration, geocoder/router proxies, chat turn-card pushes, secret custody. Both bot and Mini App call the identical `geo/` module.
2. **guidance_sessions (KV)** — the only "database": who's driving where, ~2h TTL, auto-purged. Everything longer does not exist in v1.
3. **Mini App (static SPA)** — map render (MapLibre + Rams style), guidance state machine `idle → routing → guiding ⇄ rerouting → arrived`, off-route detection local (snap to route polyline, >50m for >10s with hysteresis), recalculation is a client decision.
4. **Geocoder/Router boundaries** — TS interfaces in the Worker; self-hosted Photon/OSRM behind them, demo servers allowed only in local dev via config flag; PostGIS/Valhalla slot in later without rewrites.
**Key patterns:** webhook with secret path; initData → HMAC-validated session token exchange (Pattern 2); `start_param` destination handoff via short KV id; client-authoritative guidance (anti-pattern: server-driven turn-by-turn — webview JS pauses when minimized); Tailwind v4 preset + tokens package generated from mockup YAML, mockups as screenshot acceptance criteria only. Monorepo: `apps/worker`, `apps/web`, `packages/design-tokens`.
### Critical Pitfalls
Top 5 (full 10 + checklists in PITFALLS.md):
1. **Trusting `initDataUnsafe` / client-side validation** — HMAC with key `= HMAC(bot_token, "WebAppData")` (not the raw token), sorted `\n`-joined data-check string, `auth_date` freshness, one-time session-token exchange. Build the validator as the first backend artifact with CI vectors + tampered payloads.
2. **Silent geolocation death in Telegram WebView** — `navigator.geolocation` can fail with code 1 and no prompt in clients lacking the permission bridge. LocationManager primary, version-gated, `navigator.geolocation` fallback, chat `request_location` universal fallback, denied-screen; tested on a real Android/iOS/Desktop/browser matrix as the location slice's exit criterion. Android LocationManager stale-fix bug → combine both sources via one `getLiveData()` wrapper.
3. **Turn-by-turn engineering traps** — snapping (project GPS onto route polyline), reroute debounce/cooldown (≥5–8s, one OSRM call per off-route event), ETA smoothing (windowed speed, no visible whiplash), adaptive `watchPosition` sampling + Wake Lock only during active guidance; validate with recorded-drive replays in CI.
4. **Design-fidelity erosion** (user's #1 risk) — extract tokens from mockup HTML before any screen; no default-styled component libraries; per-UI-phase side-by-side mockup-vs-screenshot approval artifact; reject refactors touching spacing/color/typography without a mockup diff.
5. **KL search quality + OSM data gaps** — Nominatim-style failures on "KLCC"/"Jln"/"DUKE" queries kill the marquee feature; the curated alias gazetteer is the substance of the search phase, not polish. OSM Malaysia has patchy turn-lane/flyover data → golden-route test corpus (10–20 real KL pairs), honest quality bar, no lane-guidance claims.
Also load-bearing: Cloudflare Worker limits (free plan: 10ms CPU / 50 subrequests — cache-first, one intent = one small invocation, ≤6 subrequests worst-case, measured via `wrangler tail`), and ops-scope-creep discipline (every v1 service maps to a shipped requirement).
## Implications for Roadmap
Based on combined research, a 7-phase skeleton (Pitfalls assumed P1–P7 roles; Architecture's P0 maps to Phase 1).
### Phase 1: Design Tokens + Component Foundation
**Rationale:** Design fidelity is the flagged #1 risk; tokens are zero-risk work every later phase consumes. Building UI first and tokens later is the single most likely way to lose the Rams look. (Architecture P0; Pitfalls P1.)
**Delivers:** `packages/design-tokens` (exacted hexes, 8px grid + documented exceptions, fonts self-hosted as woff2 subsets), Tailwind v4 preset, ~15 hand-rolled Rams components, user-approved side-by-side gallery-vs-mockup artifact, map style JSON base (forked OpenFreeMap `liberty` restyled to Rams).
**Addresses:** visible platform for all FEATURES P1 UI items. **Avoids:** Pitfall 9 (fidelity death by translation).
### Phase 2: Bot Skeleton + initData Security
**Rationale:** The bot is how users reach the Mini App; the `startapp` handshake needs a real bot to test against. The initData validator must be the first backend artifact. (Architecture P1; Pitfalls P1–P2.)
**Delivers:** Worker deployed with webhook + secret path, `/start` + command menu, grammY + KV conversations, initData HMAC validator with CI vectors, session-token exchange, `/privacy`.
**Uses:** grammY, Hono, KV, wrangler. **Avoids:** Pitfall 1 (identity spoofing) — tampered/stale-payload tests gate the phase.
### Phase 3: Mini App Shell + Geolocation
**Rationale:** Session/auth + geolocation plumbing precedes any geo-service consumption; this is the riskiest platform-integration phase and its unknowns (WebView quirks, permission bridges) must surface early — the exit criterion is reading a live position on a real phone across the client matrix. (Architecture P2; Pitfalls P3–P4.)
**Delivers:** telegram-web-app.js shell (`ready`/`expand`, viewport handling — never `100vh`, BackButton wired to router, CloudStorage for tokens, theme vars), init/session flow, LocationManager + fallbacks (`getLiveData()` wrapper, denied screen, manual map-origin fallback), map rendering user position on styled basemap, boot with empty `start_param`.
**Avoids:** Pitfalls 2 (silent geolocation death) and 4 (localStorage/viewport/back-button traps).
### Phase 4: Destination Search (Geocoder + KL Gazetteer)
**Rationale:** Nothing user-meaningful exists until search resolves; search quality is the data-correctness experiment with the most unknowns — a Photon bake-off belongs here (budgeted explicitly, per Architecture gap #2). (Architecture P3; Pitfalls P5.)
**Delivers:** VPS provisioned with self-hosted Photon (GraphHopper MY dump) behind Worker proxy + KV cache; free-text bot search → inline keyboard results; curated KL landmark/alias gazetteer (~200 entries) + Malay abbreviation normalizer; `/api/search`; real-KL query acceptance set ("KLCC", "Sunway Pyramid", "Jln", "DUKE").
**Addresses:** search table stakes. **Avoids:** Pitfall 5 (Nominatim rate walls + landmark weakness).
### Phase 5: Routing Service + Route Display
**Rationale:** Full search → route → render loop lands here — the "product exists" milestone. Self-hosted OSRM must stand up *before* guidance makes routing latency-sensitive and chatty. Route model must carry step geometry + snapped-progress support from day one (Pitfall 3 pre-work). (Architecture P4; Pitfalls P6–P7, P8.)
**Delivers:** Self-hosted OSRM (GHCR image, MY extract, preprocess pipeline in Docker Compose); Router interface with demo-server dev-only config; route computation with `steps=true` + alternatives; Mini App route-choice screen (2–3 quiet Rams cards); static map snapshot renderer groundwork; Golden-route corpus (10–20 real KL pairs) + subrequest-budgets instrumentation.
**Addresses:** route display + alternatives table stakes. **Avoids:** Pitfalls 6 (demo-server dependency), 7 partially (golden routes), 8 (Worker limits).
### Phase 6: Turn-by-Turn Guidance (Live Driving)
**Rationale:** Builds on every prior piece and carries the most behavioral unknowns (reroute triggers, hysteresis tuning, ETA smoothing) — schedule slack here. Hybrid chat turn-cards may be its own follow-up phase or folded in; backgrounded turn-card framing (best-effort projection) needs a small product decision + live experiment. (Architecture P5; Pitfalls P3.)
**Delivers:** Client guidance state machine (snapped progress, off-route hysteresis, debounced reroute), maneuver banner + collapsible list, ETA progression, haptics, Wake Lock handling, `heartbeat` TTL endpoint, chat turn-cards (best-effort projection while backgrounded), closing confirmation during guidance.
**Addresses:** missed-turn recalculation, ETA, haptics, backgrounded cards. **Avoids:** Pitfall 3 (recalc storms/ETA whiplash/battery).
### Phase 7: Hardening + Private Launch
**Rationale:** The empty/error/permission states ARE the polish surface of a navigation tool; a named hardening phase (Architecture P6, Pitfalls pre-launch P7) enforces the "Looks Done But Isn't" checklist before real drivers.
**Delivers:** Timeout/off-road/no-geometry states, version-gating fallbacks, budgets audit (subrequests/CPU, cache hit-rate), battery drain QA (<10%/hr), full device matrix pass, privacy verification (no coordinates in logs), golden-route regression pass, known-limitations doc.
**Avoids:** Pitfalls 7 (data honesty), 8 (limits), plus the "looks done but isn't" checklist.
### Phase Ordering Rationale
- Strict dependency funnel: tokens → bot/auth → shell/location → search → route → guidance → hardening; each phase produces something end-to-end verifiable.
- Phase order deliberately surfaces unknowns early where they're cheap: WebView geolocation (Phase 3) and KL search quality (Phase 4) are the two experiments with the most uncertainty.
- v1.x features (voice, live-trip share, toll toggle, speed limits) hang off validated routing+guidance and should not enter the 7-phase spine until the core proves out — plan a v1.x milestone after user validation.
- Apparent conflict resolved: PROJECT.md/IDEAS.md say "Cloudflare Pages" for hosting; both ARCHITECTURE.md and STACK.md converge on **Workers with static assets** (one deployable, Cloudflare's directed path for all new projects). Recommend recording this as a Key Decision.
### Research Flags
Phases likely needing `/gsd-research-phase` during planning:
- **Phase 3 (Shell + Geolocation):** client-matrix quirks, Android LocationManager stale-fix workaround, browser-based testing harness — MEDIUM-confidence community findings.
- **Phase 4 (Search):** Photon-vs-alternatives KL bake-off is a budgeted data experiment; gazetteer sourcing/structure.
- **Phase 6 (Guidance):** backgrounded turn-card product framing ("best-effort projection"), static map snapshot generation, voice guidance TTS reliability (if Phase 6.x).
Phases with standard patterns (skip research-phase, use established docs):
- **Phase 1 (Design tokens):** internal asset work; referenced mockups are the spec.
- **Phase 2 (Bot + auth):** grammY + Cloudflare Workers hosting is first-class documented (official tutorial covers the exact setup verbatim).
- **Phase 5 (Routing/OSRM):** standardize on documented preprocess pipeline; no exotic APIs.
## Confidence Assessment
| Area | Confidence | Notes |
|------|------------|-------|
| Stack | HIGH | Core versions verified against registries/official docs 2026-10-22; explicit "why not" for every rejected alternative; MEDIUM for geolocation workarounds + snapshot path |
| Features | MEDIUM-HIGH | Telegram platform details HIGH (official docs); competitor/UX findings MEDIUM; KL-local conventions MEDIUM (some single-source, flagged) |
| Architecture | HIGH | Core verified against official Telegram/grammY docs; two flagged platform-behavior gaps (turn-card UX, Photon KL quality) MEDIUM |
| Pitfalls | HIGH (security/platform) / MEDIUM (nav-engine UX, KL data quality) | Official sources for security/limits; navigation-engineering practice synthesized, community-corroborated |
**Overall confidence:** HIGH on the shape of the system; MEDIUM on KL-specific data quality and phone-in-hand platform behavior — both have concrete experiments baked into the phase plan.
### Gaps to Address
- **Backgrounded turn-card acceptability:** what "good enough" means for minimized-app hints is a product decision requiring a small live experiment — resolve during Phase 6 planning.
- **Photon KL search quality:** untested against real KL queries (abbreviation, mall, subdistrict phrasing) — Phase 4 must budget a bake-off with a fallback (self-hosted Nominatim or hand-seeded alias file escalation).
- **Voice guidance on iOS WKWebView:** `speechSynthesis` reliability LOW confidence, single-source inference — phase-specific verification before planning voice features (v1.x).
- **OSM maxspeed completeness per KL arterial / AES camera availability:** LOW — defer speed-limit display until data is sampled; AES stays in v2.
- **Bot API Rich Messages (`InputRichBlockMap`, 10.2):** potential native chat map blocks; flagged watch-only, not a v1 dependency.
- **Bot API "Serverless" (June 2026):** noted but not recommended; watch-only.
- **Exact `initData` validation API shape at grammY 1.46:** code-verified HIGH for mechanics, verify exact typings at Phase 2 start.
## Sources
Aggregated from STACK.md, FEATURES.md, ARCHITECTURE.md, PITFALLS.md (full source lists with per-source confidence in each file).
### Primary (HIGH confidence)
- Telegram Bot API / Mini Apps official docs (core.telegram.org) — Bot API 10.3, LocationManager, initData HMAC recipe, BackButton/viewport/CloudStorage, start_param
- OpenFreeMap official site + GitHub; MapTiler pricing/terms
- Cloudflare Workers docs (static assets, limits, migrate-from-Pages); Cloudflare Pages pricing
- Nominatim usage policy (OSMF); OSRM demo-server policy; OSRM v26.10.0 / Valhalla 3.9.1 releases; Docker Hub staleness (GHCR is current)
- npm registry version checks (grammy, maplibre-gl, wrangler, hono, vite, zod, turf) 2026-10-22
### Secondary (MEDIUM confidence)
- Telegram-Mini-Apps / tma.js issue trackers (Android LocationManager stale fix, Desktop geolocation gap, viewport quirks)
- OSM Malaysia quality studies; OSM wiki addressing/tagging
- grammY community walkthroughs (Workers + KV/DO patterns); AdsGram TMA mistakes write-up; Photon/Nominatim ecosystem notes
- Waze/Google Maps 2025–2026 comparative reviews
- Turn-by-turn engineering practice (snapping, hysteresis, replays) — known-wisdom synthesis
### Tertiary (LOW confidence — validate at phase research)
- WKWebView `speechSynthesis` reliability; per-arterial maxspeed completeness; AES camera data in OSM
- Bot API Serverless (June 2026) maturity; exact grammY 1.46 typed API surface
---
*Research completed: 2026-10-22*
*Ready for roadmap: yes*