agents-mobile: store-readiness polish (manifests, permissions, icons, splash)#4626
agents-mobile: store-readiness polish (manifests, permissions, icons, splash)#4626msfstef wants to merge 4 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4626 +/- ##
==========================================
+ Coverage 57.71% 59.25% +1.53%
==========================================
Files 342 385 +43
Lines 40009 42734 +2725
Branches 11659 12273 +614
==========================================
+ Hits 23091 25320 +2229
- Misses 16881 17339 +458
- Partials 37 75 +38
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@- |
Electric Agents Mobile BuildLocal mobile checks ran for commit The EAS Android preview build was skipped because the |
…ash, icons Cover binary-readiness gaps for App Store / Play review: - Declare iOS privacy manifest (ios.privacyManifests) for the required-reason APIs used by AsyncStorage and Sentry. Apple auto-rejects uploads (ITMS-91053) that call these without a declaration; statically-linked pods' own manifests aren't reliably read, so declare at app level. NSPrivacyTracking is false and crash/perf data is declared not-linked / not-tracking. - Set expo-image-picker microphonePermission:false and block RECORD_AUDIO + legacy READ/WRITE_EXTERNAL_STORAGE on Android. The app uses the system photo picker (content URIs, no broad media access) and never records audio, so these only bloat the Play permission list / Data Safety form. - Add expo-splash-screen + assets/splash-icon.png (dark #101217 background). There was no splash, so cold start flashed blank white against the dark theme. - Ship an opaque iOS icon (flatten the transparent corners onto #101217) since Apple rejects icons with alpha, and add a monochrome adaptive-icon layer for Android 13+ themed icons. - Add expo-system-ui so userInterfaceStyle:automatic actually applies on Android (prebuild warned it was a no-op without it). - Add an app description. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two robustness fixes for flows a store reviewer is likely to hit: - Wrap the app provider tree in Sentry.ErrorBoundary (inside ThemeProvider so the fallback is themed) with a "Try again" screen. An uncaught render error otherwise shows a blank/native crash screen, which is an automatic review rejection. Sentry still reports the error. - Give the /oauth/callback route a 10s escape hatch. A cold start onto the callback with no pending request could spin on "Finishing sign-in…" forever; now it surfaces a "Back to sign-in" action. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Gate the cloud-auth console.warn calls behind __DEV__ via an exported devWarn helper (reused from CloudAuthContext) so token-exchange / HTTP-status details don't get written to production device logs. - Point the account-deletion link at electric.ax to match the terms/privacy URLs (it was on a different domain). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9c6fe6a to
1cfe0c6
Compare
|
Addressed the review + a couple of polish tweaks (branch force-pushed):
The splash |
Independent review of the permission changes found that blocking WRITE_EXTERNAL_STORAGE breaks photo capture on Android 7-9: expo-image-picker 17's launchCameraAsync -> ensureCameraPermissionsAreGranted() hard-requires that permission to be GRANTED on API < 29 (ImagePickerModule.kt), and a blocked (tools:node="remove") permission can never be granted. Library picking and Android 10+ are unaffected. Keep blocking RECORD_AUDIO + READ_EXTERNAL_STORAGE (genuinely unused); leave WRITE_EXTERNAL_STORAGE in place. It's inert on Android 10+ and a benign legacy permission, not a Play review risk. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Ran an independent adversarial review of the privacy-manifest + permissions changes. Outcome:
|
Overview
Audit + polish pass on
packages/agents-mobilefocused on the app binary's readiness for App Store + Google Play review (store-listing content like screenshots/descriptions is out of scope). The goal was low-hanging fruit and chores — missing manifests, icons, permissions hygiene, cold-start polish — that matter for passing review.Done as a 4-agent investigation (2 reading the code, 2 researching current 2025–2026 Apple/Google requirements from official + community sources), with the high-stakes claims verified first-hand against the code and an Android prebuild.
This PR description is the reference record of that investigation so future sessions on this branch don't repeat the research.
App context: Expo SDK 54, RN 0.81.5, New Architecture, expo-router, managed (CNG) workflow,
com.electricsql.agents.mobile, iOS min target 16.4. OAuth sign-in (GitHub/Google via system browser), AsyncStorage, Sentry, photo attachments (expo-image-picker), WebView/DOM chat timeline.What this PR changes
Three commits:
1. Harden store config: privacy manifest, permissions, splash, icons (
app.config.ts, assets, deps)ios.privacyManifests) declaring the required-reason APIs used by AsyncStorage + Sentry (CA92.1,C617.1,35F9.1,E174.1),NSPrivacyTracking: false, and crash/perf data types (not linked, not tracking). Apple auto-rejects uploads (ITMS-91053) that call these without a declaration; statically-linked pods' own manifests aren't reliably read, so we declare app-level.microphonePermission: falseon expo-image-picker + AndroidblockedPermissionsforRECORD_AUDIOandREAD_EXTERNAL_STORAGE(verified stripped viatools:node="remove"). The app uses the system photo picker (content URIs, no broad media access) and never records audio, so those only inflate the Play permission list.WRITE_EXTERNAL_STORAGEis intentionally not blocked — expo-image-picker's pre-Android-10 (API < 29) camera path hard-requires it, so blocking it breaks photo capture on Android 7–9 (caught in the independent permissions review).expo-splash-screen+assets/splash-icon.png(dark#101217, dark variant). There was none, so cold start flashed blank white against the dark theme.#101217; Apple rejects icons with alpha) + monochrome adaptive-icon layer for Android 13+ themed icons.expo-system-uisouserInterfaceStyle: automaticactually applies on Android (prebuild warned it was a no-op without it).description.2. Add error boundary and OAuth callback timeout (
app/_layout.tsx,app/oauth/callback.tsx)Sentry.ErrorBoundaryaround the provider tree (themed "Try again" fallback) so an uncaught render error is recoverable instead of a blank/native crash a reviewer can hit. Sentry still reports the error./oauth/callbackso a cold start with no pending request can't spin on "Finishing sign-in…" forever — surfaces "Back to sign-in".3. Clean up auth logging and legal link (
cloudAuth.ts,CloudAuthContext.tsx,AccountScreen.tsx)console.warncalls behind__DEV__(exporteddevWarnhelper, reused from the context) so token-exchange / HTTP-status details don't hit production device logs.electric.axto match the terms/privacy URLs (it was on a different domain —electric-sql.com).These are real review blockers but can't be config-only fixes:
AccountScreencurrently only opens a web page whose own copy says "your account is not deleted by tapping the button … email support to start the request" — the canonical rejected pattern. Needs a genuine in-app deletion flow calling a backend delete endpoint (the same admin-APIcloudAuthalready uses) with a confirm dialog + local sign-out. A web link is fine as the secondary path, not the only one.OnboardingScreen.tsx). 4.8 generally requires an equivalent privacy-preserving login (SIWA) when social logins are the mechanism. Needsexpo-apple-authentication+ backend support for Apple as an identity provider, or a defensible "client for a specific service" argument. iOS-only; Android can stay Google/GitHub. Decision needed.agents-server-uialready provides this; if not it's a likely blocker on both stores (may also need block-user + a visible support contact).Should-fix (mostly submission-process / out-of-binary)
app/session.tsx+agents-server-uiEmbedApp.tsx). The DOM embed shows "Connecting…/Loading session…" indefinitely on connection failure; server-down only surfaces as a red dot in the overflow menu. A reviewer on a flaky network sees a permanent spinner = "broken." Deliberately not fixed here: the embed ref exposes no connection/error signal, and a speculative native health-poll overlay on this flash-sensitive screen (see the Expo DOM embed flash issue) risks false "can't connect" flashes that read worse than a spinner. Proper fix: expose connection state from the embed ref, or add an error+retry state inside the sharedEmbedApp.tsx(affects desktop/web too).bundletool dump config --bundle=app.aabshowsPAGE_ALIGNMENT_16K, and the merged manifest has no strayAD_ID/QUERY_ALL_PACKAGES(reconcile with Data Safety / Advertising ID declaration if present).launchCameraAsync) is actually used; if photo-library-only ships, dropcameraPermission.Polish / nice-to-have
accessibilityRole="link"on the legal Terms/Privacy<Text onPress>; decorativeIconSVGs not hidden from screen readers; no Dynamic Type /allowFontScaling(fixed px sizes).ThemeProviderdefaults to dark, so a light-mode device may briefly flash dark before hydrating. Splash mitigates; could seed the initial scheme fromAppearance.getColorScheme()synchronously.resolveVersionCode()dev fallback (Date.now()/1000) is a latent footgun if a local build ever reaches a store upload (Play caps versionCode at 2.1e9). CI always writes.build-info.jsonso low risk; consider throwing on store profiles when neither env nor build-info is present.Already satisfied by the stack (verified — no action)
edgeToEdgeEnabled: true+react-native-safe-area-contextinsets handled across screens.production/canary-storeusedistribution: "store"; keep theapkpreview/canary profiles out ofeas submit.ITSAppUsesNonExemptEncryption: falseis correct (HTTPS/OAuth/AsyncStorage). No ATT needed (Sentry isn't tracking, no IDFA) — don't addexpo-tracking-transparency.Verification
expo-doctor18/18 ·tsc --noEmitclean ·eslintclean ·vitest88/88 ·expo prebuild --platform androidconfirmed the blocked permissions (tools:node="remove"), the<monochrome>adaptive layer, and thesplashscreen_logoresources (incl.drawable-night-*).pnpm install --frozen-lockfilepasses on the trimmed lockfile.🤖 Generated with Claude Code