20f5c459d6
`reloadAsync()` restarts the JS runtime but not the native process, and `expo-linking` keeps handing out the URL the app was opened with (its native registry outlives the runtime). The `previousIntentUrl` guard lives in the JS runtime, so it was wiped by the reload and the launch URL was handled a second time: `intent/apply-ota` prompted to apply the deployment again, which reloaded again, and looped. Reloads now go through `reloadWithUpdate`, which leaves a marker in device storage, and the intent handler seeds its guard with the launch URL when it sees that marker, so the URL is treated as already handled. Also fix the pull request deployment detection that made this a loop rather than a stray prompt. `currentlyRunning.channel` is the channel configured in the native build (`production`/`testflight`), not the channel an update was served from - our updates service selects that with the `channel` extra param - so it never started with `pull-request` and the back-off guards in `useOTAUpdates` were dead. On a TestFlight build that meant the automatic check reset the channel param and offered to relaunch onto the regular bundle, which re-fired the intent, which re-applied the deployment. Instead, record the update id we apply and compare it against the running one; reverting drops the record so the automatic checks resume. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DEz4XBc47YabrrCzSfcRXj