# NoteLett Mobile — Delegation Roadmap (single-agent, sequential) **Purpose:** One document to hand to an AI or human implementer. Execute **blocks in order** (later blocks assume earlier ones are merged). When a block is done, update **`docs/AGENT_TASK_ROADMAP.md`** (e.g. **Current State** table and any new mobile checklist rows), then commit and push. **Canonical product checklist:** [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md). **Repo:** `learning_ai_notes` **Mobile app root:** `mobile/` (Expo Router under `mobile/src/app/`). **Stack:** Expo ~55, React Native ~0.83, Zustand + MMKV, `@bytelyst/*` (auth-client, api-client, telemetry-client, feature-flag-client, kill-switch-client, blob-client, offline-queue, diagnostics-client). **Related pattern:** NomGap uses the same idea under `learning_ai_fastgap/docs/MOBILE_DELEGATION_ROADMAP.md` — **do not** copy fasting-specific paths or M-numbers; this file is authoritative for NoteLett. --- ## 0 — Prerequisites (before writing code) - [ ] **Install:** From `learning_ai_notes` root: `pnpm install` (workspace: `backend`, `web`, `mobile`). - [ ] **Private registry:** If `.npmrc` references `${GITEA_NPM_HOST}`, set env so installs resolve `@bytelyst/*`. - [ ] **Baseline:** `cd mobile && pnpm run typecheck` (and `pnpm test`) pass on your machine. - [ ] **Branch:** `git checkout -b feat/mobile-` (one branch per major block is fine). **Note:** This repo’s `pnpm-workspace.yaml` lists only `backend`, `web`, `mobile` — not `learning_ai_common_plat`. Platform packages typically resolve from the registry; link local plat only if your team uses that workflow. --- ## 1 — Already shipped (verify before redoing) | Area | What to verify | |------|----------------| | **Router + shell** | `mobile/src/app/_layout.tsx` — bootstraps auth, `initPlatform()`, hydrates stores | | **Entry** | `mobile/src/app/index.tsx` → `Redirect` to `/auth` | | **Auth UI** | `mobile/src/app/auth.tsx` — email/password → `useAuthStore` → `/(tabs)` | | **Auth + API** | `mobile/src/api/auth.ts` (`createAuthClient`, MMKV `storagePrefix: PRODUCT_ID`), `mobile/src/api/client.ts` (`createApiClient` + `getToken`) | | **Platform clients** | `mobile/src/lib/platform.ts` — telemetry init + `trackEvent`, feature flags, kill switch, `blobClient`, diagnostics singleton | | **Stores + API modules** | `auth-store`, `notes-store`, `workspace-store`, `inbox-store`; `mobile/src/api/notes.ts`, `workspaces.ts`, `note-agent-actions.ts` | | **Offline queue (module)** | `mobile/src/lib/offline-queue.ts` exports `noteOfflineQueue` — **UI/store wiring is still partial** (see capture screen copy) | | **Tabs + detail** | `(tabs)/` index, search, capture, inbox; `mobile/src/app/note/[id].tsx` | | **Tests** | Vitest on stores (`*.test.ts`); no `@testing-library/react-native` in `mobile/package.json` today | --- ## 2 — Execution order (mandatory) ``` Block A → Auth flow + session UX (register, refresh, guarded entry) Block B → Kill switch gate + safe degraded UI Block C → Broadcast + survey (web parity) Block D → Profile / settings + feedback-client Block E → Offline queue: enqueue + flush on boot / resume Block F → Blob / attachments (capture or note detail) via shared blobClient Block G → Code health (telemetry versions, a11y, dead code) Block H → RNTL + smoke screen tests Block I → Optional — @bytelyst/sync or deeper sync (last; needs API contract review) ``` --- ## Block A — Auth flow and session UX ### A.1 Goals - **Signed-in users** should not be forced through `/auth` on every cold start — `index` should redirect to `/(tabs)` when `useAuthStore` / `getAuthClient()` is authenticated (after `bootstrap` resolves). - Add **register** (and optional forgot-password) paths aligned with backend + web, or document why mobile is sign-in only. - Ensure **token refresh** behavior matches `@bytelyst/auth-client` expectations (no silent 401 loops on `getApiClient()`). ### A.2 Files to read first - `mobile/src/app/index.tsx`, `mobile/src/app/auth.tsx`, `mobile/src/app/_layout.tsx` - `mobile/src/store/auth-store.ts`, `mobile/src/api/auth.ts`, `mobile/src/api/config.ts` - Web reference: `web/src/lib/auth.ts`, auth pages under `web/src/app/(auth)/` ### A.3 Optional (platform SDK parity) If the team standardizes on **`@bytelyst/react-native-platform-sdk`**: either integrate **`AuthProvider`** with the **same MMKV keys** as `createAuthClient({ storagePrefix: PRODUCT_ID })`, or document a minimal bridge (same approach as NomGap Block A in `learning_ai_fastgap`). ### A.4 Done criteria - [x] Cold start: authed → tabs; unauthed → auth - [x] `cd mobile && pnpm run typecheck && pnpm test` - [x] Update **Current State** / any new checklist rows in [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`ae0a481`](https://github.com/saravanakumardb1/learning_ai_notes/commit/ae0a481) — `feat(mobile): complete block A auth session flow` --- ## Block B — Kill switch gate ### B.1 Goal Mirror web `middleware` / layout behavior: when **`checkKillSwitch()`** reports disabled, block main app UI and show message (reuse pattern from `web` or NomGap `KillSwitchGate`). ### B.2 Files - `mobile/src/lib/platform.ts` (`checkKillSwitch`) - `mobile/src/app/_layout.tsx` or a small `KillSwitchGate` wrapper component ### B.3 Done criteria - [x] Manual test with kill switch on/off (or mocked) - [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`acdedbc`](https://github.com/saravanakumardb1/learning_ai_notes/commit/acdedbc) — `feat(mobile): add kill switch gate in root layout` --- ## Block C — Broadcast + survey (web parity) ### C.1 Goal Integrate **`@bytelyst/broadcast-client`** and **`@bytelyst/survey-client`** in the root layout (banner + modal), matching API usage from web (see `web` app layout and `@bytelyst/broadcast-client` / `@bytelyst/survey-client` exports). ### C.2 Files - Add deps to `mobile/package.json` if missing - `mobile/src/app/_layout.tsx` + new thin components under `mobile/src/components/` as needed - Reference: `web/src/app/(app)/layout.tsx` (or wherever BroadcastBanner / SurveyBanner live) ### C.3 Done criteria - [x] No duplicate/wrong client API (align with current package exports) - [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`48896ab`](https://github.com/saravanakumardb1/learning_ai_notes/commit/48896ab) — `feat(mobile): integrate broadcast and survey clients` --- ## Block D — Settings / profile + feedback ### D.1 Goal Add **`@bytelyst/feedback-client`** from mobile — same payload shape as web (`submit({ type, title, body })` or current API). ### D.2 Files - New screen or tab entry: e.g. `mobile/src/app/settings.tsx` or profile section on a tab - `mobile/package.json` if adding `feedback-client` ### D.3 Done criteria - [x] Graceful offline / error handling - [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`746cba7`](https://github.com/saravanakumardb1/learning_ai_notes/commit/746cba7) — `feat(mobile): add settings tab with feedback client` --- ## Block E — Offline queue wiring ### E.1 Goal `noteOfflineQueue` exists but capture screen still says wiring is deferred. **Enqueue** failed note mutations; **flush** on app start and on connectivity / `AppState` active (match web offline-queue behavior). ### E.2 Files - `mobile/src/lib/offline-queue.ts` - `mobile/src/store/notes-store.ts` (and any create/update paths) - `mobile/src/app/(tabs)/capture.tsx` ### E.3 Done criteria - [x] Airplane-mode manual test: queue → reconnect → drain - [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`5d82160`](https://github.com/saravanakumardb1/learning_ai_notes/commit/5d82160) — `feat(mobile): wire offline queue enqueue and flush` --- ## Block F — Blob / attachments ### F.1 Goal Any mobile upload path (future file capture, artifacts) should use **`blobClient`** from `mobile/src/lib/platform.ts` — no second SAS or raw `fetch` duplicate. ### F.2 Files - `mobile/src/lib/platform.ts` (`blobClient`) - Future: note detail or capture file picker ### F.3 Done criteria - [ ] Single upload abstraction; E2E or manual upload test - [ ] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [ ] Commit: `feat(mobile): blob uploads via shared blobClient` (or `refactor` if replacing duplicate code) --- ## Block G — Code health Execute in **flexible order**; prefer **small commits**. | Task | Hints | |------|--------| | **Telemetry metadata** | Use `expo-constants` for `appVersion` / `buildNumber` instead of hardcoded strings in `platform.ts` | | **Feature flags in UI** | Gate experimental mobile surfaces with `isFeatureEnabled` where web uses flags | | **Accessibility** | `accessibilityLabel` on tab icons and primary actions | | **Theme** | Prefer shared tokens from `@bytelyst/design-tokens` / `theme/` consistently | **Done criteria** - [x] Runtime telemetry/client metadata uses `expo-constants` + platform metadata helpers - [x] Accessibility labels added on tabs and primary action buttons - [x] Typecheck/tests pass and changes reflected in [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`e4683ad`](https://github.com/saravanakumardb1/learning_ai_notes/commit/e4683ad) — `fix(mobile): complete block G metadata and accessibility` --- ## Block H — RNTL + smoke tests ### H.1 Infrastructure - [x] Add **`@testing-library/react-native`** + **`@types/react-test-renderer`** to `mobile` `devDependencies` - [x] Extend `mobile/vitest.config.ts` with resolve aliases for RN / Expo / RNTL mocks - [x] Create `mobile/__mocks__/` with mocks for `react-native`, `expo-router`, `expo-constants`, `expo-status-bar`, `react-native-mmkv`, `@testing-library/react-native` ### H.2 Minimum tests - [x] **Auth screen** — import smoke test (valid component, named export, no redirect when unauthenticated) - [x] **Home tab screen** — import smoke test (valid component, named export) ### H.3 Done criteria - [x] `cd mobile && pnpm run typecheck && pnpm test` — 32 tests pass (27 store + 5 component) - [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [x] Commit: [`5a0175f`](https://github.com/saravanakumardb1/learning_ai_notes/commit/5a0175f) — `feat(mobile): add Block H — Vitest RN mock aliases + component smoke tests` > **TODO-1:** Deeper RNTL render tests (text assertions, fireEvent) require `react-test-renderer` to produce non-null output for mocked RN host components in Node. Current smoke tests verify import + `isValidElement` instead. A future iteration could add `jsdom` environment + `@testing-library/react` as an alternative rendering path for full DOM-based assertions. --- ## Block I — Optional sync engine (`@bytelyst/sync`) **Last.** Mobile currently hydrates via **REST + Zustand**. Replacing with **`@bytelyst/sync`** requires mapping entities (notes, workspaces, inbox) to backend contracts and conflict rules — verify with backend before swapping. ### I.1 Done criteria (only if attempted) - [ ] Parity with current hydration + offline behavior - [ ] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md) - [ ] Commit: `feat(mobile): adopt @bytelyst/sync` (large; may split) --- ## 3 — After every block 1. `cd mobile && pnpm run typecheck` 2. `cd mobile && pnpm test` 3. Sync **[`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md)** 4. Commit with a clear message; push to remote 5. If block descriptions drift, update **this file** in the same PR --- ## 4 — Reference paths | Area | Path | |------|------| | Root layout | `mobile/src/app/_layout.tsx` | | Auth | `mobile/src/app/auth.tsx`, `mobile/src/store/auth-store.ts` | | Platform init | `mobile/src/lib/platform.ts` | | API | `mobile/src/api/client.ts`, `mobile/src/api/config.ts` | | Workspace definition | `pnpm-workspace.yaml` | --- ## Post-Completion Audit (March 31, 2026) Systematic review of all completed blocks (A–H) identified and fixed: - **`feedback-client.ts`** — null coercion on `getAuthToken` (commit `9d3ac06`) - **`offline-queue.ts`** — missing `Content-Type: application/json` header on flush - **`settings.tsx`** — hardcoded `platform: 'ios'` → dynamic `APP_PLATFORM` - **`notes-store.ts` / `inbox-store.ts`** — `isLoading` stuck true on API failure (commit `c96c785`) - **`_layout.tsx`** — stale `lastFlushed` banner when queue already empty (commit `a412494`) - **All screens** — missing `accessibilityLabel` on interactive elements (commit `83f4953`) - **Backend** — `trackEvent` call signature mismatch + route test `delete` mock (commit `6acd1a7`) **Remaining:** Block F (blob uploads) and Block I (sync engine) are deferred/optional. --- **End of delegation roadmap.** Point your agent at: **`docs/MOBILE_DELEGATION_ROADMAP.md`**.