# Mobile DRY Refactoring Roadmap > **Goal:** Eliminate hand-rolled platform code across all mobile surfaces (iOS, Android, React Native) by migrating to shared `@bytelyst/*` packages and SDKs. > > **Scope:** 14 mobile surfaces (iOS/Android/RN) across 8 product repos. 12 have gaps needing work. Estimated total effort: ~7.5 days. > Surfaces with no gaps: PeakPulse iOS (gold standard, 9 wrappers), MindLyst Android (Koin, all 6 SDK components). > > **Date:** 2026-03-21 > > **Prerequisite:** All shared SDKs already exist and are tested: > > - `packages/swift-platform-sdk/` — 13 Swift components, 1,870 LOC > - `packages/kotlin-platform-sdk/` — 13 Kotlin components, 35 tests > - `packages/react-native-platform-sdk/` — createRNPlatformSDK(), AuthProvider, useAuth > - 18 TypeScript client packages (`@bytelyst/*`) --- ## Table of Contents 1. [Phase 1 — ChronoMind Android: Replace Old Services with SDK](#phase-1) 2. [Phase 2 — JarvisJr Android: Wire Platform SDK from Scratch](#phase-2) 3. [Phase 3 — FlowMonk Mobile (RN): Add Missing Packages + Fix Raw Fetch](#phase-3) 4. [Phase 4 — LysnrAI Android: Migrate 6 Hand-Rolled Data Services](#phase-4) 5. [Phase 5 — NomGap (RN): Replace Raw Fetch in billing-api + photo-meal](#phase-5) 6. [Phase 6 — LysnrAI iOS: Migrate SSO, Usage, Sync, CertPinning](#phase-6) 7. [Phase 7 — Auth App Android: Wire BLAuthClient](#phase-7) 8. [Phase 8 — Auth App iOS: Add Telemetry, Flags, Kill Switch](#phase-8) 9. [Phase 9 — ChronoMind iOS + NoteLett Mobile: Minor Gaps](#phase-9) 10. [Verification Matrix](#verification-matrix) --- ## Phase 1 — ChronoMind Android: Replace Old Services with SDK **Priority:** HIGH | **Effort:** 1 day | **Repo:** `learning_ai_clock` | **Commit:** [`2060a83`](https://github.com/saravanakumardb1/learning_ai_clock/commit/2060a83) ### Context ChronoMind Android already had `platform/Config.kt` and `platform/PlatformModule.kt` (Hilt) providing all SDK clients. `LoginScreen.kt`, `SettingsScreen.kt`, and `TimerViewModel.kt` were **already migrated** to SDK clients in a prior session. The old services (`AuthService`, `TelemetryService`, `FeatureFlagService`) were dead code. `SyncRepository` used a hand-rolled `PlatformApiClient` with raw `HttpURLConnection`. ### Files to Modify - `android/app/src/main/java/com/chronomind/app/auth/AuthService.kt` — DELETE - `android/app/src/main/java/com/chronomind/app/auth/LoginScreen.kt` — UPDATE call sites - `android/app/src/main/java/com/chronomind/app/telemetry/TelemetryService.kt` — DELETE - `android/app/src/main/java/com/chronomind/app/telemetry/FeatureFlagService.kt` — DELETE - `android/app/src/main/java/com/chronomind/app/sync/SyncRepository.kt` — UPDATE to use BLSyncEngine - `android/app/src/main/java/com/chronomind/app/sync/PlatformApiClient.kt` — DELETE (replace with BLPlatformClient) - `android/app/src/main/java/com/chronomind/app/di/AppModule.kt` — VERIFY old providers removed - `android/app/src/main/java/com/chronomind/app/viewmodel/TimerViewModel.kt` — UPDATE to SDK clients - `android/app/src/main/java/com/chronomind/app/ui/screens/SettingsScreen.kt` — UPDATE AuthService refs ### Tasks - [x] **1.1 Audit call sites** — `LoginScreen.kt`, `SettingsScreen.kt`, `TimerViewModel.kt` already use SDK clients. No external references to old services. - [x] **1.2 Update AppModule.kt** — No old `@Provides` for auth/telemetry/flags existed. Updated `SyncRepository` provider to inject `BLSecureStore`. - [x] **1.3 Auth call sites** — Already migrated in prior session (`LoginScreen` → `BLAuthClient`, `SettingsScreen` → `BLAuthClient`). - [x] **1.4 Telemetry call sites** — Already migrated (`TimerViewModel` injects `BLTelemetryClient`). - [x] **1.5 Feature flag call sites** — Already migrated (`PlatformModule` provides `BLFeatureFlagClient`). - [x] **1.6 Migrate sync** — `PlatformApiClient` rewritten from `HttpURLConnection` → OkHttp. `SyncRepository` now reads auth token from `BLSecureStore` via `tokenProvider` lambda instead of manual `SharedPreferences`. - [x] **1.7 Wire KillSwitch** — `BLKillSwitchClient` injected in `MainActivity.kt` with kill switch check on app start. Shows blocking message if killed. - [x] **1.8 Delete dead code** — `auth/AuthService.kt` (328 LOC), `telemetry/TelemetryService.kt` (178 LOC), `telemetry/FeatureFlagService.kt` (89 LOC) deleted. Net: **-567 lines**. - [ ] **1.9 Build verification** — Requires Android SDK environment (corporate proxy). **TODO-1: Verify Gradle build compiles on dev machine.** --- ## Phase 2 — JarvisJr Android: Wire Platform SDK from Scratch **Priority:** HIGH | **Effort:** 1 day | **Repo:** `learning_ai_jarvis_jr` | **Commit:** [`336535a`](https://github.com/saravanakumardb1/learning_ai_jarvis_jr/commit/336535a) ### Context JarvisJr Android had no `platform/` module. It had a stub `sync/PlatformApiClient.kt` that returned `Result.success(Unit)` for everything. The app uses Hilt for DI. Full SDK wiring was needed from scratch. ### Files to Create - `android/app/src/main/java/com/jarvisjr/app/platform/Config.kt` — NEW - `android/app/src/main/java/com/jarvisjr/app/platform/PlatformModule.kt` — NEW (Hilt) ### Files to Modify - `android/settings.gradle.kts` — add `includeBuild` for kotlin-platform-sdk - `android/app/build.gradle.kts` — add kotlin-platform-sdk dependency - `android/app/src/main/java/com/jarvisjr/app/di/AppModule.kt` — remove any old stubs - `android/app/src/main/java/com/jarvisjr/app/sync/PlatformApiClient.kt` — DELETE (replace with SDK) ### Tasks - [x] **2.1 Add kotlin-platform-sdk to build** — `includeBuild` in `settings.gradle.kts` + `implementation("com.bytelyst:platform-sdk")` in `app/build.gradle.kts`. - [x] **2.2 Create `platform/Config.kt`** — `BLPlatformConfig(productId = "jarvisjr", baseUrl, platform = "android", applicationId = "com.jarvisjr.app")`. - [x] **2.3 Create `platform/PlatformModule.kt`** — Hilt `@Module` providing 7 SDK components: `BLPlatformConfig`, `BLSecureStore`, `BLPlatformClient`, `BLAuthClient`, `BLTelemetryClient`, `BLFeatureFlagClient`, `BLKillSwitchClient`. - [x] **2.4 Create LoginScreen** — New `ui/screens/LoginScreen.kt` using `BLAuthClient`. Auth gate wired in `MainActivity.kt`. - [x] **2.5 Wire telemetry** — `AgentViewModel.kt` now injects `BLTelemetryClient`. - [x] **2.6 Wire SettingsScreen** — Real user info from `BLAuthClient.authState` + logout button via `SettingsViewModel`. - [x] **2.7 Wire KillSwitch** — `BLKillSwitchClient` check on app start in `MainActivity.kt`. - [x] **2.8 Delete stub PlatformApiClient** — `sync/PlatformApiClient.kt` deleted. - [ ] **2.9 Build verification** — Requires Android SDK environment. **TODO-2: Verify Gradle build compiles on dev machine.** - [ ] `./gradlew :app:test` — all tests pass --- ## Phase 3 — FlowMonk Mobile (RN): Add Missing Packages + Fix Raw Fetch **Priority:** MEDIUM | **Effort:** 0.5 day | **Repo:** `learning_ai_flowmonk` | **Commit:** [`ce70d8c`](https://github.com/saravanakumardb1/learning_ai_flowmonk/commit/ce70d8c) ### Context FlowMonk mobile had 5 `@bytelyst/*` packages but was missing `telemetry-client`. Its `api.ts` was a hand-rolled fetch wrapper with no auth token injection. ### Current Packages (5) `auth-client`, `feature-flag-client`, `kill-switch-client`, `offline-queue`, `platform-client` ### Missing Packages (5) `telemetry-client`, `blob-client`, `design-tokens`, `api-client`, `react-native-platform-sdk` ### Files to Modify - `mobile/package.json` — add 5 missing deps - `mobile/src/lib/api.ts` — refactor to use `@bytelyst/api-client` or `@bytelyst/platform-client` - `mobile/src/lib/clients.ts` — already has `authClient`, `featureFlagClient`, `killSwitchClient`, `offlineQueue`, `platformClient`; add `telemetryClient` ### Files to Create - `mobile/src/lib/platform.ts` — centralized platform init (follow NoteLett pattern) ### Tasks - [x] **3.1 Add `@bytelyst/telemetry-client`** to `mobile/package.json`. Other missing packages (`blob-client`, `design-tokens`, `api-client`, `react-native-platform-sdk`) deferred — not actively needed yet. - [x] **3.2 Wire telemetryClient** in `mobile/src/lib/clients.ts` alongside existing 5 clients. - [x] **3.3 Inject auth tokens in `api.ts`** — `json()` helper now reads token from `authClient.getAccessToken()` and adds `Authorization` + `X-Product-Id` headers to all API requests. Typed API surface preserved. - [ ] **3.4 Build verification** — **TODO-3: Run `cd mobile && npm run typecheck` to verify.** --- ## Phase 4 — LysnrAI Android: Migrate 6 Hand-Rolled Data Services **Priority:** MEDIUM | **Effort:** 1 day | **Repo:** `learning_voice_ai_agent` | **Commit:** [`24a09a2`](https://github.com/saravanakumardb1/learning_voice_ai_agent/commit/24a09a2) ### Context LysnrAI Android had good SDK wiring (`platform/Config.kt` + `platform/PlatformModule.kt` with Hilt providing `BLTelemetryClient`, `BLFeatureFlagClient`, `BLKillSwitchClient`, `BLSecureStore`). **Note:** `BLAuthClient` is intentionally NOT in PlatformModule because `AuthViewModel` has deep LysnrAI-specific integration (DataStore, keyboard bridging, CloudSync). Audit found 11 of 13 `data/` services were **dead code** (zero external callers). ### Hand-Rolled Services to Migrate | File | Current | Target SDK | | ------------------------------ | ---------------------------- | --------------------------------------------------- | | `data/FeatureFlagService.kt` | Raw HTTP + SharedPreferences | `BLFeatureFlagClient` (already in PlatformModule) | | `data/KillSwitchService.kt` | Raw HTTP | `BLKillSwitchClient` (already in PlatformModule) | | `data/BlobService.kt` | Raw HttpURLConnection | `BLBlobClient` | | `data/LicenseService.kt` | Raw HTTP | `BLLicenseClient` | | `data/SSOService.kt` | Raw HTTP + SharedPreferences | `BLAuthClient` (OAuth flows) | | `data/UsageService.kt` | Raw HttpURLConnection | `BLPlatformClient` (generic fetch) | | `data/ProfileService.kt` | Raw HTTP | `BLPlatformClient` (generic fetch) | | `data/CertificatePinning.kt` | Custom OkHttp interceptor | Evaluate: keep or move to SDK | | `data/GuestMode.kt` | SharedPreferences | Evaluate: keep (product-specific) | | `sync/CloudSyncService.kt` | Raw HTTP | `BLSyncEngine` | | `sync/OfflineQueue.kt` | Custom queue | `BLSyncEngine` or `@bytelyst/offline-queue` pattern | | `sync/SessionSyncService.kt` | Raw HTTP | `BLSyncEngine` | | `telemetry/TelemetryClient.kt` | Raw HTTP | `BLTelemetryClient` (already in PlatformModule) | ### Tasks - [x] **4.1 Audit all data/ services for external callers** — grep found 11 of 13 services have zero external imports (dead code). - [x] **4.2 Delete dead code (11 files, -1,005 LOC)**: - [x] `data/FeatureFlagService.kt` (96 LOC) — superseded by `BLFeatureFlagClient` in PlatformModule - [x] `data/KillSwitchService.kt` (63 LOC) — superseded by `BLKillSwitchClient` in PlatformModule - [x] `data/BlobService.kt` (149 LOC) — no external callers - [x] `data/UsageService.kt` (118 LOC) — no external callers - [x] `data/ProfileService.kt` (81 LOC) — no external callers - [x] `data/SSOService.kt` (164 LOC) — no external callers - [x] `data/AuditLogger.kt` (71 LOC) — no external callers - [x] `data/BiometricAuth.kt` (79 LOC) — no external callers - [x] `data/GuestMode.kt` (75 LOC) — no external callers - [x] `data/SettingsStore.kt` (22 LOC) — no external callers - [x] `data/CertificatePinning.kt` (87 LOC) — no external callers - [x] **4.3 Kept (active callers, product-specific):** - `data/LicenseService.kt` — used by `SettingsScreen` (static calls with UI state) - `data/RuntimeConfig.kt` — used by `LysnrAIApp` + `TelemetryClient` - `telemetry/TelemetryClient.kt` — static singleton used by keyboard extension (no Hilt DI possible). **TODO-4: Migrate TelemetryClient to BLTelemetryClient when keyboard extension DI is reworked.** - `sync/` services — active callers in `RecordViewModel`, `AuthViewModel`, `SessionsViewModel` - [ ] **4.4 Build verification** — **TODO-5: Verify Gradle build compiles on dev machine.** --- ## Phase 5 — NomGap (RN): Replace Raw Fetch in billing-api + photo-meal **Priority:** MEDIUM | **Effort:** 0.5 day | **Repo:** `learning_ai_fastgap` | **Commit:** [`63bd34b`](https://github.com/saravanakumardb1/learning_ai_fastgap/commit/63bd34b) ### Context NomGap had excellent `@bytelyst/*` package adoption (18 packages). `billing-api.ts` had a hand-rolled `billingFetch()`. `photo-meal.ts` already used shared `apiPost` — raw fetch to Azure SAS URL is intentional (direct blob upload). ### Files to Modify - `src/api/billing-api.ts` — hand-rolled `billingFetch()` with manual Bearer token - `src/lib/photo-meal.ts` — raw `fetch()` to SAS URL for blob upload ### Tasks - [x] **5.1 Refactor `billing-api.ts`** — Replaced `billingFetch()` with `apiGet`/`apiPut` from shared `@bytelyst/platform-client` wrapper. Auth token + `x-product-id` now injected automatically. Net: **-19 lines**. - [x] **5.2 `photo-meal.ts`** — Already uses shared `apiPost` for SAS token + recognition. Raw `fetch` to SAS URL is intentional (direct Azure Blob upload). No changes needed. - [ ] **5.3 Build verification** — **TODO-6: Run `npm test` to verify all 505 tests still pass.** --- ## Phase 6 — LysnrAI iOS: Migrate SSO, Usage, Sync, CertPinning **Priority:** MEDIUM | **Effort:** 1 day | **Repo:** `learning_voice_ai_agent` | **Commit:** [`862e981`](https://github.com/saravanakumardb1/learning_voice_ai_agent/commit/862e981) ### Context LysnrAI iOS had the most complete Platform/ directory (9 SDK wrappers). Audit found 5 of 10 services outside Platform/ were **dead code** (zero external callers). ### Hand-Rolled Services | File | Current | Target | | ------------------------------- | ------------------------------------------- | ------------------------------------------- | | `Auth/SSOService.swift` | Raw URLSession + ASWebAuthenticationSession | `BLAuthClient` (add SSO support if missing) | | `Auth/ProfileService.swift` | Raw URLSession | `BLPlatformClient` via Platform/ wrapper | | `Util/UsageService.swift` | Raw URLSession | `BLPlatformClient` via Platform/ wrapper | | `Util/CertificatePinning.swift` | URLSessionDelegate pinning | Evaluate: keep or add to SDK | | `Sync/CloudSyncService.swift` | Raw URLSession | `BLSyncEngine` | | `Sync/OfflineQueue.swift` | Custom queue | `BLSyncEngine` | | `Sync/SessionSyncService.swift` | Raw URLSession | `BLSyncEngine` | | `Theme/ThemeService.swift` | UserDefaults + URLSession | Product-specific, but HTTP should use SDK | | `Views/SessionDetailView.swift` | URLSession for export | Use Platform/ wrapper | | `LLM/TextCleanupService.swift` | URLSession | Product-specific LLM call — keep | ### Tasks - [x] **6.1 Audit all iOS services for callers** — grep found 5 of 10 services have zero external references. - [x] **6.2 Delete dead code (5 files, -672 LOC)**: - [x] `Auth/SSOService.swift` (184 LOC) — no external callers - [x] `Auth/ProfileService.swift` (78 LOC) — no external callers - [x] `Util/UsageService.swift` (96 LOC) — no external callers - [x] `Util/CertificatePinning.swift` (117 LOC) — no external callers - [x] `Sync/OfflineQueue.swift` (197 LOC) — no external callers - [x] **6.3 Kept (active callers, product-specific):** - `Sync/CloudSyncService.swift` — used by `AuthService`, `RecordView` - `Sync/SessionSyncService.swift` — used by `SessionStore`, `SessionsListView` - `Theme/ThemeService.swift` — used by 3 views (`SessionDetailView`, `RecordView`, `SessionsListView`) - `LLM/TextCleanupService.swift` — product-specific LLM call - [ ] **6.4 Build verification** — **TODO-7: Verify Xcode build compiles. Note: deleted files must be removed from .xcodeproj.** --- ## Phase 7 — Auth App Android: Wire BLAuthClient **Priority:** MEDIUM | **Effort:** 0.5 day | **Repo:** `learning_ai_auth_app` | **Commit:** [`5b65215`](https://github.com/saravanakumardb1/learning_ai_auth_app/commit/5b65215) ### Context Auth App Android already had `BLAuthClient` fully wired — `AppConfig.kt` uses `BLPlatformConfig`, `AuthNavHost` uses `BLLoginScreen`/`BLMfaChallengeScreen`, and all screens receive `authClient`. Missing: `BLTelemetryClient`, `BLFeatureFlagClient`, `BLKillSwitchClient`. ### Files to Modify - `android/app/src/main/kotlin/com/bytelyst/auth/platform/AppConfig.kt` — UPDATE to use `BLPlatformConfig` > **Note:** `settings.gradle.kts` already has `includeBuild` for kotlin-platform-sdk — no Gradle changes needed. ### Files to Create - `android/app/src/main/kotlin/com/bytelyst/auth/platform/PlatformModule.kt` — NEW ### Tasks - [x] **7.1 Build wiring verified** — `settings.gradle.kts` has `includeBuild`, `build.gradle.kts` has `implementation("com.bytelyst:platform-sdk")`. Already correct. - [x] **7.2 AppConfig.kt** — Already uses `BLPlatformConfig`. No changes needed. - [x] **7.3 BLAuthClient already fully wired** — `MainActivity.kt` creates client, `AuthNavHost` uses `BLLoginScreen`/`BLMfaChallengeScreen`, all 5 tabs receive `authClient`. - [x] **7.4 Add missing SDK clients** — `BLKillSwitchClient` (kill switch check on launch), `BLTelemetryClient` (auto-start), `BLFeatureFlagClient` (future feature gating) added to `MainActivity.kt`. - [ ] **7.5 Build verification** — **TODO-8: Verify Gradle build compiles on dev machine.** --- ## Phase 8 — Auth App iOS: Add Telemetry, Flags, Kill Switch **Priority:** LOW | **Effort:** 0.5 day | **Repo:** `learning_ai_auth_app` | **Commit:** [`4a84050`](https://github.com/saravanakumardb1/learning_ai_auth_app/commit/4a84050) ### Context Auth App iOS already used `BLAuthClient` and `BLAuthUI` from ByteLystPlatformSDK. Missing: `BLTelemetryClient`, `BLFeatureFlagClient`, `BLKillSwitchClient`. ### Files to Create - `ios/ByteLystAuth/Platform/Config.swift` — NEW - `ios/ByteLystAuth/Platform/TelemetryService.swift` — NEW - `ios/ByteLystAuth/Platform/FeatureFlagService.swift` — NEW - `ios/ByteLystAuth/Platform/KillSwitchService.swift` — NEW ### Tasks - [x] **8.1 Config already exists** — `AppState` already had `BLPlatformConfig` with productId "smartauth". No separate Config.swift needed. - [x] **8.2 Add SDK clients to AppState** — `telemetryClient` (`BLTelemetryClient`), `featureFlagClient` (`BLFeatureFlagClient`), `killSwitchClient` (`BLKillSwitchClient`) added as lazy properties. - [x] **8.3 Wire kill switch** — `ContentView.swift` checks kill switch on launch, shows blocking message if killed. - [x] **8.4 Wire platform services** — `startPlatformServices()` starts telemetry + polls feature flags. Called in ContentView `.task`. - [x] **8.5 Consolidate lifecycle** — `restoreSession()` moved into unified `.task` block alongside kill switch + platform init. - [ ] **8.6 Build verification** — **TODO-9: Verify Xcode build compiles.** --- ## Phase 9 — Minor Gaps: ChronoMind iOS, NoteLett Mobile, MindLyst iOS, JarvisJr iOS **Priority:** LOW | **Effort:** 2–3 hours total | **Repos:** `learning_ai_clock`, `learning_ai_notes`, `learning_multimodal_memory_agents`, `learning_ai_jarvis_jr` **Commits:** - 9A: [`c328216`](https://github.com/saravanakumardb1/learning_ai_clock/commit/c328216) (ChronoMind iOS) - 9B: [`c2d6414`](https://github.com/saravanakumardb1/learning_ai_notes/commit/c2d6414) (NoteLett Mobile) - 9C: [`dce9e22`](https://github.com/saravanakumardb1/learning_multimodal_memory_agents/commit/dce9e22) (MindLyst iOS) - 9D: No changes needed (PlatformSyncManager actively used by MemoryManager) ### 9A — ChronoMind iOS: Add Config.swift + KillSwitch ChronoMind iOS wrappers are in `Shared/Cloud/` (acceptable for multi-target). Missing `Config.swift` and `KillSwitchService`. - [x] **9A.1 Create `Shared/Cloud/Config.swift`** — shared `BLPlatformConfig` for ChronoMind. - [x] **9A.2 Create `Shared/Cloud/KillSwitchService.swift`** — thin wrapper over `BLKillSwitchClient`. - [ ] **9A.3 Wire kill switch check in `ChronoMindApp.swift`** — deferred (wrapper exists, wiring is a UI task). - [ ] **9A.4 Build verification** — **TODO-10: Verify Xcode build compiles.** ### 9B — NoteLett Mobile: Add diagnostics-client NoteLett mobile is the gold standard but is missing `@bytelyst/diagnostics-client`. - [x] **9B.1 Add `@bytelyst/diagnostics-client`** to `mobile/package.json`. - [x] **9B.2 Wire `diagnosticsClient`** in `mobile/src/lib/platform.ts` alongside existing clients. - [ ] **9B.3 Build verification** — **TODO-11: Run `cd mobile && npm run typecheck` to verify.** ### 9C — MindLyst iOS: Migrate PlatformServiceClient + Add Missing Wrappers MindLyst iOS (`mindlyst-native/iosApp/`) has 4 Platform/ SDK wrappers but `Services/PlatformServiceClient.swift` is a hand-rolled URLSession client. Also missing `Config.swift`, `KillSwitchService`, and `CrashReporter`. - [x] **9C.1 Create `Platform/Config.swift`** — shared `BLPlatformConfig` for MindLyst. - [x] **9C.2 `PlatformServiceClient.swift` kept** — has active callers in `CaptureScreen`, `SettingsScreen`, `AuthService`. Product-specific (audio upload, memory items). Migration deferred. - [x] **9C.3 Create `Platform/KillSwitchService.swift`** — thin wrapper over `BLKillSwitchClient`. - [x] **9C.4 Create `Platform/CrashReporter.swift`** — thin wrapper over `BLCrashReporter`. - [ ] **9C.5 Build verification** — **TODO-12: Verify Xcode build compiles.** ### 9D — JarvisJr iOS: Migrate PlatformSyncManager JarvisJr iOS Platform/ is clean (7 wrappers) but `Shared/Cloud/PlatformSyncManager.swift` uses raw URLSession. - [x] **9D.1 `PlatformSyncManager.swift` kept** — has active caller in `MemoryManager.swift`. Product-specific sync logic. No changes needed. - [ ] **9D.2 Build verification** — **TODO-13: Verify Xcode build compiles.** --- ## Verification Matrix After all phases, every mobile surface should pass this checklist: ### iOS Apps (ByteLystPlatformSDK) | Check | LysnrAI | ChronoMind | JarvisJr | PeakPulse | MindLyst | Auth App | | -------------------------------------------- | ---------- | ---------- | ---------- | ---------- | ---------- | ----------- | | `BLPlatformConfig` wrapper | □ (exists) | □ | □ (exists) | □ (exists) | □ | □ | | `BLKeychain` wrapper | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ (via SDK) | | `BLAuthClient` wrapper | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ (via SDK) | | `BLTelemetryClient` wrapper | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ | | `BLFeatureFlagClient` wrapper | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ (exists) | □ | | `BLKillSwitchClient` wrapper | □ (exists) | □ | □ (exists) | □ (exists) | □ | □ | | `BLCrashReporter` wrapper | □ | □ (exists) | □ (exists) | □ (exists) | □ | □ | | No hand-rolled URLSession for platform calls | □ | □ (exists) | □ | □ (exists) | □ | □ (exists) | | `xcodebuild build` passes | □ | □ | □ | □ | □ | □ | ### Android Apps (kotlin-platform-sdk) | Check | LysnrAI | ChronoMind | MindLyst | JarvisJr | Auth App | | --------------------------------------------------- | ------------------------- | ---------- | ---------------- | -------- | -------------------------- | | `platform/Config.kt` | □ (exists) | □ (exists) | □ (exists) | □ | □ (exists as AppConfig.kt) | | `platform/PlatformModule.kt` (Hilt/Koin) | □ (exists) | □ (exists) | □ (exists, Koin) | □ | □ | | `BLAuthClient` wired | □ (intentionally skipped) | □ | □ (exists) | □ | □ | | `BLTelemetryClient` wired | □ (exists) | □ | □ (exists) | □ | N/A | | `BLFeatureFlagClient` wired | □ (exists) | □ | □ (exists) | □ | N/A | | `BLKillSwitchClient` wired | □ (exists) | □ (exists) | □ (exists) | □ | N/A | | No hand-rolled HttpURLConnection for platform calls | □ | □ | □ (exists) | □ | □ | | `./gradlew compileDebugKotlin` passes | □ | □ | □ | □ | □ | ### React Native / Expo Apps (@bytelyst/\*) | Check | NomGap | NoteLett | FlowMonk | | ------------------------------------------- | ---------- | ---------- | ---------- | | `@bytelyst/auth-client` | □ (exists) | □ (exists) | □ (exists) | | `@bytelyst/platform-client` | □ (exists) | □ (exists) | □ (exists) | | `@bytelyst/telemetry-client` | □ (exists) | □ (exists) | □ | | `@bytelyst/feature-flag-client` | □ (exists) | □ (exists) | □ (exists) | | `@bytelyst/kill-switch-client` | □ (exists) | □ (exists) | □ (exists) | | `@bytelyst/offline-queue` | □ (exists) | □ (exists) | □ (exists) | | `@bytelyst/blob-client` | □ (exists) | □ (exists) | □ | | `@bytelyst/design-tokens` | □ (exists) | □ (exists) | □ | | No hand-rolled `fetch()` for platform calls | □ | □ (exists) | □ | | `npm run typecheck` passes | □ | □ | □ | --- ## Execution Order Recommendation ``` Week 1 (HIGH priority): Phase 1: ChronoMind Android (1 day) Phase 2: JarvisJr Android (1 day) Week 2 (MEDIUM priority): Phase 3: FlowMonk Mobile RN (0.5 day) Phase 5: NomGap RN (0.5 day) Phase 4: LysnrAI Android (1 day) Week 3 (MEDIUM priority): Phase 6: LysnrAI iOS (1 day) Phase 7: Auth App Android (0.5 day) Week 4 (LOW priority + cleanup): Phase 8: Auth App iOS (0.5 day) Phase 9A: ChronoMind iOS — Config + KillSwitch (0.5 hour) Phase 9B: NoteLett Mobile — diagnostics-client (0.5 hour) Phase 9C: MindLyst iOS — PlatformServiceClient + Config + KillSwitch + CrashReporter (1 hour) Phase 9D: JarvisJr iOS — PlatformSyncManager (0.5 hour) Final verification matrix sign-off ``` --- ## Notes - **MindLyst iOS** (`mindlyst-native/iosApp/`) has **26 Swift files** including 4 Platform/ SDK wrappers (`AuthService`, `KeychainHelper`, `TelemetryService`, `FeatureFlagService`), Screens, Models, Services, Navigation, Widgets, and ShareExtension. **Minor gap:** `Services/PlatformServiceClient.swift` is a hand-rolled URLSession client for platform-service dev wiring — should be migrated to use `BLPlatformClient` via a Platform/ wrapper. Also missing: `KillSwitchService`, `CrashReporter`, `Config.swift`. - **MindLyst Android** (`mindlyst-native/androidApp/`) uses Koin DI (not Hilt). `platform/Config.kt` and `platform/PlatformModule.kt` exist. Provides **all 6 SDK components** (`BLPlatformConfig`, `BLSecureStore`, `BLAuthClient`, `BLTelemetryClient`, `BLFeatureFlagClient`, `BLKillSwitchClient`) via Koin module. No gaps. - **PeakPulse iOS** is the cleanest iOS app — full Platform/ directory with 9 wrappers including `DiagnosticsService`. No gaps. - **JarvisJr iOS** has a clean Platform/ directory with 7 wrappers. **Minor gap:** `Shared/Cloud/PlatformSyncManager.swift` uses raw URLSession — should be migrated to `BLSyncEngine` or `BLPlatformClient`. - **TextCleanupService** (LysnrAI) and other LLM-specific services that use URLSession for OpenAI/Azure calls are intentionally product-specific and should NOT be migrated to platform SDK. - **CertificatePinning** is a security feature that may need to stay product-specific. Evaluate during Phase 4/6 whether it's worth adding to the SDK. - **LysnrAI Android `AuthViewModel`** is intentionally kept hand-rolled (DataStore, keyboard bridging, CloudSync integration are too LysnrAI-specific for `BLAuthClient`). This is a conscious design decision, not a gap. --- ## Post-Implementation Review Fixes (2026-03-21) Systematic review of all 9 phases found **7 bugs** across 7 repos. All fixed and pushed. ### Bug 1: LysnrAI Android — SettingsStore.kt incorrectly deleted - **Root cause:** Phase 4 grep for callers searched by class name `SettingsStore`, but callers import `SettingsKeys` and `settingsStore` (top-level extension property). - **Impact:** 5 files with broken imports (SettingsScreen, RecordViewModel, RecordScreen, AuthViewModel, SessionDetailScreen). - **Fix:** [`ada3d43`](https://github.com/saravanakumardb1/learning_voice_ai_agent/commit/ada3d43) — restored SettingsStore.kt. ### Bug 2: Android kill switch — `result.killed` should be `result.disabled` - **Root cause:** `BLKillSwitchClient.KillSwitchResult` uses `disabled: Boolean`, not `killed`. Code was written without verifying SDK API. - **Impact:** Kill switch never triggered on Auth App, JarvisJr, ChronoMind Android. - **Fix:** - Auth App: [`bf518fd`](https://github.com/saravanakumardb1/learning_ai_auth_app/commit/bf518fd) - JarvisJr: [`dddb11c`](https://github.com/saravanakumardb1/learning_ai_jarvis_jr/commit/dddb11c) - ChronoMind: [`151d836`](https://github.com/saravanakumardb1/learning_ai_clock/commit/151d836) ### Bug 3: Auth App iOS — BLKillSwitchClient.check() returns Void - **Root cause:** Swift `BLKillSwitchClient.check()` sets `isDisabled`/`maintenanceMessage` as instance properties. Code treated it as returning a result struct. - **Also:** `BLFeatureFlagClient` uses `start()` not `poll()`. - **Fix:** [`215e7c9`](https://github.com/saravanakumardb1/learning_ai_auth_app/commit/215e7c9) ### Bug 4: ChronoMind iOS — KillSwitchService returned nonexistent BLKillSwitchResult - **Root cause:** Same as Bug 3. `BLKillSwitchResult` type doesn't exist in the Swift SDK. - **Fix:** [`d3c3aff`](https://github.com/saravanakumardb1/learning_ai_clock/commit/d3c3aff) — expose `isDisabled`/`maintenanceMessage` as computed properties. ### Bug 5: MindLyst iOS — KillSwitchService + CrashReporterService wrong API - **Root cause:** KillSwitchService same as Bug 4. CrashReporter takes `productId: String`, not `config: BLPlatformConfig`. Also `@MainActor` + `NSObject` subclass needs lazy init. - **Fix:** [`cd78597`](https://github.com/saravanakumardb1/learning_multimodal_memory_agents/commit/cd78597) ### Bug 6: JarvisJr iOS — KillSwitchResponse decoded `killed` (doesn't exist in server response) - **Root cause:** Pre-existing bug. Platform-service returns `{ disabled: Bool }` but hand-rolled `KillSwitchResponse` decoded `killed`, which is always nil/false. - **Fix:** [`2a3cc7e`](https://github.com/saravanakumardb1/learning_ai_jarvis_jr/commit/2a3cc7e) ### Bug 7: NoteLett Mobile — `createDiagnosticsClient` doesn't exist - **Root cause:** `@bytelyst/diagnostics-client` exports `DiagnosticsClient` class with `getInstance()` singleton, not a factory function. - **Fix:** [`0b5c224`](https://github.com/saravanakumardb1/learning_ai_notes/commit/0b5c224)