Repository navigation
chore: prepare Bitkit 3.1.1 - #1878
piotr-iohk wants to merge 11 commits into
Conversation
fix: run pubky work on its own service queue (cherry picked from commit ebe9bc0)
fix: adopt paykit 0.1.1-rc6 (cherry picked from commit 816f08e)
feat: use the pubky.app profile directly (cherry picked from commit e062e86)
fix: save the profile without waiting for an unchanged copy (cherry picked from commit 831c4d2)
chore: bump ldk-node and vss client (cherry picked from commit 7d57966)
chore: bump ldk-node to 0.7.0-rc.80 (cherry picked from commit 428ed54)
chore: bump paykit to 0.1.1-rc8 (cherry picked from commit 8e86d30)
ci: pass Blocktank regtest auth to E2E jobs (cherry picked from commit 1764309)
|
After 3.1.1 ships from this branch, close this PR without merging. A separate follow-up PR will bump the version and update the 3.1.1 changelog on Validation after the profile-tag fix. Results before the tag-preservation fixThe first migration run hit HTTP 401 in all six wallet funding scenarios. The first staging run hit the same error in both hardware wallet funding cases. The job logs and device artifacts show the failures occurred at the Blocktank regtest deposit call. Android #1820 is now backported in
The authenticated migration run passed native 3.1.0 upgrade and restore and RN 1.1.6 upgrade and restore. Its optional RN sweep failed in legacy-wallet setup after funding worked, before the native app was installed. The standard rerun uses the four upgrade and restore cases. Extended run evidence The E2E-main local run reproduced two Android send behaviors from 3.1.0 on all three attempts. The BIP21 case expected the review route added by Android #1763, and the Lightning amount case reproduced the stale spending limit fixed by Android #1808. The comparison run pins E2E to The Android Compose UI failures are |
[High impact] The changes reach public profiles and payment-related workflows across Bitkit. The PR appears safe to merge, with a non-blocking improvement needed for profile link input. Findings
SummaryPrepares Bitkit 3.1.1, build 197.
Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
Edit[Edit profile] --> Shared[Write pubky.app profile at loaded revision]
Shared --> Tags{Tags changed?}
Tags -->|Yes| Wait[Wait for Paykit copy write]
Tags -->|No| Background[Write Paykit copy in background]
Wait --> Result[Show saved profile or tag error]
Background --> Result
Payment[Prepare payment] --> Size{Largest proof fits?}
Size -->|Yes| Continue[Continue payment]
Size -->|No| Stop[Stop before payment]
Reviews (1) · Last reviewed commit: "Merge pull request #1820 from mdozhdev/c..." · Reviewed by Greptile |
| val links = fitted?.links ?: links.map { | ||
| PubkyProfileLink( | ||
| label = it.label.singleLine(), | ||
| url = it.url.toPubkyAppLinkUrl() ?: it.url | ||
| ) |
There was a problem hiding this comment.
Accepted links can prevent saving
Existing-profile edits skip the link limits used during migration. AddLinkSheet accepts any nonblank label and URL, while this branch neither limits labels to 100 code points nor rejects URLs that fail toPubkyAppLinkUrl(). Instead, ?: it.url sends the rejected value unchanged.
Check these limits before enabling Save and show which link needs fixing, so one invalid link does not make the whole profile save fail.
There was a problem hiding this comment.
Confirmed. The form accepts links that the pubky.app publisher can reject, which blocks saving the profile. The link validation bug is tracked in #1882, with the matching iOS issue at synonymdev/bitkit-ios#1301. The 3.1.1 branch now has the separate restore fix that preserves saved profile tags.
talosmachina
left a comment
There was a problem hiding this comment.
No findings. Prepares Bitkit 3.1.1 (versionCode 197) on release-3.1.1 from v3.1.0: seven backports, the version bump and the collected changelog. Reviewed 47d05d8, full tier, static.
What I checked, and 3 candidates I ruled out
Backports: each of #1798, #1803, #1801, #1865, #1824, #1872, #1873 and #1820 carries the same changed lines as its merge on master (compared line by line against the master merge commit, 0 lines differ).
Version commit ce4d5cb: versionCode 196 to 197, versionName 3.1.1, the four consumed fragments match the CHANGELOG 3.1.1 entries.
Dependency pins: paykit 0.1.1-rc8, ldk-node 0.7.0-rc.80, vss-client 0.5.26-rc2, the same pins as master.
CI on the release branch: CI, E2E, E2E Staging and E2E Migration green on 47d05d8; Android Instrumented Tests still running when I read it.
Build: not built here. On the Bitkit Android track the review is reasoned from the code and CI, since Gradle would run the branch's own build scripts.
Ruled out
- Fragments collected twice after the merge to master: the merge brings back
master's copies of 1798/1801/1803/1870 intochangelog.d/next. Ranscripts/collect-changelog.sh --target nexton the merged tree: they land under[Unreleased], because the script only dedupes inside that section. Same pattern the 3.0.1 hotfix already went through, so a release-process choice, not this diff's defect. - Merge to master conflicts:
PaykitExceptionExt.ktandlibs.versions.tomlconflict againstmaster(GitHub shows the PR as conflicting). Both are master moving ahead of the same backports; resolving to master's side is correct for both. - CI secret exposure:
BLOCKTANK_REGTEST_AUTHis added only to E2E job env in two workflows already onmasterfrom #1820.
Merge confidence: 4/5, no findings, but reviewed statically and the instrumented suite had not finished.
Reviewer model: claude-opus-5-5
Reasoning effort: Not exposed (the run metadata names the model only)
|
@piotr-iohk this conflicts with |
|
@jvsena42 Thanks for flagging the conflicts. This PR is for reviewing 3.1.1, which is based on After 3.1.1 ships from this branch, we’ll close this PR without merging and open a separate PR on |
3.1.1 validation after the profile-tag fixThe release branch now includes the tag-preservation fix in signed commit
|
There was a problem hiding this comment.
Verdict: ⛔️ Request changes. Saved tags can still be lost outside the restore form. The picks, the bitkit-core pin, the version bump and the restore fix itself are correct.
Findings:
-
HIGH: Adding a tag or saving an edit after an unreadable load can wipe the stored tags.
app/src/main/java/to/bitkit/repositories/PubkyRepo.kt:1418loadOwnProfilesets the globaltagsUnavailableas soon as it reads the Paykit copy. It does this before the pubky.app read, and beforeloadProfileapplies the profile to_profile(:883-891).- If the pubky.app read then fails,
_profileis left as it was (:894-902) while the flag says the tags are readable. - Saves take their unread flag from that global at save time: the profile screen's tag sheet (
ProfileViewModel.kt:236→profileEditBase(),:1136) and the edit screen (EditProfileViewModel.kt:64).
How to replicate (flaky network):
- Open Profile. The Paykit copy read fails, so the profile shows with no tags and
tagsUnavailable = true. - Open Profile again (
ProfileViewModel.kt:76loads on each open). This time the copy read succeeds, so the flag goes false, but the pubky.app read fails. Or the user acts while that read is still in flight: the profile body and Add Tag stay enabled during a load (ProfileScreen.kt:142-150,:318-319). - Add tag
x.copyUnreadis false (:1299), sostoredCopyskips the read andkeptTags = [x]. The copy write at:1366replaces stored[a, b]with[x]and reports success. - Or: open Edit, change the name and save.
keptTags = []equalsbaseTags, so the background copy write (:1359-1363) stores no tags, and the screen shows "Profile saved".
This is the same root cause as the restore bug
9eb6a8896fixes: a load clears the flag before the shown profile catches up. The fix only covers the restore form.Suggested fix: set
tagsUnavailablewhere the load's profile is applied (loadProfile'sonSuccess, together with_profile), not at the read. Or carrytagsUnreadon the profile the way iOS does: on iOS,ProfileViewandEditProfileViewpassprofile.tagsUnreadfrom the profile they show. The same code is onmaster. -
LOW: A new profile is created without its tags, with no message, when the copy write fails.
app/src/main/java/to/bitkit/repositories/PubkyRepo.kt:1080publishIdentityProfilelogstagErrorand drops it.- When the Paykit copy write fails,
writeProfileOncereturns the profile withbaseTags(:1370-1376). - The create screen then reports success, and the tags the user typed are gone. No stored tags are lost: on a restore the copy is left as it was.
- In shipped 3.1.0, the same failure failed the whole create.
QA for 9eb6a88:
Check: The picks match the 3.1.1 list ✅ The picks are #1798, #1803, #1801, #1865, #1824, #1872, #1873 and #1820, in that order, each identical to its master merge.
- Checked with
git log --first-parent v3.1.0..9eb6a88. Each pick's changed lines were diffed against its master merge, and 0 lines differ. - Three more commits are direct:
d13769269(the 3.1.0 changelog line now links #1706; the same commit is on master), the version commitce4d5cb25, and the tag fix9eb6a8896. - Nothing else is in the patch. No bitkit-core rc15 (#1876) and no Spark/Breez code: the added lines have 0 matches.
Check: Dependency pins ✅ bitkit-core stays at `0.8.0-rc11`. ldk-node is `0.7.0-rc.80`, vss-client `0.5.26-rc2` and Paykit `0.1.1-rc8`.
git diff v3.1.0...9eb6a88 -- gradle/libs.versions.toml changes only the ldk-node, vss-client and Paykit lines.
Check: Restoring a profile keeps its tags ✅ Without the fix, a restore save erases the stored tags. With it, they are kept. The tag sheet and the edit screen are still exposed (HIGH above).
The bug at 47d05d808, without the fix:
fetchRemoteProfilecannot read the Paykit copy, so the restore form opens with empty tags (PubkyRepo.kt:954-957).- A later
loadOwnProfilereads the copy and setstagsUnavailable = false(:1417-1418). - On Save,
publishIdentityProfilepasses no unread flag, socopyUnreadis false (:1299).storedCopythen skips the read, sokeptTagsis the form's empty list. baseTagsis also empty, so the copy write publishes no tags and the stored tags are gone.
The fix: 9eb6a8896 stores tagsUnread in RestoreBase and passes it to writeProfile (:957, :1073). storedCopy then re-reads the copy and merges its tags with the form's. If that read fails, keptTags is null and the copy is not written.
The other save paths:
- The edit screen takes
profileEditBase()when the form opens (EditProfileViewModel.kt:64), andwriteProfileOnceORs that flag with the live one. That covers a load that marks the tags unreadable after the form opened. It does not cover a form opened while the flag is already wrong (HIGH). - The tag sheet on the profile screen reads the flag when the save starts (
ProfileViewModel.kt:236). It is exposed in the same way (HIGH). - A retry after
PubkyProfileTagsNotSavedErrorcarriestagsUnread = copyUnread(:1373). - The background copy refresh in
loadOwnProfileruns only after a successful read and writes the tags it just read (:1448-1449). - The migration and the avatar move write only to pubky.app, never to the Paykit copy.
The commit: it matches #1884's code commit line for line. Only the changelog fragment is dropped, which is right: this bug never shipped. @jvsena42 reviewed #1884 on master with no findings.
Check: Version and release notes ✅ versionCode goes 196 → 197 and versionName to `3.1.1`. The four fragments are collected into `[3.1.1] - 2026-11-12`, and the compare links are updated.
These notes do not block the release:
Check: CI at 9eb6a88 ✅ CI, E2E (shipped 3.1.0 suite), E2E Staging and E2E Migration all passed.
UI Tests show 253 passed and 3 failed. The failures are amountShowsNumberPadAndContinue, hardwareEditorReturnsToTrezorTab and providerRefundShowsSourceNetworkAndLocalizedAmountAndHonorsEligibility. All three also fail on shipped 3.1.0.
Considerations:
- A fresh-install restore whose lookup fails: the form opens empty with no error (
CreateProfileViewModel.kt:97-100). Saving then cannot overwrite an existing pubky.app profile, because Paykit refuses a write with no revision when a profile exists (PubkyProfileChangedError). A Paykit-only profile would still be overwritten, as it already is in shipped 3.1.0. This is unchanged by this PR.
No device cases were driven here, and I could not show that the new journeys J1–J3 ran at this head. Local E2E used the shipped 3.1.0 suite (611509ee2), and staging used e2e main (0ca01e0a2). The runs come from @piotr-iohk's validation after the tag fix. No E2E case covers the HIGH above. The review covered the full v3.1.0...9eb6a88 patch, statically.
Twin: synonymdev/bitkit-ios#1298 has the same picks and the same tag fix. Its extras are explained: no #1798 on iOS, plus a test commit for the staging fixtures.
Reviewed by claude-opus-5-5
Validation after Ovi’s reviewAndroid release head
|
|
@ovi-reviewer review |
|
@ovitrif @jvsena42 Could you review the Android 3.1.1 release candidate at |
There was a problem hiding this comment.
Verdict: ✅ Approve. The reported profile defects are fixed.
Findings: No new findings. The previously accepted link-validation issue remains tracked in bitkit-android#1882.
QA for 0f23fc2:
Built the reviewed head and drove the listed journeys on Android 15 with disposable regtest wallets.
Edit the public Pubky profile ✅ J1: the public-profile notice is visible, and the saved name remains visible after relaunch.
J1 journey. Changed the name to Edited In Bitkit. A separate public read of the pubky.app profile also returned {"name":"Edited In Bitkit"} with HTTP 200.
j1-edit-profile-2x.2x.mp4
Profile notice, save and relaunch at 2× speed.
Reject an oversized payment request note ✅ J2: the oversized note is refused, while Coffee sends and reaches the payer’s 1,000-sat confirmation.
J2 journey. Pasted the 256-heart fixture. The app showed “Shorten the note and try again.” and kept Send available; no request appeared on the payer. Replacing the note with Coffee sent the request successfully. The payer received it and showed Coffee with 1,000 sats on confirmation. I closed confirmation without paying.
The payer needed regtest funds to reach confirmation; its initial unfunded preparation was a setup limitation.
| Note validation and delivery: requester left, payer right | Funded payer confirmation |
|---|---|
j2-note-comparison-2x.2x.mp4 |
j2-review-2x.2x.mp4 |
Both recordings are at 2× speed; the paired recording is aligned in time.
Explain a rejected Pubky signup code ✅ J3: the specific error appears, authorization remains retryable, and no identity is created.
J3 journey. Opened an ordinary signup grant through Scan → Paste using an invalid token for the reachable staging homeserver. The app showed “The homeserver did not accept this signup code. Try again or use another signup link.” Authorize became available again. Canceling returned to the wallet’s profile introduction. The device log also reported signup_code_rejected.
j3-rejected-signup-2x.2x.mp4
Rejected token, retryable authorization and absent identity at 2× speed.
Check: Profile regressions and release validation ✅ The focused profile tests pass, and current-head CI and staging E2E are green.
PubkyRepoTest, CreateProfileViewModelTest, ProfileViewModelTest and EditProfileViewModelTest passed at this head. They cover the held/failed refresh, unread-tag preservation, partial creation warning and navigation.
Fixed findings:
- Tag preservation: the profile, revision and unread status are installed together; tag actions use one edit-base snapshot.
- Creation warning: setup retains the active identity and surfaces the partial-save warning.
Current-head reports:
Earlier full E2E, migration and native UI evidence remains at its original head in the earlier validation report. I did not rerun those suites.
Covered the full release diff (d137692..0f23fc2), using the common ancestor with the PR base as requested for this release branch. All listed journeys were driven. Dependency updates, USDT receipt matching, migration and injected profile failure/race paths were not driven as device cases; they were reviewed statically, with the profile paths additionally checked by the focused regression tests.
Twin: bitkit-ios#1298 chore: prepare Bitkit 3.1.1 matches the release changes and tag-preservation behavior; this Android head also fixes its own snapshot and creation-warning findings.
Reviewed by gpt-6.1-sol-high
Release PR: reviewed and ready to release, but kept unapproved so it can't merge before the store release.
|
✅ Ready to release: review at 0f23fc2, no open findings. I dismissed both approvals on this PR: a release PR stays unapproved until after the store release, so it can't be merged by accident. |
Twin: synonymdev/bitkit-ios#1298
Prepares Bitkit 3.1.1 (build 197) from
v3.1.0, with the version bump and collected hotfix changelog.Description
Design
No separate release design; profile behavior and QA details are in the linked backports.
Preview
See the profile backport for the feature preview.
QA Notes
Setup
The runs below test
ce4d5cb25704d19d42860380251b57a0b7e6caffonrelease-3.1.1using the E2E repository’smainbranch. Migration uses the archived native 3.1.0 build and includes the extended RN 1.1.6 cases.Journeys
edit-pubky-app-profile.xml— profile changes are shared with pubky.app.request-note-too-long.xml— an oversized Paykit note is rejected before submission.grant-signup-rejected-code.xml— a rejected signup code gets a clear error.Automated Checks
Evidence
Local backend APK assembled and verified; 722 targeted unit tests passed. Detekt and Android Lint passed.
Gaps
Full E2E and migration results are tracked in the linked runs. Device journeys are separate from the local unit tests.
Models used
GPT-6(reasoning:Not exposed)GPT-6(reasoning:Not exposed)The session exposes the model family but not its variant or reasoning setting.