Conversation
|
Regtest APKDownload bitkit-dev-debug universal APK (expires in 30 days). |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
09d9eb5 to
282f3e4
Compare
jvsena42
left a comment
There was a problem hiding this comment.
Approved — no HIGH/MEDIUM findings at head 282f3e485. (Posted as a comment because GitHub does not allow approving your own PR.)
Checked: Traced the critical-update flag from checkCriticalAppUpdate (launched once in AppViewModel.init) to the top-level branch in MainActivity. The branch comes before the migration, onboarding and wallet branches, so it replaces OnboardingNav, the restore flow and ContentView. The only surfaces left above it are ToastOverlay and SplashScreen. With the NavHosts gone, Back finishes the activity, and sheet or deeplink state has nowhere to render, so the screen cannot be bypassed. It also cannot trap a user who has no critical release: the flag is only raised for a critical build with a higher build number, and a failed fetch leaves it false. The greptile transaction-sheet thread is resolved by 282f3e4. _transactionSheet is only populated through showTransactionSheet, which now returns early while the flag is set, and checkCriticalAppUpdate clears a sheet that is already showing. If wallet creation or restore is in progress when the flag rises, the work continues in WalletViewModel's scope: the node, restore and backup observation are not tied to the disposed composables. The new ON_STOP observer uses the same condition as ContentView's observer, so the node still stops in the background. Routes.CriticalUpdate has no remaining references, and docs/screens-map.md still maps CriticalUpdateScreen.kt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…date-during-onboarding # Conflicts: # journeys/README.md
jvsena42
left a comment
There was a problem hiding this comment.
Approved — no blocking HIGH/MEDIUM findings at head d0b510eb3. (Posted as a comment because GitHub does not allow approving your own PR.)
Inline: LOW — Critical-release precondition cannot be met with repo tooling
Checked: Follow-up on ca84620 (new journey): Reviewed commit ca84620 against MainActivity.kt, CriticalUpdateScreen.kt and AppViewModel.checkCriticalAppUpdate at head d0b510e. The asserted text matches strings.xml (Critical Update, Update\nBitkit now uppercased by Display, Update Bitkit), TOS exists on TermsOfUseScreen, the critical branch precedes OnboardingNav so Terms of Use is not composed, Back has no handler and no NavHost so it leaves the app, and Update Bitkit opens Env.PLAY_STORE_URL. The precondition is the problem, see the finding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jvsena42
left a comment
There was a problem hiding this comment.
Approved — no blocking HIGH/MEDIUM findings at head 5b3e5cff8. (Posted as a comment because GitHub does not allow approving your own PR.)
Inline: LOW — Fallback self-served fixture needs HTTPS and every PlatformDetails field
Checked: Follow-up: Follow-up on 5b3e5cf, which answers the critical-release precondition LOW. The primary setup route is real at head. checkCriticalAppUpdate returns early on isDebug (AppViewModel.kt:5599), and Env.isDebug = BuildConfig.DEBUG. Without E2E, Env.RELEASE_URL is the production updater feed (Env.kt:147-151). The check is isCritical && buildNumber > BuildConfig.VERSION_CODE (AppViewModel.kt:5604), so lowering versionCode (build.gradle.kts:189) below the feed's 188 raises the flag. just build assembleDevRelease is valid because the build recipe takes a task argument. The APK lands in outputs/bitkit/devRelease (build.gradle.kts:343). The release build type does need the release signing config (keystore.properties or the KEYSTORE_* env vars) and is filtered to armeabi-v7a/arm64-v8a (:257-261), as the journey says. The new gate action reports a missing fixture as not run, so the old false fail at Terms of Use is gone. One LOW on the fallback route.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jvsena42
left a comment
There was a problem hiding this comment.
Approved — no blocking HIGH/MEDIUM findings at head 87f3ea03d. (Posted as a comment because GitHub does not allow approving your own PR.)
Checked: Follow-up on 87f3ea0: the fixture-route LOW is fixed. The journey now requires the feed to be served over HTTPS (release builds have no usesCleartextTraffic), to be the complete object with only buildNumber and critical edited so strict Json decoding of PlatformDetails succeeds, and tells the runner to distinguish a bad fixture from a regression via the "Failure fetching new releases" log line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jvsena42
left a comment
There was a problem hiding this comment.
Approved — no blocking HIGH/MEDIUM findings at head 1d524c902. (Posted as a comment because GitHub does not allow approving your own PR.)
Checked: Follow-up on 1d524c9: a behaviour-preserving extraction. RootDestination (MainActivity.kt:364-380) keeps the original branch order — critical update, then migration loading (not in recovery), then onboarding (no wallet, not in recovery), then the wallet — and walletExists is the same walletViewModel.walletExists read in the same composition (MainActivity.kt:131), so the onboarding branch sees the value it read inline before. The critical-update lambda keeps the ON_STOP node-stop observer unchanged, and the wallet lambda keeps ContentView, the auth overlay, the forgot-PIN sheet and the wipe effect. RootDestinationTest pins the order, including critical update winning over onboarding and migration.
# Conflicts: # journeys/README.md
There was a problem hiding this comment.
Verdict: ✅ Approve
Reaudit: diff 1 file.
Findings:
N/A
Audit:
Already done in comment.
QA: emu-1 and emu-2 redroid containers on Android 15 (API 35, arm64-v8a), exact-head devRelease and devDebug builds
-
✅ passed: Cleared app data and cold-launched the exact-head non-debug critical fixture.
1b. ⏭️ skipped: tested on 1d524c9
1c. ✅ passed: Installed and configured Cromite as the Android browser handler, relaunched the exact-head non-debug critical fixture, and tapped Update Bitkit from the Critical Update screen.
<details><summary><b>Test 1c:</b><br> relaunch → tap Update Bitkit: Play Store opens the Bitkit listing.</summary>
<table><tr><td><video src="https://github.com/user-attachments/assets/f35ae488-bd7c-4a4a-b24c-1a889bc0699f"></video></td></tr></table>
<table><tr><td><img src="https://github.com/user-attachments/assets/e678695d-d226-45b1-8ccd-5182a48007ed" width="240"></td></tr></table>
</details>
-
⏭️ skipped: tested on 1d524c9
-
⏭️ skipped: tested on 1d524c9
4a. ⏭️ skipped: tested on 1d524c9
4b. ⏭️ skipped: tested on 1d524c9 -
⏭️ skipped: tested on 1d524c9
-
✅ passed: Installed the exact-head arm64 devDebug APK and confirmed the package DEBUGGABLE flag.
-
✅ passed: Installed the same-version critical fixture over the existing target wallet, then paid its real 3,000-sat invoice from the independent funded Bitkit payer.
Tip
Test 6 worth a journey:
- Install the debug build and clear app data
- Launch Bitkit
- Wait for the startup checks to finish
- Verify Terms of Use remains visible
- Verify Critical Update is not visible
Test 7 worth a journey:
- Create independent target and payer wallets
- Fund the payer Savings balance and transfer funds to Spending
- Create a 3,000-sat Lightning invoice on the target
- Install the critical release over the target without clearing data
- Launch the target and verify Critical Update
- Pay the invoice from the payer
- Verify Critical Update remains and no transaction sheet appears
Coverage:
QA: 10 of 10 manual tests passed
Reviewed by gpt-5.6-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)



Related to #804
Related to #1275
This PR shows the mandatory update screen at the top level of the app, so it also appears during onboarding and restore instead of only after the wallet home screen loads.
Description
Out of Scope
AppViewModel/ home timed sheets: the non-critical "Update available" sheet still shows only from the home screen. This matches iOS, which also gates the non-critical sheet on the home view. bug: app update check only gates the home screen #804 stays open for it.AppScene.CriticalUpdateScreen.kt: layout unchanged. The illustration renders larger on tall devices than in the 375x812 Figma frame; this predates the PR.journeys/app-update/critical-update-onboarding.xml: the journey is committed, but it is not run here — it needs a non-debug build and a local change to reach a critical release, which the journey now documents as its setup route.Design
Preview
The screenshots come from a temporary, uncommitted build that faked a critical release on an existing wallet, because debug builds skip the release check and reaching onboarding would have meant wiping a funded wallet. The onboarding case itself was not recorded.
Launch, splash, Home, then about 6s later the release check completes and the app switches to the Critical Update screen with no PIN overlay:
critical-update-flip-existing-wallet.mp4
No "before" capture: on master the navigation event is emitted with no replay and its only listener lives in the wallet content view, which is not composed during onboarding, so there is nothing to show.
QA Notes
Manual Tests
Needs a non-debug build and a release feed listing a critical Android build with a higher build number than the installed one.
regression:Release feed with a newer non-critical build → Home: "Update available" sheet still shows, no Critical Update.regression:Debug build → launch: no Critical Update screen.The journey below captures test 1 as an agent-run spec. It is committed at
journeys/app-update/critical-update-onboarding.xml(commits ca84620, 5b3e5cf, 87f3ea0), with its rows added to the suites and cross-platform tables injourneys/README.md. It cannot be run from a stock build of this repo: debug builds skip the check, and neither release feed reachable at build time satisfies it, so the journey now gates on that as its first action and spells out the local setup needed:journeys/app-update/critical-update-onboarding.xml
Automated Checks
AppViewModelSendFlowTest.ktcovers the critical update check. A newer critical build sets the flag; a newer non-critical build, the same build number marked critical, and a failed fetch do not; debug builds skip the fetch entirely.AppViewModelSendFlowTest.ktalso covers the transaction sheet interaction: raising the flag clears a showing sheet, and a later payment cannot open one.RootDestinationTest.kt, which rendersRootDestination— the branch chain, extracted out ofonCreateinto a stateless composable takingisCriticalUpdateRequired,isShowingMigrationLoading,walletExistsandisRecoveryModeplus a content slot per branch — with a tagged stub per slot. Nine cases, each asserting one slot renders and the other three do not: the critical update wins over migration loading, over onboarding, over the wallet and in recovery mode; migration loading beats onboarding; recovery mode skips both migration loading and onboarding; and the plain onboarding and wallet cases. Moving the critical branch below any other fails four of them.ON_STOPobserver is still checked by hand: it composes only inside thecriticalUpdateslot, but asserting the stop itself needs aTestLifecycleOwnerandlifecycle-runtime-testingis not a dependency in this project.just build,just compile,just test,just lint.RootDestinationTest.ktwas compiled only, throughjust build compileDevDebugAndroidTestKotlin; instrumented tests need a device and were not run here, they run in CI'sui-tests.yml. The follow-up commits 5b3e5cf and 87f3ea0 touch onlyjourneys/app-update/critical-update-onboarding.xmlandjourneys/README.md, so they need no Gradle run; the journey XML was checked to parse.