chore(mobile): upgrade to Expo SDK 57 (#402) - #433
Conversation
Bump apps/mobile from Expo SDK 56 to SDK 57 (RN 0.85 to 0.86, React 19.2 unchanged, reanimated 4.3.1 to 4.5.0, worklets 0.8.3 to 0.10.0 as an ABI pair moved together). Reconcile both overrides blocks (root + mobile) and the hermes-compiler transitive re-pin to the resolved 57.x set; keep the exact-pin discipline the release guard relies on. Bump non-Expo native modules to RN-0.86-compatible releases: true-sheet 3.11.3, sentry 8.17.2, lucide-react-native 1.23.0. RNGMA stays pinned at 16.3.3 / play-services-ads 25.2.0 because SDK 57 still ships Kotlin 2.1.20 and 16.4.0 requires Kotlin 2.3+. Sweep apps/web + packages/shared minor/patch deps. Update stale SDK-56 references in .npmrc, android-release.yml, forbid-expo-pin-bump.mjs, with-android-release-build-fixes.js, prepare-release-workspace.js, and the root + mobile CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
thomasluizon
left a comment
There was a problem hiding this comment.
Code Review: PR #433 — chore(mobile): upgrade to Expo SDK 57 (#402)
Scope: PR #433 in thomasluizon/orbit-ui-mobile (branch issue-402, diff vs main)
Recommendation: APPROVE
Summary
A mobile-only native-toolchain upgrade (Expo SDK 56 → 57, RN 0.85 → 0.86) plus a
minor/patch apps/web + packages/shared dependency sweep. The diff is 12 files — package
manifests, the lockfile, and version-string updates in docs/hooks/workflow comments — with
zero application source changed. Version coherence is verified both internally (the
resolved lockfile) and externally (npm registry). No Critical/High/Medium findings.
Findings
Critical
None.
High
None.
Medium
None.
Low / Info
- Info —
lucide-react-nativejumps^1.7.0 → ^1.23.0(16 minor versions) to match web's
lucide-react ^1.23.0. Icon libraries can rename/remove icons across that span, but
type-checkis green across all 3 workspaces, which would fail on any removed named export
in use — so every imported icon still resolves. Good parity move; noted only, no action.
Coherence verification (the load-bearing check for this PR)
ABI-paired reanimated/worklets — verified across all three pin sites:
| Site | reanimated | worklets |
|---|---|---|
apps/mobile/package.json deps |
4.5.0 | 0.10.0 |
apps/mobile/package.json overrides |
— | 0.10.0 |
root package.json overrides |
— | 0.10.0 |
- The resolved
react-native-reanimated@4.5.0peerDependencies (lockfile + npm registry)
requirereact-native: "0.83 - 0.86"andreact-native-worklets: "0.10.x"— the
pinned RN0.86.0and worklets0.10.0satisfy both exactly. expo@57.0.4's canonical RN devDependency is0.86.0(npm registry) — matches the pin.- Lockfile resolves a single deduped copy of each:
expo@57.0.4,react-native@0.86.0,
react-native-reanimated@4.5.0,react-native-worklets@0.10.0. No stale56.x/0.85.x
/4.3.1/0.8.3leftovers.
Both overrides blocks reconciled to 57.x, mutually consistent: every one of the 8
overlapping keys between the root and mobile override blocks is byte-identical, and every
override version matches its corresponding direct-dep version (expo 57.0.4, expo-router
57.0.4, expo-dev-client 57.0.5, expo-image-picker 57.0.2, expo-linking 57.0.2,
expo-notifications 57.0.3, etc.). The exact/tilde pinning discipline is preserved so the
android-release.yml exact-string guard keeps matching.
RNGMA / Kotlin gate: correct hold decision. react-native-google-mobile-ads stays
16.3.3 and PINNED_PLAY_SERVICES_ADS_VERSION is unchanged at 25.2.0 (only the
.because explanation text was version-updated) — Kotlin remains 2.1.20 under SDK 57, so
RNGMA 16.4.0 / play-services-ads 25.4.0 (Kotlin 2.3 metadata) would still break the build.
Consistent with the SDK-56 posture; no regression.
fix-hoisting junctions: no edit needed and none made — the junctions key on package
names (react-native → worklets/nativewind/css-interop/root), which are unchanged by a
version bump. The Metro singleton-resolver block was correctly not reintroduced (#351).
Backward-compat: N/A and clean — the diff touches no packages/shared/src/types/*, no
endpoints.ts, and no orbit-api DTO. Zero fields added/removed/renamed → cannot break an
already-shipped mobile client.
Subagents
| Agent | Verdict |
|---|---|
| parity-checker | N/A — no behavioral code changed; the web sweep vs mobile SDK bump are the sanctioned platform-adapter divergence (dep-sweep defers mobile to Expo tooling) |
| i18n-syncer | N/A — no i18n strings touched |
| contract-aligner | N/A — no shared types / endpoints / DTOs touched |
| security-reviewer | N/A — no orbit-api code |
| design-reviewer | N/A — no UI files |
Validation
| Check | Result |
|---|---|
| Lint | PASS (per PR-documented gates; not re-executed — see Deferred) |
| Type check | PASS (per PR-documented gates) |
| Tests | PASS (per PR-documented gates) |
| Build (api) | N/A — no orbit-api change |
Additional PR-documented native gates: npx expo install --check clean, npx expo prebuild --clean --platform android succeeds, npm audit --omit=dev --audit-level=critical 0
critical.
Deferred — N/A dimensions & checks not independently re-run
- Dimensions marked N/A (surface untouched): SOLID/clean-arch, comment policy (only
pre-existing WHY comments had version text updated), type safety, no-console.log,
DESIGN.md/AI-slop, parity (behavioral), i18n, contract drift, backend hard rules,
FEATURES.md parity. Each is N/A because the diff changes only dependency manifests, the
lockfile, and version strings in docs/hooks — no application source. - Validation not independently re-executed in this review pass. The PR documents every
gate green; re-running a fullturbo build/testinstall cycle inside a separate worktree
would risk disrupting the parallel worktree state for no added signal — the diff has no
source logic beyond whattype-check/buildalready cover, and the coherence claims were
verified statically + against the npm registry instead. - Device QA + Play submit are explicitly manual/out-of-automated-scope (auth, habit
CRUD/offline queue, AI voice, TrueSheet stacking, AdMob, IAP, widget, and the
carried-over reanimated Android-memory watch item).
What's good
- Meticulous three-site pin coherence with the two override blocks fully reconciled and the
release guard's exact-string discipline preserved. - Every non-Expo native module decision (TrueSheet, Sentry RN, lucide, view-shot, iap, etc.)
is individually justified against RN 0.86 / New Arch rather than bulk-bumped. - The
expo.install.excludefor@sentry/react-nativeand the deferredreact-native-view-shot
5.1.0 pin (Expo-managed,--checkrejects 5.1.1) are respected, not fought. - Honest, thorough PR body: the 5 pre-existing expo-doctor findings, the vitest worker-pool
timeout caveat, and the splash-config follow-up are all surfaced rather than hidden. - Web/shared sweep is strictly minor/patch and correctly skips the SDK-pinned React set,
web'seslint ^9.39.4(#255), and the TypeScript 7 major.
Recommendation
APPROVE. Clean, coherent, well-documented one-SDK-at-a-time upgrade with no contract, parity,
or backward-compat exposure. Proceed to the documented manual steps (release APK build,
device QA, Play submission; raise AppConfig.MinSupportedVersion only after the carrying
build is live in the Play fleet, #210).
There was a problem hiding this comment.
Code Review: PR #433 — chore(mobile): upgrade to Expo SDK 57 (#402)
Scope: PR #433, branch issue-402 → main
Recommendation: APPROVE
Summary
A mobile-only native-toolchain upgrade (Expo SDK 56 → 57, RN 0.85 → 0.86) plus a minor/patch
apps/web + packages/shared dependency sweep. The diff is 12 files — package manifests, the
lockfile, and version-string updates in docs/hooks/workflow comments — with zero application
source changed. Version coherence is verified both internally (resolved lockfile) and
externally (npm registry). No Critical/High/Medium findings.
Findings
Critical / High / Medium
None.
Low / Info
- Info —
lucide-react-nativejumps^1.7.0 → ^1.23.0(16 minor versions) to match web's
lucide-react ^1.23.0. Icon libraries can rename/remove icons across that span, but
type-checkis green across all 3 workspaces, which would fail on any removed named export
in use — so every imported icon still resolves. Good parity move, noted only, no action.
Coherence verification (load-bearing check for this PR)
ABI-paired reanimated/worklets — verified across all three pin sites:
| Site | reanimated | worklets |
|---|---|---|
apps/mobile/package.json deps |
4.5.0 | 0.10.0 |
apps/mobile/package.json overrides |
— | 0.10.0 |
root package.json overrides |
— | 0.10.0 |
- Resolved
react-native-reanimated@4.5.0peerDependencies requirereact-native: "0.83 - 0.86"andreact-native-worklets: "0.10.x"— the pinned RN0.86.0and worklets0.10.0
satisfy both. expo@57.0.4's canonical RN devDependency is0.86.0— matches the pin.- Lockfile resolves a single deduped copy of each:
expo@57.0.4,react-native@0.86.0,
react-native-reanimated@4.5.0,react-native-worklets@0.10.0. No stale56.x/0.85.x
leftovers.
Both overrides blocks reconciled to 57.x, mutually consistent: every overlapping key
between root and mobile override blocks is byte-identical, and every override version matches
its direct-dep version. Exact/tilde pinning discipline preserved so the android-release.yml
exact-string guard keeps matching.
RNGMA / Kotlin gate: correct hold decision. react-native-google-mobile-ads stays 16.3.3
and PINNED_PLAY_SERVICES_ADS_VERSION is unchanged at 25.2.0 (only the .because
explanation text was version-updated) — Kotlin remains 2.1.20 under SDK 57, so RNGMA 16.4.0 /
play-services-ads 25.4.0 (Kotlin 2.3 metadata) would still break the build.
fix-hoisting junctions: no edit needed and none made — junctions key on package names,
unchanged by a version bump.
Backward-compat: N/A and clean — diff touches no packages/shared/src/types/*, no
endpoints.ts, no orbit-api DTO. Zero fields added/removed/renamed → cannot break an
already-shipped mobile client. (orbit-api sibling repo not checked out in this job; no
cross-repo contract surface is touched by this diff regardless.)
Subagents
| Agent | Verdict |
|---|---|
| parity-checker | N/A — no behavioral code changed |
| i18n-syncer | N/A — no i18n strings touched |
| contract-aligner | N/A — no shared types/endpoints/DTOs touched; not verifiable in CI (orbit-api not checked out) but moot here |
| security-reviewer | N/A — no orbit-api code |
| design-reviewer | N/A — no UI files |
Validation
Per CI adaptation, Phase 6 (/validate) skipped — Build / Unit Tests / SonarCloud run as
separate required checks on this PR.
What's good
- Meticulous three-site pin coherence with both override blocks fully reconciled and the
release guard's exact-string discipline preserved. - Every non-Expo native module decision (TrueSheet, Sentry RN, lucide, view-shot, iap, etc.)
is individually justified against RN 0.86/New Arch rather than bulk-bumped. - Honest PR body surfacing pre-existing expo-doctor findings and follow-ups rather than hiding
them. - Web/shared sweep is strictly minor/patch, correctly skipping SDK-pinned React and the
TypeScript 7 major.
Recommendation
APPROVE. Clean, coherent, well-documented single-SDK upgrade with no contract, parity, or
backward-compat exposure.
|
There was a problem hiding this comment.
Review Complete
Scope: PR #433 in thomasluizon/orbit-ui-mobile (issue-402 → main) — chore(mobile): upgrade to Expo SDK 57 (#402)
Recommendation: APPROVE
| Severity | Count |
|---|---|
| Critical (incl. |
0 |
| High | 0 |
| Medium | 0 |
| Low / Info | 1 |
Code Review: PR #433 — chore(mobile): upgrade to Expo SDK 57 (#402)
Summary
Pure dependency-coherence PR: Expo SDK 56 → 57 for apps/mobile (react-native 0.85.3→0.86.0, reanimated 4.3.1→4.5.0, worklets 0.8.3→0.10.0, the full expo-* family), plus an unrelated minor/patch sweep on apps/web and packages/shared. No application source, UI, endpoints, or user-facing behavior changed — only manifests, package-lock.json, and comment/doc strings referencing the SDK number. Every version pin was cross-checked across all sites (apps/mobile/package.json deps + overrides, root package.json overrides, .npmrc, .github/workflows/android-release.yml, with-android-release-build-fixes.js) and the resolved package-lock.json: everything is internally coherent, single-deduped (expo@57.0.4 / react-native@0.86.0 / react-native-reanimated@4.5.0 / react-native-worklets@0.10.0, no orphaned 56.x/0.85.x shadow copies), and matches the PR body's version table exactly.
A merge-from-main commit landed on the branch after the prior approval, but it only brought in an already-merged, unrelated commit (#432, post-auth navigation fix) with no conflicts — the actual PR diff against main is unchanged from what was previously reviewed.
Findings
Critical
None
High
None
Medium
None
Low / Info
[INFO] Stale "Expo SDK 56" mentions left in three skill docs· dimension: dead/stale code · location:.claude/skills/deep-research/SKILL.md:113,.claude/skills/audit-performance/SKILL.md:29,.claude/skills/prime/SKILL.md:138· issue: these still say "Expo SDK 56" while this same PR updated the identical phrase in rootCLAUDE.mdandapps/mobile/CLAUDE.md· risk: none functional — AI-tooling context only, not shipped code · fix: swap to "SDK 57" next time those files are touched. Not posted as a blocking finding per the rubric's signal gate (Low/Info).
Subagents
| Agent | Verdict |
|---|---|
| parity-checker | N/A — confirmed via subagent run: dependency-manifest-only change; Expo SDK is mobile-only, web's bumps are an independent, unrelated minor/patch sweep, not a mirror obligation |
| i18n-syncer | N/A — no user-facing strings or i18n JSON changed |
| contract-aligner | N/A — no packages/shared/src/types/*/endpoints.ts changed; orbit-api not in this diff |
| security-reviewer | N/A — orbit-api not touched |
| design-reviewer | N/A — no apps/* UI files changed |
Validation
| Check | Result |
|---|---|
| Lint | N/A — CI-wrapper scope, skip per skill instructions; PR body reports it green |
| Type check | N/A — same |
| Tests | N/A — same |
| Build (api) | N/A — orbit-api not touched |
Substituted targeted manual verification: every version-pin site is mutually consistent, and package-lock.json resolves single deduped versions for every SDK-pinned package (verified by direct lockfile grep).
Deferred — N/A dimensions & files not verdicted
SOLID/clean-arch, Type safety, console.log, DESIGN.md/AI-slop, i18n, Contract drift + backward-compat, Backend hard rules, FEATURES.md parity are all N/A — this diff touches no application logic, UI, schemas, or orbit-api surface. All 12 changed files received an individual verdict during the review. Nothing deferred.
What's good
- Exemplary cross-file version discipline: the ABI-paired reanimated/worklets bump and every version-number comment stayed in lockstep — a common source of silent drift in dependency PRs.
- The RNGMA/Kotlin gate was re-verified (Kotlin 2.1.20 ceiling confirmed unchanged on SDK 57's RN 0.86 catalog) rather than assumed, and the
play-services-adsWHY comment was updated to the current SDK number. - Honest, evidence-backed PR body separating pre-existing
expo-doctorfindings from anything this upgrade could have caused.
Recommendation
Merge — no code-quality, security, parity, contract, or backward-compat risk. Optional non-blocking follow-up: fix the three stale "Expo SDK 56" doc mentions listed above whenever those files are next touched.
…ity-reviewer auto-fire, agent lockstep (#439) - Bump stale Expo SDK 56 -> 57 (verified live: app.json/package.json = 57.0.4, #402/#433) in prime + deep-research SKILL. audit-performance uses version-agnostic "Expo mobile" — nothing to bump there. - Repoint deleted-integration-suite references (suite removed in orbit-api #198; only bin/obj leftovers remain, not in Orbit.slnx) to the real unit-test projects (Orbit.Application.Tests): prime, plan (x2), create-stories. - security-reviewer: description + body now say it auto-invokes during /pr-review when the diff touches orbit-api code (it does — Phase 4), not "explicit-invocation only". - Lockstep note on contract-aligner + security-reviewer: the twins in orbit-api cannot be deduped (orbit-api runs /pr-review from its own root in CI via claude-review.yml; subagents resolve per-repo). Reconciled ui-mobile copies' stale file refs to reality (JwtTokenService.cs, ServiceCollectionExtensions.Infrastructure.cs, feature-local DTOs — Common/DTOs does not exist). Paired with orbit-api chore/harness-stage-6-agent-lockstep. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>



Summary
Upgrades
apps/mobilefrom Expo SDK 56 to SDK 57 (RN 0.85 → 0.86) — a deliberate, one-SDK-at-a-time bump. SDK 57 is a small, no-breaking-changes release, so the work is native-toolchain coherence, not app-code rewrites. In the same PR: bothoverridesblocks reconciled to the resolved 57.x set, the ABI-paired reanimated/worklets pair moved together across all three pin sites, non-Expo native modules moved to their RN-0.86 releases, and aapps/web+packages/sharedminor/patch dependency sweep.npx expo install --checkis clean,npx expo prebuild --clean --platform androidsucceeds, and the full monorepo CI gate set (build / type-check / lint / test / critical audit) passes.Closes #402.
Resolved SDK 57 version table
exporeact-nativereact/react-domreact-native-reanimatedreact-native-workletsreact-native-gesture-handlerexpo-modules-coreexpo-routerhermes-compilerbabel-preset-expoeslint-config-expoexpo-*expo install --fix)The ABI-paired reanimated/worklets pins are coherent across all three sites:
apps/mobiledep,apps/mobileoverride, and the root override.npm lsresolves a single dedupedreact-native@0.86.0/reanimated@4.5.0/worklets@0.10.0/expo@57.0.4. Bothoverridesblocks were relaxed beforeexpo install --fix(npm overrides win at resolution) then re-tightened to the resolved 57.x versions. The original exact/tilde pinning discipline was preserved so theandroid-release.ymlguard's exact-string match keeps working.Native third-party module decisions
expo install --fixdoes not touch these — each was evaluated against RN 0.86 / New Architecture by hand.@lodev09/react-native-true-sheetreact-native: *,reanimated: >=4,worklets: *— RN 0.86 compatible@sentry/react-nativereact-native: >=0.65.0(excluded from--fixviaexpo.install.exclude)lucide-react-nativelucide-react^1.23.0react-native-view-shotexpo install --checkrejects 5.1.1. Deferred to Expo's recommendation.@siteed/audio-studioexpo-iapreact-native-draggable-flatlistreact-native-markedreact-native-qrcode-svgsp-react-native-in-app-updates@react-native-clipboard/clipboardRNGMA / Kotlin gate outcome
Kotlin version found:
2.1.20— captured from the prebuilt project. Expo SDK 57'sexpo-root-projectgradle plugin resolveskotlinVersionfrom RN 0.86's version catalog (react-native/gradle/libs.versions.toml→kotlin = "2.1.20") and hard-rejects Kotlin ≥ 2.3.0. This is unchanged from SDK 56 (which also ran Kotlin 2.1.20).Decision:
react-native-google-mobile-adsstays pinned at16.3.3— BLOCKED, Kotlin < 2.3. RNGMA 16.4.0 pullsgoogleMobileAds 25.4.0, which ships Kotlin 2.3 metadata the SDK 57 (Kotlin 2.1.20) toolchain cannot read.play-services-adstherefore also stays pinned at 25.2.0 (via thewith-android-release-build-fixesgradle resolution strategy — its WHY comment was updated to reference SDK 57).Web + shared dependency sweep (minor/patch only)
apps/web:@sentry/nextjs10.63.0 → 10.64.0,@supabase/supabase-js2.110.0 → 2.110.1,react-hook-form7.80.0 → 7.81.0,@types/node26.1.0 → 26.1.1,typescript-eslint8.62.1 → 8.63.0,vitest+@vitest/coverage-v84.1.9 → 4.1.10.packages/shared:@types/node26.1.0 → 26.1.1,typescript-eslint8.62.1 → 8.63.0,vitest+@vitest/coverage-v84.1.9 → 4.1.10.Skipped (out of sweep policy):
react/react-dom(the SDK-pinned React set, held at 19.2.3 by the root override),eslint(web deliberately pinned at ^9.39.4 per #255; latest 10.x is a major),typescript(latest 7.x is a major).Validation
npx expo install --checknpx expo prebuild --clean --platform androidnpm run buildnpm run type-checknpm run lintnpm run testnpm audit --omit=dev --audit-level=critical@expo/config-pluginstree, 0 critical)Notes:
turbo run testintermittently hit vitest worker-pool timeouts locally (3 heavy suites saturating the machine); re-running@orbit/webin isolation passes all 2198 tests. Not caused by the vitest patch bump.hoisting:fix-hoisting.jsneeded no edit — RN 0.86 hoists to the rootnode_moduleswhere worklets / nativewind / css-interop resolve it natively; junctions self-heal. The Metro singleton-resolver block was not reintroduced (it caused grey-screen Expo SDK 56 is available (app is on 55) #351).npx expo-doctor— 5 pre-existing findings (none caused by this upgrade, none a native-version incompatibility)All five fail identically on SDK 56 and are unrelated to the SDK bump; expo-doctor is not a CI gate.
splashschema error — thesplashkey is absent from both the SDK 56 and SDK 57 config schemas (verified against the versioned schema API), so this is long-standing. Prebuild currently renders a white splash background (the#020618value is silently ignored). Migrating to theexpo-splash-screenplugin would be a user-visible launch-screen change (white → navy), so it is surfaced here as a recommended separate follow-up rather than bundled into a dependency bump.app.config.jsnot usingapp.json— false positive:app.config.jsdoesrequire("./app.json")and spreadsappJson.expo. expo-doctor's static check can't see the spread.expo-modules-coreinstalled directly — intentional: theandroid-release.ymlguard checkspkg.dependencies['expo-modules-core']for release coherence. Removing the pin would weaken the guard.react-native-device-info(10.3.0 vs 8.7.1) — entirely internal tosp-react-native-in-app-updates(it pulls both directly and viareact-native-siren); a third-party hygiene issue, version-independent of this upgrade.@siteed/audio-studio,sp-react-native-in-app-updates) + "no metadata" (orbit-widgetlocal module) — informational; these are in-use, vetted packages.Manual post-merge steps (maintainer)
Out of automated scope — the deliverable ends at this PR.
npm run android:apkfromapps/mobile; confirm the signed release binary builds and installs on a device. (Prebuild here used a placeholdergoogle-services.json; the real Firebase config is injected at release time.)@siteed/audio-studio), bottom sheets (TrueSheet stacking), AdMob interstitial/rewarded, notifications, IAP (expo-iap/ Play Billing), the home-screen widget, and the app-update prompt. Watch the reanimated Android-memory item (~25–30%, a Hermes/RN-0.85 change carried since SDK 56, not a 57 regression) on lower-end / ColorOS devices.version+versionCodeand run.github/workflows/android-release.yml(workflow_dispatch), which regenerates the project, runs the pinned-native-module guard, builds the signed AAB, and submits via EAS. RaiseAppConfig.MinSupportedVersiononly after the carrying build is live in the Play fleet (Minimum-supported-version gate (client-skew protection) #210).expo-splash-screenplugin (see expo-doctor finding fix: full Nuxt-to-Next.js parity audit #1) to restore the intended navy#020618splash and clear the schema warning.🤖 Generated with Claude Code