8.1 KiB
Phase 3: Mini App Shell + Geolocation - Context
Gathered: 2026-10-22 Status: Ready for planning
## Phase BoundaryThe Telegram Mini App boots inside Telegram, validates its session through Phase 2's /api/session gate, acquires live location reliably across Android/iOS/Desktop/browser with a full fallback chain (Telegram-native → WebView → chat keyboard), and renders the user's position on the Rams basemap under Telegram light + dark themes. Every failure surface is deliberate (never blank); no coordinate persists anywhere. BackButton + safe-area groundwork land here; the observable PX-01/PX-03 behaviors are Phase 6's. Search/routes/guidance are later phases.
## Implementation DecisionsMini App stack + repo layout
- D-01: Frontend is TypeScript + Vite, no UI framework — MapLibre GL JS v6 bundled, interactivity via small focused modules (the mockups' pattern, productionized). This stack is the foundation Phases 4–6 build on.
- D-02: App source lives in a
web/npm workspace besidetheme/andbot/; root package.json gainsweb:*delegate scripts. Theme is consumed as a build-time dependency (theme/dist/rams.cssimported into the Vite build, not via CDN) — completing Phase 1's self-hosted/CDN-free decision for the app path. - D-03: Post-initData validation, the session token is held memory-only (module scope), never written to localStorage/sessionStorage/cookies. Revalidation happens on every fresh boot (Telegram revives webviews) and whenever the 900s TTL stalls mid-session. Research may stress-test this invariant across webview reload quirks, but the invariant itself is locked.
Location acquisition strategy
- D-04: Primary chain: Telegram-native
WebApp.getLocation()first (Android LocationManager with version-gated paths), fall back tonavigator.geolocationinside the WebView, then the chat-keyboard "send location" fallback (bot-side; app consumes it). Research details the Android stale-fix and iOS WKWebView quirks — the CHAIN is locked, the per-link mechanics are research's. - D-05: Cadence is gated streaming: one-shot fixes during browsing/search; continuous watch only during guidance (Phase 6 builds the streaming state machine). Map recenters on each fix; the position marker always shows the freshest fix.
- D-06: Denied/no-geolocation presents a deliberate Rams-styled denied state — explains why location matters, offers retry, points to Telegram's attach → Location path as the manual fallback (GEO-02). No silent banners.
- D-07: Coordinates live in memory only while the app runs. Zero persistence across every storage API (localStorage, sessionStorage, cookies, IndexedDB, service-worker caches). This zero-persistence assertion is part of the phase's GEO-03 spot-check, stress-tested by research against every storage surface the app touches.
Shell UX + screens
- D-08: Phase 3's screen surface is map-first and single: full-bleed Rams basemap, position dot, follow/recenter control, minimal status strip reflecting session/location state. Route lines, layer toggles, search furniture belong to later phases — nothing placeholder-shaped.
- D-09: Boot choreography has staged deliberate states: validating session → acquiring location → live — each Rams-styled, none blank (extends the neutral-denied rule from PX-02).
- D-10: Map survives theme flips Rams-constant (Phase 1 D-04 slots pay off); only shell surfaces re-theme via the Telegram theme params. Safe-area handling + BackButton wiring land here as infrastructure.
Dev/test harness
- D-11: A Mock-Telegram harness for the daily loop: a local module injecting a fake
window.Telegram.WebApp(dev-signed initData, BackButton events, theme params with light/dark switch, sample location streams) plus a runner page booting the real app off-Telegram in a plain browser. Real-device batteries are spent at the device-matrix exit gate, not during daily dev. - D-12: Local dev topology is wrangler dev on 8787 serving web/dist statics +
/api/*+ webhook with local KV simulation; remote staging used sparingly right before real-device verification sessions.
Worker static-asset wiring
- D-13: Statics served by the Workers
[assets]binding (wrangler config), fromweb/dist's build output; Worker fetch still owns/api/*+ webhook first. Completes the one-deployable architecture from the roadmap. - D-14: Build inlines everything the app needs: CSS from theme, MapLibre, basemap style JSON (from
theme/basemap/liberty-rams.json), self-hosted fonts copied at build time intoweb/dist. Tiles remain OpenFreeMap's endpoint at runtime; no CDN fallbacks. - D-15: Deployment stays one command (
npm run deploy= build web → wrangler deploy with assets). Same ergonomics as Phase 2. Hashed filenames from Vite play naturally with Cloudflare's asset caching.
OpenCode's Discretion
- Exact per-link mechanics of the location chain (Telegram API calls, permission bridging) — research's domain
- Boot-state copy, exact mock initData signing mechanics, recenter-toggle visual details (within the Rams system)
assetsconfig tuning (binding name, not_found_handling, asset prefixes)- How the reserved exit-gate Playwright matrix extends theme/scripts/screenshot.mjs
- The app should feel like the navigate_map_overview mockup distilled: calm, map-dominant, nothing decorative.
- Allowed fallbacks compose as a chain, not a menu — user should perceive one honest app.
- Mock harness borrow: dev initData signing already exists conceptually in bot/scripts (HTTP world), and the hand-rolled test runner pattern (theme) prevails.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Phase scope + decisions
.planning/ROADMAP.md§Phase 3 — goal, 5 success criteria incl. device-matrix exit, research recommendation.planning/REQUIREMENTS.md§GEO-01/02/03, PX-02 (this phase's requirements).planning/phases/02-bot-skeleton-initdata-security/02-CONTEXT.md— identity floor /api/session contract details (opaque token, 900s TTL, rotation).planning/phases/01-design-tokens-rams-foundation/01-CONTEXT.md— theme package contract (Rams-constant map, D-04 theme slots)
Codebase
theme/dist/rams.css,theme/src/components.css,theme/src/tokens.css— consumption surfacetheme/basemap/liberty-rams.json— the map style to loadbot/src/webhook.ts,bot/src/kv/sessions.ts— the /api/session contract the web app callsstitch_minimalist_route_navigator/navigate_map_overview/code.html— layout reference (read-only)AGENTS.md— repo conventions
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
theme/dist/rams.css+components.css— the app imports these directly (build-time)theme/basemap/liberty-rams.json+build.mjs— map style + derivation upstream of a loaded style objecttheme/fonts/*self-hosted woff2 — copied at build time into web/disttheme/scripts/screenshot.mjs— Playwright toolchain for exit-gate device-matrix extension- Phase 2's
/api/session— the auth endpoint, deploy scripts + .dev.vars conventions
Established Patterns
- npm workspaces with delegate scripts (theme/, bot/)
- Hand-rolled node test runner (audit.test.mjs shape)
- TDD discipline; zero-persistence privacy invariant (KV TTL-only)
Integration Points
- web/dist → wrangler
[assets]binding on the existingaraharah-botWorker WebApp.getLocation()/ navigator.geolocation → module that also feeds Phase 5's route snapping and Phase 6's guidance loop- BackButton jumps to full state graph in Phase 6
</code_context>
## Deferred Ideas- Scripted Playwright device-matrix spec (mock-based) — extend theme/scripts toolchain at the exit gate
- Placeholder-shaped search drawer / route furniture — later phases by design
- Real-device TestFlight/Play Beta channels — out of scope; device-matrix exit pass suffices for v1
Phase: 03-mini-app-shell-geolocation Context gathered: 2026-10-22