fix(ota): re-land the background-apply rollback fix stranded by #2960/#2963 - #2986
Conversation
Capgo consumes a next()-staged bundle only from installNext(), which runs
from appMovedToBackground(). The reload therefore lands in a process the OS
is about to freeze. Freezing stops threads but not the clock, so on resume
every overdue setTimeout fires at once — webpack's 120 s chunkLoadTimeout
included — and every in-flight import() rejects with a ChunkLoadError for a
local file that was never missing. notifyAppReady sat behind two of those
imports plus React hydration, so it was never called and the plugin rolled
the bundle back.
PEANUT-UI-SVT is the whole chain in one trace: booted at 13:46:04, no
breadcrumb for 23m46s, then four ChunkLoadErrors and the rollback inside
40 ms — one of them the chunk that calls notifyAppReady. PEANUT-UI-SVE is
the milder shape, where the call landed 6 ms past the deadline.
- notifyAppReady now runs from a raw inline script in the root layout,
straight off the bridge stub Capacitor injects at document start. No
chunk, no hydration, no React. The React path still calls it as a
fallback for binaries whose bridge does not expose the stub.
- That trades away Capgo's rollback net, so a boot-incomplete counter
replaces it. It is the better net: Capgo could not tell a bundle whose JS
is broken from one the OS froze and rolled back on both, whereas a frozen
boot resumes and clears the counter.
- A bundle staged by an earlier launch is now applied in the foreground
while the splash still covers the reload, instead of being left to the
background apply. Once per bundle, and only on binaries that can restart
in place; outside that window the background apply remains the fallback.
- import() retries once on a chunk-load failure, since the second attempt
runs with the process scheduled again.
Also makes the failure legible. Capgo's own logs reach Sentry only through
bridge.eval("console.error(...)") into a WebView the rollback is about to
tear down, and the [capgo] prefix filter dropped whatever survived — the
entire population read as one event in 90 days. The two rollback strings
are now actionable, and getFailedUpdate() is reported at launch from the
plugin's own SharedPreferences record, which outlives the reload.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code-analysis diffPainscore total: 7163.66 → 7166.57 (+2.91) 🆕 New findings (21)
…and 1 more. ✅ Resolved (20)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
🖼 Visual diff — 7 screens moved9 of 66 shots changed · 57 identical · baseline
job summary · before/after/diff images — artifact Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data. |
There was a problem hiding this comment.
Chip review — changes requested
The background-apply mitigation is well tested on current native builds, but its replacement rollback path can strand older Android binaries on a broken OTA bundle.
Findings
- BLOCKING · src/utils/native-app-ready.ts:59 · Preserve rollback on pre-8.46 Android binaries
On an Android binary with capacitor-updater < 8.46.0, a broken OTA can still execute this inline HTML but fail before React mounts. Line 64 marks that bundle successful, disabling Capgo's native rollback; after three such launches, this line calls reset(), whose old Android implementation runs _reload() on the shared plugin handler and blocks that handler waiting for notifyAppReady. The reloaded page's notify call cannot run, so reset times out and restores the broken bundle. These binaries are explicitly supported by canRestartInPlace(), so version-gate the early-ready path to preserve native rollback there (or provide a recovery that does not use the blocked handler), and cover the old-binary failed-boot case.
Checked clean
- Confirmed the detached worktree head, supplied base SHA, and merge base exactly match the requested review target.
- Reviewed all 12 changed files across OTA readiness, staged launch apply, chunk retry, splash sequencing, Sentry filtering, and their tests.
- Exact-head aggregate, unit, typecheck, lint, format, analysis, native-export, design-system lint, provenance, and Vercel-comment checks passed; screenshot and preview jobs were still running when checked.
- Verified Capacitor injects native plugin stubs before document head scripts, and compared the pinned 8.51.14 Android updater source with 8.45.9: current reset/set/reload calls are threaded, while 8.45.9 reset/set/reload block the shared handler around app-ready waiting.
- The new Sentry carve-outs keep rollback failures while continuing to suppress transient updater noise.
Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.
Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.
Exact head: 9902287ad11e · Context: repo, provider · Took 17m
Re-lands
1d67a3cc, which was merged into a branch that had been merged away eight seconds earlier and so never reacheddev.What happened
fix/ota-android-restart-deadlock→devfix/ota-background-apply-rollback→fix/ota-android-restart-deadlockThe child landed on a branch that no longer had anywhere to go. Both PRs read as merged, the branch still holds the commit, and nothing anywhere says the fix is missing —
devhas simply never had it. Same shape as the loss #2941 recovered from, eight seconds instead of a stale head.Found by auditing every PR merged in the last 14 days for commits absent from
devby patch-id. Two other gaps turned up and are not in this PR: #2945 is still open with the TASK-22193 review-sheet work, and a docs-only commit sits on api-tsfix/card-geo-prohibited(#1450).What it fixes
Unchanged from the original — cherry-picked with no conflicts, author preserved:
Live in Sentry as
PEANUT-UI-SVT(booted 13:46:04, no breadcrumb for 23m46s, then fourChunkLoadErrors and the rollback inside 40 ms — one of them the chunk that callsnotifyAppReady) andPEANUT-UI-SVE(the milder shape, the call landing 6 ms past the deadline).Net effect on
devtoday: an OTA bundle that applies while the app is backgrounded can roll itself back on Android.src/utils/native-app-ready.tsand its test do not exist ondevat all.Verified
pnpm typecheck,prettier --check .,ds-lintratchet unchangednext buildclean