- AGENT_TASK_ROADMAP: Current State + remaining gaps - MOBILE_DELEGATION: Remaining/deferred table, Block A/G/H, audit follow-up Made-with: Cursor
306 lines
15 KiB
Markdown
306 lines
15 KiB
Markdown
# 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, broadcast-client, survey-client, feedback-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-<block-name>` (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` — kill-switch gate UI, broadcast strip + survey modals, `initPlatform()`, hydrates, offline queue flush on boot + `AppState` active |
|
||
| **Entry** | `mobile/src/app/index.tsx` — waits for `hasBootstrapped`, then `/(tabs)` vs `/auth` |
|
||
| **Auth UI** | `mobile/src/app/auth.tsx` — sign-in + register → `useAuthStore` → `/(tabs)` |
|
||
| **Auth + API** | `mobile/src/api/auth.ts`, `mobile/src/api/client.ts` |
|
||
| **Platform clients** | `mobile/src/lib/platform.ts` — telemetry (versions from `app-metadata.ts`), flags, kill switch, blob, diagnostics |
|
||
| **Broadcast / survey / feedback** | `mobile/src/lib/broadcast-client.ts`, `survey-client.ts`, `feedback-client.ts`; banners + survey UI in `_layout`; feedback form on `(tabs)/settings.tsx` |
|
||
| **Stores + API modules** | `auth-store`, `notes-store`, `workspace-store`, `inbox-store`; `mobile/src/api/*.ts` |
|
||
| **Offline queue** | `mobile/src/lib/offline-queue.ts` — `flushNoteQueue`, `getNoteQueueSize`; wired from `_layout` |
|
||
| **Tabs + detail** | `(tabs)/` index, search, capture, inbox, **settings**; `mobile/src/app/note/[id].tsx` |
|
||
| **Tests** | Store tests + RN mocks + component smokes (`*.test.tsx`) — confirm counts in `mobile/package.json` / CI |
|
||
|
||
---
|
||
|
||
## Remaining / deferred (March 2026)
|
||
|
||
| # | Item | Status |
|
||
|---|------|--------|
|
||
| 1 | Hydrates only after `hasBootstrapped` + `isAuthenticated` | ✅ `_layout.tsx` |
|
||
| 2 | Telemetry **`flushTelemetry()`** on `AppState` background/inactive | ✅ `platform.ts` + `_layout.tsx` |
|
||
| 3 | Blob uploads single path | ✅ `api/blob-upload.ts` + capture screen comment |
|
||
| 4 | Extra RNTL smoke (`SettingsScreen`) | ✅ `settings.test.tsx` |
|
||
| 5 | Deeper RNTL (fireEvent / full render tree) | 🟨 Deferred — see Block H TODO |
|
||
| 6 | `@bytelyst/react-native-platform-sdk` vs discrete clients | ⏸ Optional (**A §A.3**) |
|
||
| 7 | `@bytelyst/sync` | ⏸ Block **I** |
|
||
|
||
**Architecture note:** NomGap uses **`react-native-platform-sdk`** providers; NoteLett uses **direct** platform HTTP clients — both valid until you standardize.
|
||
|
||
---
|
||
|
||
## 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 authenticated (after `bootstrap`). **Shipped:** `hasBootstrapped` + redirect in `mobile/src/app/index.tsx`.
|
||
- **Register** aligned with backend + web. **Shipped:** `auth-store.register` + auth UI paths; optional **forgot-password** still open if desired.
|
||
- 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] **Refine:** Data hydrates (notes/workspaces/inbox), broadcast/survey polling, and queue flush timers run **only when** `hasBootstrapped && isAuthenticated` — avoids pre-auth 401s
|
||
- [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 Implementation
|
||
|
||
- `mobile/src/lib/platform.ts` — `blobClient` singleton (already existed)
|
||
- `mobile/src/api/blob-upload.ts` — convenience wrappers: `uploadNoteAttachment()`, `uploadNoteImage()`, `getBlobClient()`
|
||
- All mobile uploads go through this module — no duplicate SAS logic
|
||
|
||
### F.3 Done criteria
|
||
|
||
- [x] Single upload abstraction via `blob-upload.ts`; no duplicate fetch/SAS paths
|
||
- [x] Update [`AGENT_TASK_ROADMAP.md`](./AGENT_TASK_ROADMAP.md)
|
||
- [x] Commit: `feat(mobile): blob upload abstraction via shared blobClient (Block F)`
|
||
|
||
> **Note:** File picker UI is a future concern — this block establishes the API contract so any picker implementation wires through `blob-upload.ts`.
|
||
|
||
---
|
||
|
||
## 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` |
|
||
| **Telemetry flush** | `flushTelemetry()` on `AppState` `background` / `inactive` (matches NomGap pattern) |
|
||
| **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] **`flushTelemetry`** exported from `platform.ts`; root layout flushes on background
|
||
- [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)
|
||
- [x] **Settings screen** — import smoke test (`settings.test.tsx`)
|
||
|
||
### H.3 Done criteria
|
||
|
||
- [x] `cd mobile && pnpm run typecheck && pnpm test` — component smokes include auth, home, settings
|
||
- [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`)
|
||
|
||
**Follow-up (March 31, 2026):**
|
||
|
||
- [x] **Auth-gated hydrates** — notes/workspaces/inbox + broadcast/survey polling + offline flush timers only after `isAuthenticated`
|
||
- [x] **`flushTelemetry()`** on app background/inactive
|
||
- [x] **Settings** RNTL smoke test; **capture** documents `blob-upload` for future file picker
|
||
|
||
**Still deferred:** Block **I** (`@bytelyst/sync`); optional RN **platform SDK** alignment; deeper RNTL assertions (Block H TODO).
|
||
|
||
---
|
||
|
||
**End of delegation roadmap.** Point your agent at: **`docs/MOBILE_DELEGATION_ROADMAP.md`**.
|