# MapMap Nav — architecture, contracts, task plan (Phase 1 scaffold)

## Architecture decision

One Expo app (SDK 57, dev client, TypeScript) + one **local Expo module**
`modules/mapmap-react-native` wrapping the native SDKs (Swift `MapMapKit` +
`MapMapValhalla` from github.com/Mapmapai/mapmap-ios 0.2.0; Kotlin
`ai.mapmap:core:0.2.0` from GitHub Packages). Map rendering stays in JS via
`@maplibre/maplibre-react-native` fed by a style JSON the module returns
(local territory tiles → fully offline). All navigation logic lives in the
native SDKs; the bridge is a thin async facade + event stream — no business
logic in JS beyond UI state. Alternative considered: WebView + web SDK —
rejected (not native turn-by-turn, no offline story, not what a client demo
must prove). Screen navigation: react-navigation native stack; app modes as
an explicit state machine (EXPLORE→SEARCH→PREVIEW→GUIDANCE→ARRIVED,
Settings/Territories/About pushed).

## Bridge contract (source of truth: `modules/mapmap-react-native/src/MapmapNav.types.ts`)

The types file IS the contract — builders implement exactly it. Native
module name: `MapmapNav`. All methods Promise-based; continuous data flows
as events. Payloads cross the bridge as JSON-serialisable objects only.

Events: `territoryProgress`, `guidance`, `voiceState`, `bridgeError`
(schemas in the types file). Guidance events ≥1Hz during a session, plus
one per state transition (banner change, reroute start/end, arrival).

Error model: every rejected promise carries `code` (stable string, e.g.
`E_TERRITORY_ALLOWANCE`, `E_NO_ROUTE`, `E_NOT_INITIALISED`) + `message`.
Builders: if the native SDK cannot honour a contract item, STOP and
report — do not redesign the interface silently.

## Territory acquisition

`installTerritory` drives the SDK's signed update path (check/apply against
the channel index) with an app-supplied fetcher carrying
`Authorization: Bearer <key>`; the core verifies signatures itself. Known
risk (log as FINDING if hit): UK package ≈3 GB vs 2 GiB free-tier download
allowance → expect `E_TERRITORY_ALLOWANCE` (402). Demo fallback: install a
smaller territory (e.g. ireland) and record the finding; UI copy stays
territory-agnostic.

## Replay corpus

No public `.drive.jsonl` corpus ships with docs (FINDING if confirmed).
End-user path: synthesise one from a computed route's geometry at realistic
speeds (module method `synthesizeDriveCorpus(routeIndex, path)` NOT in
contract — do it in JS: write JSONL of `{t, lat, lon, speed, bearing}`
samples ~1Hz; builders confirm exact line schema from the SDK's public
DriveCorpus docs and STOP if undocumented).

## Task plan (atomic; no two tasks share a file)

| # | Task | Model | Owns | Acceptance |
|---|---|---|---|---|
| T1 | iOS bridge: SPM deps + module impl (`ios/`) | opus | `modules/mapmap-react-native/ios/**` | `npx expo prebuild -p ios && xcodebuild` compiles; init/list/compute/guidance/voice methods wired to MapMapKit |
| T2 | Android bridge (`android/`) | opus | `modules/mapmap-react-native/android/**` | `:app:assembleDebug` compiles with AAR resolved; same surface as T1 |
| T3 | Module JS API + event emitter glue | sonnet | `modules/mapmap-react-native/src/**` (except types file) | typecheck; mock-backed unit test of API shape |
| T4 | Theme + shared UI primitives | sonnet | `src/theme.ts`, `src/components/**` | typecheck; matches UI-spec brand rules |
| T5 | State machine + app shell + navigation | sonnet | `src/state/**`, `App.tsx`, `src/screens/index.ts` | typecheck; mode transitions unit-tested |
| T6 | Screens S1–S5 (splash/onboarding/explore/search/place) | sonnet | `src/screens/{Splash,Onboarding,Explore,Search,Place}*` | typecheck; spec sections S1–S5 |
| T7 | Screens S6–S8 (preview/guidance/arrived) | sonnet | `src/screens/{Preview,Guidance,Arrived}*` | typecheck; spec S6–S8 incl. re-centre chip + rerouting banner |
| T8 | Screens S9–S12 (settings/territories/about/errors) | sonnet | `src/screens/{Settings,Territories,About}*`, `src/screens/errors/**` | typecheck; spec S9–S12 |
| T9 | app.json/EAS/store config + icon/splash | sonnet | `app.json`, `eas.json`, `assets/**`, `store/**` | prebuild clean; id ai.mapmap.nav |
| T10 | Corpus synthesiser + demo glue | sonnet | `src/replay/**` | unit test: 2-point route → monotonic 1Hz JSONL |

Integration (Fable, Phase 3): key + territory acquisition, simulator/
emulator runs, recordings, FINDINGS.md, showcase pack, verdict.
