learning_ai_notes/docs/MOBILE_DELEGATION_ROADMAP.md
Saravana Achu Mac 85292b61dc docs(mobile): checkboxes for auth-gated hydrates, telemetry flush, tests
- AGENT_TASK_ROADMAP: Current State + remaining gaps
- MOBILE_DELEGATION: Remaining/deferred table, Block A/G/H, audit follow-up

Made-with: Cursor
2026-03-31 01:51:11 -07:00

15 KiB
Raw Blame History

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.

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.mddo 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 repos 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.tsflushNoteQueue, 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

  • Cold start: authed → tabs; unauthed → auth
  • Refine: Data hydrates (notes/workspaces/inbox), broadcast/survey polling, and queue flush timers run only when hasBootstrapped && isAuthenticated — avoids pre-auth 401s
  • cd mobile && pnpm run typecheck && pnpm test
  • Update Current State / any new checklist rows in AGENT_TASK_ROADMAP.md
  • Commit: ae0a481feat(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

  • Manual test with kill switch on/off (or mocked)
  • Update AGENT_TASK_ROADMAP.md
  • Commit: acdedbcfeat(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

  • No duplicate/wrong client API (align with current package exports)
  • Update AGENT_TASK_ROADMAP.md
  • Commit: 48896abfeat(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


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

  • Airplane-mode manual test: queue → reconnect → drain
  • Update AGENT_TASK_ROADMAP.md
  • Commit: 5d82160feat(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.tsblobClient 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

  • Single upload abstraction via blob-upload.ts; no duplicate fetch/SAS paths
  • Update AGENT_TASK_ROADMAP.md
  • 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

  • Runtime telemetry/client metadata uses expo-constants + platform metadata helpers
  • flushTelemetry exported from platform.ts; root layout flushes on background
  • Accessibility labels added on tabs and primary action buttons
  • Typecheck/tests pass and changes reflected in AGENT_TASK_ROADMAP.md
  • Commit: e4683adfix(mobile): complete block G metadata and accessibility

Block H — RNTL + smoke tests

H.1 Infrastructure

  • Add @testing-library/react-native + @types/react-test-renderer to mobile devDependencies
  • Extend mobile/vitest.config.ts with resolve aliases for RN / Expo / RNTL mocks
  • 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

  • Auth screen — import smoke test (valid component, named export, no redirect when unauthenticated)
  • Home tab screen — import smoke test (valid component, named export)
  • Settings screen — import smoke test (settings.test.tsx)

H.3 Done criteria

  • cd mobile && pnpm run typecheck && pnpm test — component smokes include auth, home, settings
  • Update AGENT_TASK_ROADMAP.md
  • Commit: 5a0175ffeat(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
  • 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
  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 (AH) 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.tsisLoading 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)
  • BackendtrackEvent call signature mismatch + route test delete mock (commit 6acd1a7)

Follow-up (March 31, 2026):

  • Auth-gated hydrates — notes/workspaces/inbox + broadcast/survey polling + offline flush timers only after isAuthenticated
  • flushTelemetry() on app background/inactive
  • 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.