Repository navigation
fix: stabilize contact selection and amount layouts - #1003
Conversation
[Medium impact] The changes affect payment entry and contact selection in a few bounded app workflows. The PR appears safe to merge, with a non-blocking setup correction needed in the new amount journey. Findings
SummaryThis PR disables contact rows while payment details resolve and updates the shared request/subscription amount screen.
No runtime tests or visual captures were performed. The review environment is Linux and cannot render the iOS views. Reviews (1) · Last reviewed commit: "fix: stabilize contact selection and amo..." · Reviewed by Greptile |
talosmachina
left a comment
There was a problem hiding this comment.
No findings. Disables every contact row while a saved-contact payment resolves, and moves the request/subscription amount screen into a scroll view with leading-aligned amount, wrapping BTC/USD controls and a divider above the keypad. Reviewed 6ba39e0, full tier.
What I checked, and 4 candidates I ruled out
Read in full: SendContactSelectView.swift, CreatePaymentRequestView.swift (PaymentRequestAmountView), PubkyContactRow.swift, NumberPad.swift, NumberPadTextField.swift
Call sites traced: PaymentRequestAmountView from ReceiveSheet.swift:150 and SubscriptionAmountView (CreateSubscriptionView.swift:251); both keep their container identifiers, and the PaymentRequest* field, asset, unit and continue identifiers are unchanged.
CI: validate and Greptile green; Run Tests, Run Integration Tests and build-local still pending at review time.
Not built here: iOS does not build on Linux, so this review is reasoned from the code and CI, not from a simulator run.
Ruled out
- Rows stay disabled after a failed resolve:
payclearsselectedContactKeyin adefer, so every exit path (toast, thrown error, cancellation, navigation) re-enables the list. - Spacers collapse inside the new
ScrollView: the content carries.frame(minHeight: geometry.size.height, alignment: .top), so the two flexible spacers still share the leftover height and Continue sits at the bottom when everything fits. - Asset/unit actions lost in the stacked fallback:
amountActionsemits the assetHStackplus the optional unit button, and bothViewThatFitsbranches render the same builder, so the BTC-only unit toggle and conversion handlers are identical in either layout. - Keypad taps eaten by the scroll view: plausible bounce on a fixed-size pad, but not demonstrable without a device run; not posted.
Merge confidence: 4/5, no findings, but the build and test jobs had not finished and the layout was not run on a simulator.
There was a problem hiding this comment.
Verdict: ✅ Approve: one LOW layout shift, and all six cases passed at 71342ec.
Findings:
-
LOW
Bitkit/Views/PaymentRequests/CreatePaymentRequestView.swift:344: switching between BTC and USD now moves the controls. The BTC amount (NumberPadTextField, with its fiat line) is about 24 pt taller than the USD amount (NumberPadAmountText). The flexibleSpacerthat used to absorb the difference is now a fixed 16 pt gap.- Tapping USD moves the BTC/USD chips and the divider up 24 pt and the keypad up 12 pt, on both iPhone 17 Pro and iPhone SE.
- At the largest text size the shift is 52 pt for the chips and 89 pt for the keypad, because the stacked unit button disappears too.
- On base 7b3f093 the chips and keypad stay where they are. Giving the amount slot the BTC branch's height in both modes would keep them fixed.
Head, BTC then USD Base, BTC then USD 

QA for 71342ec:
Tested on iPhone 17 Pro and iPhone SE (3rd generation) simulators, iOS 26.5. Debug build on staging regtest, with the bitkit-docker USDT fixture and Trezor emulator. Two wallets were linked as contacts.
Test 1: contact rows lock while payment details resolve ✅ Only the tapped row spins, every row is disabled, and a failed resolve shows the error and re-enables them.
- Failed resolve: I tapped a contact that cannot resolve. Its row alone showed the spinner, both rows dimmed, and the accessibility tree reported both rows as not enabled. A tap on Bob in the meantime opened nothing. After about 14 s the "Unable To Pay Contact / Please try again." toast appeared and both rows became tappable again.
- Retry: Tapping Bob showed his spinner with both rows dimmed. A tap on the other row was ignored, and one amount screen opened for Bob after about 12 s.
- Both waits come from contact resolution on staging, with the spinner shown the whole time.
test1-contact-rows.2x.mp4 |
Test 2: request amount layout in USD ✅ The amount sits leading under the header, the BTC/USD controls trail directly below it, a divider sits above the keypad, and details keep $1,50.
This was a request for Bob, with the simulator on a comma-decimal locale. BTC showed the same structure with the unit button beside BTC/USD, and Continue stayed at the bottom. I closed the request without sending.
test2-request-amount.2x.mp4 |
Test 3: subscription amount in BTC and USD ✅ Same layout in both, nothing overlaps the keypad or Continue, and accepted methods are Savings only for BTC and USDT only for USD.
- Amounts: 1000 sats, then $1,50.
- Exclusive methods: Tapping USDT while in BTC switched the amount to USD and turned Savings off.
- Recipient: Bob was selectable as the recipient. I closed the sheet without proposing.
test3-subscription-amount.2x.mp4 |
Test 4: compact width and largest text ✅ On the iPhone SE everything fits at default text, and at the largest size one scroll brings Continue fully into view.
- Largest text, BTC: the unit button stacks under BTC/USD, and Continue starts below the fold.
- Scrolling: a scroll that started on the keypad entered no digit. $1,50 carried through to the details.
- Subscription amount: behaved the same at that size.
- Width: 375 pt is the narrowest width these simulator runtimes offer, so 320 pt was not driven.
- Against base 7b3f093 at the largest size: base cuts the unit button to "BIT…" and leaves Continue half off-screen with no way to scroll. The
000key reads "0…" on both builds;NumberPadis unchanged.
test4-compact-large-text.2x.mp4 |
Test 5: USDT wallet and Receive layout ✅ Savings and USDT open with the same opaque slide, Show Details keeps its position across Receive tabs, and USDT keeps Network: Arbitrum One after the network list.
- Show Details: on Savings and USDT it stayed at the same position on every switch, above the home indicator. Spending shows Receive Lightning Funds in that slot.
- Network: after going back from the network list, Show Details opened the Arbitrum One address.
- SE, default and largest text: the network row and Show Details stayed visible. The network name truncates at the largest size.
test5-wallets-receive.2x.mp4 | test5-receive-compact.2x.mp4 |
Test 6: hardware tab label ✅ With a paired Trezor emulator, the hardware tab reads Trezor on every switch and never changes to Hardware.
I switched nine times between Trezor, USDT and Savings. At the largest text size the tab truncates to "Tre…", and its accessibility label stays Trezor.
test6-hardware-label.2x.mp4 |
Not from this PR: on the iPhone SE the USDT Receive tab's Network button sits over the Edit/Copy/Share row, so tapping Copy opens the network list and leaves the clipboard unchanged. Base 7b3f093 does the same, and this PR does not touch Receive.
existing-usdt-copy-head-vs-base.2x.mp4 |
Covered: the full diff, 7b3f093..71342ec. Every changed path had a device case. Pulling the amount sheet down from mid-screen while at rest still dismisses it, the same as on base.
Twin: synonymdev/bitkit-android#1564 matches on the contact picker. On both, the spinner stays on the selected row and every row is disabled. Android also announces the loading row to accessibility services; iOS rows only report disabled. The amount-entry layout from #974 is not part of it.
|
Also addressed the compact Receive problem noted in the review, in f55dc6f. The USDT QR now fits above Edit/Copy/Share, as it does in the Bitcoin receive layout, so Network does not cover the actions. The shared QR logo scales down only when space is limited; the existing maximum size is retained. Address details still scroll, and copy/share/URI contents are unchanged. Checked production-view renders at 320/375 pt and normal/largest text. All four compact QR renders decode to the original payment URI; a new regression decodes the actual USDT and Bitcoin QR components at compact/normal heights. All three receive tests pass, with no new format or translation findings. Android already bounds the QR; its five receive-copy/navigation tests pass, and #1565 has the matching QA journey. The enlarged-text truncation in the unchanged shared buttons is separate from this overlap fix. The journey still calls for reviewer checks of the physical tap targets. |
jvsena42
left a comment
There was a problem hiding this comment.
Code scan: no findings.
Checked and clean:
- USD input uses
Locale.current.decimalSeparatorand BTC uses..handleInputandparsePaykitAmountInputare unchanged. - The hidden
amountControlscopy and the always-present BTC field are out of the accessibility tree and can't be tapped. QR.swift: the QR content, the share image andcopyValueare unchanged. Only the logo's sizing source moved, and bothQrAreacallers now have a bounded height.SendContactSelectView.disabled:pay()clearsselectedContactKeyin adefer, so every exit re-enables the rows.
Scope differs from synonymdev/bitkit-android#1565 (QR logo and the contact .disabled change are iOS-only; Android already has the latter on master). The two new journey files are byte-identical.
Device gate (partial): not run yet. Pending: the three journeys on the simulator, including whether the Bitcoin QR sits lower on tall layouts now that the logo GeometryReader makes the ZStack flexible. Perf: not relevant. The device result follows in a second review.
There was a problem hiding this comment.
Verdict:
Findings: No new code findings. The previous BTC/USD layout-shift finding is fixed.
QA for b087c50:
Built and tested on iPhone 17 Pro and iPhone SE (3rd generation) simulators, iOS 26.5, using regtest and test USDT. Nothing was paid or published.
Test 1: request and subscription amounts, normal text ✅ BTC/USD keeps the divider and keypad stationary, and 1.50 USD reaches details.
Accessibility bounds confirm the positions in both callers. A 1000-sat subscription selects Savings; selecting USDT switches the denomination to USD and deselects Savings. Both forms were closed without sending.
amount-normal-2x.2x.mp4
Test 2: compact amount entry and largest text ✅ Wrapped controls stay in place across BTC/USD, and scrolling reaches the keypad and Continue.
On the iPhone SE, the divider and keypad bounds match in both denominations for requests and subscriptions. All BTC keypad controls were tapped; after clearing input, 1000 sats and 1.50 USD reached details. Default-text compact layout was also checked. The unchanged 000 label still truncates at the largest size, as recorded in the earlier review.
amount-accessibility-2x.2x.mp4
Test 3: compact Receive Copy and QR ✅ Copy reaches the plain address without opening Network, and the displayed QR decodes at normal and largest text.
Copy occupies y483–523; Network starts at y539. Clipboard reads match the QR’s receiving address. Core Image decoding of the captured display returns the correct Arbitrum payment URI at both text sizes; Bitcoin codes also decode. The network round-trip retains Arbitrum and address details scroll. Existing button and network-label truncation remains at the largest text size.
receive-compact-2x.2x.mp4
Test 4: tall Bitcoin and USDT Receive ✅ Both displayed codes decode, their logos remain centered, and the actions stay reachable.
Core Image decoded the captured Bitcoin and USDT displays to their original payment URIs. Copy, the network round-trip and Show Details retain the USDT address and Arbitrum network. Savings, Spending and USDT switching keeps the footer reachable; Spending shows its Lightning onboarding action because this wallet has no channel.
receive-tall-2x.2x.mp4
Test 5: contact-detail Request or Pay ❌ The funded warm Pay handoff takes about 6.5 seconds against the journey’s three-second target.
Pay shows progress with Request disabled during lookup and eventually opens one send amount screen. Closing permits retry, and Request opens the request amount screen. A timed accessibility probe still sees Request Or Pay after 3.2 seconds and first sees SendAmount after 6.5 seconds. The timing assertion did not pass. This caller and its navigation helper are unchanged in the reviewed delta, so this is an unmet journey expectation rather than a new code finding on this PR.
contact-request-or-pay-2x.2x.mp4
The earlier passing report at 71342ec supplies unchanged contact-row locking/failure-retry, opaque wallet-transition and paired hardware vendor-label evidence. QR rendering was rechecked here. No planned case remains unrun. The narrowest device width driven was 375 pt; no 320 pt device or fresh Figma comparison is claimed. The conditional saved-activity/deposit-details step had no saved records to open and its routes are unchanged.
Reviewed the complete delta 71342ec..b087c50 and the prior open finding. Every changed runtime component had a device case; the test, journey and changelog edits were source-reviewed.
Pair PR: synonymdev/bitkit-android#1565 fix: stabilize usdt wallet and payment layouts matches the amount-stability intent and shared journey text. Android already bounds its Receive QR; the shared QR logo adjustment here is iOS-specific. Android was not rebuilt in this run.
|
Thanks for confirming the layout fixes and measuring the contact handoff. The 6.5-second result remains an unmet latency target; I am not counting that journey assertion as passed. This caller is unchanged by the layout PR, so I’m keeping the latency investigation under #868 rather than adding speculative timing changes here. The separate fixed-request work in #1014 adds duration-only resolution/setup diagnostics for incoming requests; it does not claim to explain this contact-detail delay. |
|
Resolved the master conflict while retaining the compact amount layout and master’s The app builds and all 44 focused amount, conversion, keypad, USDT display and component-sizing tests pass on the simulator. SwiftFormat is clean; translation validation reports no errors and the same 14 warnings as current master |
jvsena42
left a comment
There was a problem hiding this comment.
The delta since my review, 2297f2c, is a merge of master. The one conflict, in PaymentRequestAmountView.body, keeps this PR's GeometryReader/ScrollView layout and master's inputValue edits (lines 405, 430). usdtInputAmount display and the locale separator are intact, and QR.swift, UsdtReceiveView.swift and SendContactSelectView.swift are untouched. No code findings. One LOW on the journey is inline.
Device gate (partial): not run yet. Pending: paykit-amount-layout.xml, usdt-wallet-receive-layout.xml and contact-request-or-pay.xml step 19 on the simulator.
There was a problem hiding this comment.
Verdict:
Findings: No new code findings in this delta. The earlier amount-layout fix remains intact. The conditional failure/retry journey setup remains deferred to synonymdev/bitkit-ios#1031, as agreed in the review thread.
QA for 2297f2c:
Built and tested on iPhone 17 Pro and iPhone SE (3rd generation), iOS 26.5 simulators, with linked contacts and fixture USDT. No payment, request or subscription was submitted. Videos are at 2× speed.
Test 1: request and subscription amounts, normal text ✅ BTC/USD keeps the divider and keypad stationary, and 1.50 USD survives editing and reopening Amount.
Accessibility bounds confirm stable positions in both callers. The comma-decimal display retains 1,50 after conversion and draft restoration; request digit editing also returns 1,50. A 1000-sat subscription selects Savings; selecting USDT converts to USD and deselects Savings.
amount-normal-2x.2x.mp4
Test 2: compact amount entry at largest text ✅ Wrapped controls remain stationary across BTC/USD, and scrolling reaches the keypad and Continue.
Both request and subscription details retain 1.50 USD. Accessibility bounds confirm the divider and keypad positions; all BTC keypad controls were tapped after scrolling. Default-text compact request geometry was also checked. The 000 label still truncates, as in the earlier report.
amount-compact-2x.2x.mp4
Test 3: compact Receive and wallet navigation ✅ Copy reaches the plain address without opening Network, and the displayed USDT QR decodes at normal and largest text.
Clipboard reads and Core Image decoding match the receiving address and Arbitrum payment URI. Network remains Arbitrum after returning; address details and bottom actions remain reachable. Savings, USDT and saved-activity transitions stay opaque. At largest text, Network and Show Details wrap; the short Edit/Copy/Share labels still clip.
receive-2x.2x.mp4
Test 4: funded contact-detail Request or Pay ❌ The warm Pay handoff takes about 6.4 seconds against the three-second target.
Timed accessibility polling still sees Request Or Pay with Request disabled at 3.4 seconds and first sees SendAmount at 6.4 seconds. Progress is shown, one amount screen opens, closing permits retry, and Request opens PaymentRequestAmount. This caller is unchanged; the author’s response keeps the existing latency investigation under synonymdev/bitkit-ios#868. I have not counted the timing assertion as passed.
contact-handoff-2x.2x.mp4
The passing report at 71342ec supplies unchanged selected-row locking, failure/retry and paired hardware-label evidence. The report at b087c50 supplies unchanged tall Bitcoin QR evidence; compact USDT decoding was repeated here. No planned case remains unrun. The narrowest device was 375 pt; no fresh Figma comparison or 320 pt device run is claimed.
Reviewed the complete delta b087c50..2297f2c and prior open feedback. The integrated request/subscription amount paths were driven; the rest of the master integration was source-reviewed outside these layout journeys.
Pair PR: synonymdev/bitkit-android#1565 fix: stabilize usdt wallet and payment layouts matches amount stability and compact QR intent. Its contact failure setup differs as tracked in synonymdev/bitkit-ios#1031. Android was not rebuilt in this run.
Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest
|
Device gate (partial):
|
|
Thanks for the completed compact-device checks. I have kept the three-second handoff assertion explicitly unpassed and linked the 6.4-second measurement under #868 in Out of Scope. I passed the measurement to the separate performance investigation; this layout PR will not change contact preparation speculatively. The shared controls’ largest-text clipping was also recorded in the earlier base comparison and remains outside this layout delta. The deterministic failure setup remains tracked in #1031. |
|
Fixed the matching fresh-amount bug found on Android #1565 in e539585. Selecting USDT before opening a new subscription amount now allows digit entry immediately; existing cents and conversion behavior stay intact. Verified the actual iOS editor with zero → 150.50 and existing 150.50 → 150.55, including the Continue result. All 44 focused regression tests passed, changed-file formatting is clean, and all PR commits are locally signed and GitHub-verified. The amount journey now covers fresh USDT selection and reopening the entered amount. |
|
Published signed normal merge Validation: 13 focused tests and 2,874 unit executions passed; UI had 38 passes, four existing fixture skips and one failure waiting for RecipientManual while the node was starting. That exact failure was independently reproduced on current master The body now separates this evidence from earlier device results and retains the incomplete J1–J3 assertions. New-head unit/integration/E2E CI is running. Human re-review is requested for the integration; no review findings were dismissed or resolved by this update. |
There was a problem hiding this comment.
Verdict: ⛔️ Request changes — the existing keypad-legibility and contact-timing assertions remain unpassed at 754a04c.
Findings: No new code defects found in the delta. The merge preserves the stable amount layout while adopting master's dot-decimal input. The failures below remain the previously deferred journey assertions.
QA for 754a04c: I built this head and tested iPhone SE (375 pt) and iPhone 17 (402 pt) simulators on iOS 26.5. Contact testing used mutually saved and linked disposable identities and a payer with 25 fixture USDT. No payment, request or subscription was submitted. Videos play at 2×; timing observations use monotonic accessibility samples from the original run.
Test 1: J1 — Compact request and subscription amounts ❌ Fresh entry, preserved cents, stationary controls and scrolling work; the shared 000 label still reads 0….
At largest accessibility text, a fresh USD request and a subscription with USDT selected before opening Amount accept 1.50 without Delete. Continue and reopening preserve cents. Accessibility bounds put the divider, first keypad row and Continue at y=275, y=292 and y=576 in BTC and USD in both editors. Every Bitcoin key was tapped; clearing and entering 1 then 000 produces 1,000 sats. The visible 000 caption remains truncated in both editors.
| Request amount | Subscription amount |
|---|---|
J1-compact-request2x.2x.mp4Entry, reopening, denomination switching and keypad. |
J1-compact-subscription2x.2x.mp4USDT-first entry, reopening and keypad. |
J1-compact-fresh-request2x.2x.mp4
Fresh USD request entry at largest text, starting from zero.
Test 2: J1 — Standard-width amount controls and decimal entry ✅ Both editors preserve 1.50, fit one control row at normal text and wrap the unit at largest text.
In a comma-decimal region, the numeric field and decimal key use a dot, and Continue/reopening retain 1.50. BTC/USD switching leaves the divider, keypad and Continue stationary at each text size, checked through accessibility bounds. Both request and subscription fit controls on one row at normal text and wrap the unit at largest text. Selecting USDT clears Bitcoin methods; selecting Savings clears USDT, checked through the selected method icons.
| Request amount | Subscription amount |
|---|---|
J1-standard-request2x.2x.mp4 |
J1-standard-subscription2x.2x.mp4 |
Test 3: J3 — Funded warm contact Request or Pay ❌ One warm handoff exceeds three seconds; progress, single-flow navigation and Request work.
Pay shows progress with Request disabled during lookup and opens one SendAmount screen showing 25 fixture USDT available. Closing allows retry; Request opens PaymentRequestAmount.
On one warm attempt, the physical tap command completes at 0.778 s. The 3.956–4.392 s accessibility observation still has no SendAmount, over 3.17 s after tap completion. SendAmount is first observed by 5.448 s from command start. The repeat is faster, first observed by 3.345 s from command start. These samples bound observations rather than exact presentation times.
J3-warm-retake2x.2x.mp4
Two warm Pay attempts, closing and retry, then Request opening the amount editor.
Test 4: J3 — Contact picker locking, failure and retry ✅ All rows lock during lookup; a prepared endpointless peer errors and a payable retry opens one amount screen.
Only the selected contact shows progress. Accessibility samples confirm both rows are disabled; physical taps on the other row during lookup open no second flow. After the first payable selection, dismissing the whole Send presentation and reopening it allows the endpointless-contact leg. Its error re-enables both rows, and selecting the payable contact then opens one SendAmount screen. Dismissing that presentation returns home.
J3-picker-exact2x.2x.mp4
Payable selection, full dismissal/reopening, endpointless error and payable retry.
I retained these passing assertions where the merge leaves their implementation unchanged:
- Compact and standard Receive QR decoding, both Copy warnings, anchored footers and wraparound at normal/largest text.
- Opaque wallet transitions and saved USDT activity navigation.
- Paired hardware vendor-label check.
Scope: the contiguous ac54f6e…754a04c delta and outstanding feedback. Imported master scanner/send ownership, USDT payment protection, blocked retry, profile, terms and formatting changes were source-reviewed outside these layout journeys. No planned case remains unrun. No fresh 320-point device, Figma comparison or hardware pairing was driven. I did not rerun native suites locally; current-head integration CI passed, while unit CI and E2E CI remain pending.
Twin:
- synonymdev/bitkit-android#1565 fix: stabilize usdt wallet and payment layouts at
aba8c0fmatches both layout journey files byte-for-byte. Android was not rebuilt here.
Reviewer model: gpt-6.1-sol
Reasoning effort: high
Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest
jvsena42
left a comment
There was a problem hiding this comment.
Code scan of b17f49c..754a04c: one LOW inline, reproduced on the simulator.
- The QR-logo and USDT-footer thread is fixed (
b17f49coverlay,17e6533spacer). That spacer is what causes the LOW, so it's a new thread, not a reopen. - The merge resolutions are correct:
ac54f6ekeepscopyMessageon both copy paths and the ProgressView;754a04ckeeps the PR layout with master's logic. - The
SendContactSelectView.disabledguard, the hidden BTC field andQR.swiftare unchanged sinceb17f49c.
Device gate (partial): 754a04c — the swipe band is verified (above); usdt-receive-swipe-tabs.xml and usdt-wallet-receive-layout.xml are still running. Skipped — already driven at this head: J1 and J3 (iOS, by ovi-reviewer: #1003 (review)). Device results will follow in a separate review.
|
Device gate (partial):
Still pending: step 5 (hardware vendor label), which needs the Trezor emulator paired. It wasn't free this pass and will run next. Skipped — already driven at this head: J1 |
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Re-reviewed the full pull request at 754a04cb after the comparison base moved to e4bf44a2. The merge base is e4bf44a2e0baa78060871d6792e0ecf2143a1cca. No coverage baseline was supplied, so this pass covered the whole diff.
1 actionable finding — resolve or provide an evidence-backed rebuttal.
Send contact rows are disabled for the whole saved-contact lookup, the selected row keeps its spinner, and a second pay returns immediately while one lookup is in progress. Request and subscription amounts keep the Bitcoin field in the layout while USD is shown, so the divider and keypad stay in place across denomination changes. A new USD amount stays empty until the first digit; a non-zero amount reloads through inputValue, which keeps two fraction digits. On USDT receive, the QR and Edit/Copy/Share sit above a flexible spacer, with Network and Show Details under it, and the center logo is an overlay capped at 20% of the shorter side and 68pt.
Contact disabling matches bitkit-android#1564 at c1f1c0a784a04107617eccef5fdc698b9dc08789. The receive column matches UsdtReceiveContent at that revision: QR block, then network, then details, with address details scrolling on their own. The linked handoff amount frame and refactor amount frame stack amount, trailing controls, divider, keypad, and Continue in that order. Comparison was source and design only.
Device testing: not performed in this review. Unit and UI tests were not executed. J1–J3 remain the device contract, including the author-tracked keypad legibility (#1036) and contact-preparation timing (#868) limits.
Earlier findings reconciled at this revision: 1 still present, 4 verified fixed, 2 tracked separately outside this PR. Tracked separately (outside this PR; unresolved): The 000 keypad label still truncates at the largest text size. Issue #1036 is open. At 754a04c the shared integer keypad still sets the key text to 000 in NumberPad.swift:75, and NumberPadButton draws it at a fixed 24pt font without minimumScaleFactor. That file's blob 3ecc8a591e72b30ae6e7731bff0cbbcd4e53a2d4 is identical at base e4bf44a, and the PR diff does not include Bitkit/Components/NumberPad.swift. The amount editor still calls this keypad; the type expression getNumberPadType is the same expression on both sides of the amount-editor diff, and AmountInputViewModel.swift:189 still returns .integer for modern Bitcoin. The current-head device review still reports the visible caption as 0… at the largest text size. This PR does not change that shared label. This is not a runtime verification or merge-readiness declaration. Tracked separately (outside this PR; unresolved): Contact-detail Pay still exceeds the three-second journey target. Issue #868 is open. The timed path is ContactDetailView.onPayTapped at lines 170-184, which waits for an eligible target and then opens Send or a request. ContactDetailView.swift blob 2f53f01ea491a6ca51b07dbe1e06ce426b9ab4ab is identical at base e4bf44a and head 754a04c, and that file is absent from the PR diff. The three-second SendAmount action in journeys/payment-requests/contact-request-or-pay.xml:12 is outside the file's only diff hunk, which starts at line 17. The PR's contact-list change is SendContactSelectView.swift:46, .disabled(selectedContactKey != nil), on the Send → Contact picker. That picker is a different entry from ContactPay. The current-head review still records a warm ContactPay sample past three seconds. This PR does not change that handoff. This is not a runtime verification or merge-readiness declaration.
Findings
- [LOW] A horizontal swipe in the empty band between Edit/Copy/Share and Network no longer switches Receive tabs — inline at
Bitkit/Views/Wallets/UsdtReceiveView.swift:222. Still present from an earlier unpublished review.
Reviewer model: grok-4.7
Reasoning effort: high
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Follow-up review of the full pull request at 7ff5320, since 754a04cb, with merge base e4bf44a2. Inherited baseline: review 5649620981. This pass inspected the USDT Receive blank-band swipe hit target, the new UI-test swipes, and the updated receive swipe journey. Unchanged contact selection, amount entry, QR-versus-details and footer/logo layout, the three earlier journeys, receive layout tests, and the prior Android/Figma comparison remain inherited.
No new actionable code findings.
The address page wraps receive content in a full-height stack and applies contentShape(Rectangle()) before the existing simultaneous tab swipe, so the flexible gap between Edit/Copy/Share and Network belongs to that gesture. Swipe direction stays on the existing handler: right selects the previous tab, and left wraps to the first tab. Android at c1f1c0a attaches swipeToChangeTab to the weighted address column, and 78798d2a still does. That source comparison matches the new iOS hit target.
Validation: unit-tests run 40299418579 on this revision passed UsdtWalletUITests.testReceiveAndInvalidPaymentStaySeparateFromBitcoin in 101s on iPhone 16 / iOS 26.2. That case swipes both ways through the measured gap, requires the USDT receive screen to close, and checks the same amountless QR after returning. It leaves Spending versus Savings to journey J4. Device testing: not performed in this review, and J4 was not driven.
Earlier findings reconciled at this revision: 1 verified fixed, 2 tracked separately outside this PR. Tracked separately (outside this PR; unresolved): The 000 keypad label still truncates at the largest text size. Issue #1036 is open and describes this shared 000 label. At 7ff5320 NumberPad.swift:75 still sets the integer key text to 000, and NumberPadButton at lines 147-151 draws it at a fixed 24pt font with frame(maxWidth: .infinity) and no minimumScaleFactor. That file's blob 3ecc8a591e72b30ae6e7731bff0cbbcd4e53a2d4 is identical at base e4bf44a, and the PR diff does not include Bitkit/Components/NumberPad.swift. CreatePaymentRequestView.swift:362-363 still selects that keypad, and AmountInputViewModel.swift:186-189 still returns .integer for modern Bitcoin; that view-model blob c586885eb2e70a8f644a7528d1589f069c4657a3 is also identical at both SHAs. This PR does not change the shared label. This is not a runtime verification or merge-readiness declaration. Tracked separately (outside this PR; unresolved): Contact-detail Pay still exceeds the three-second journey target. Issue #868 is open. The timed path is ContactDetailView.onPayTapped at lines 170-184, which waits on eligibleTarget(publicKey:waitingAtMost: .seconds(2)) and then opens requestOrPay or payContact. ContactDetailView.swift blob 2f53f01ea491a6ca51b07dbe1e06ce426b9ab4ab is identical at base e4bf44a and head 7ff5320, and that file is absent from the PR diff. journeys/payment-requests/contact-request-or-pay.xml:12 still requires SendAmount within 3 seconds after ContactPay; the file's only diff hunk is the line 19 Send → Contact action. SendContactSelectView.swift:46 adds .disabled(selectedContactKey != nil) on the Send → Contact picker, and pay() at line 59 returns immediately when a lookup is already in progress. That picker is a different entry from ContactPay. This PR does not change the contact-detail handoff. This is not a runtime verification or merge-readiness declaration.
Reviewer model: grok-4.7
Reasoning effort: high
There was a problem hiding this comment.
Verdict: ⛔️ Request changes — the existing keypad-legibility and contact-timing journey assertions still fail at 7ff5320.
Findings: I found no new code defects in this delta. The blank-band swipe fix holds in both directions. The failures below remain the separately tracked, unchanged keypad and contact-detail behavior.
QA for 7ff5320: I built this head and tested iPhone SE (375 pt) and iPhone 17 Pro Max (440 pt) simulators on iOS 26.5. Contact checks used mutually saved, linked disposable identities and 25 fixture USDT. No payment, request or subscription was submitted. Videos play at 2×; timing observations use the original run.
Test 1: J4 — QR and blank-band swipes ✅ Both blank-band directions select the expected tab without copying; a QR tap still copies.
On the tall phone at normal text, the empty band spans y=716–810. A right swipe at y=763 selects Spending; a left swipe wraps to Savings. QR right/left swipes and the 600 ms return from Spending also work. Immediate captures show no copy tooltip after swipes, and clipboard reads retain a sentinel. A direct QR tap copies the plain address and shows the USDT copy/network warning. All nine updated journey actions pass.
j4-tall-final-2x.2x.mp4
QR gestures, tap-to-copy, then both blank-band directions.
Test 2: J2 — Compact and tall Receive layout ✅ QR decoding, both Copy paths, network navigation, scrolling details and footer alignment pass at both text sizes.
At normal and largest text on both phones, the QR fits above Edit/Copy/Share and Network remains separate. Copy from the QR and from scrolled address details writes the plain address, independently checked from the clipboard; both paths show the copy warning. Returning from Network retains Arbitrum One. Accessibility bounds confirm equal Savings/USDT footer bottom edges at each size. Core Image decodes all eight captured Bitcoin/USDT displays to their expected payment URIs. Existing largest-text action-caption clipping remains visible under the shared accessibility follow-up.
| Compact Receive | Tall Receive |
|---|---|
j2-compact-complete-2x.2x.mp4 |
j2-tall-complete-2x.2x.mp4 |
Normal/largest text, QR and details Copy, network round-trip and footer comparisons.
Test 3: J1 — Compact amount editors at largest text ❌ Entry, cents, stationary controls and scrolling work; the shared 000 caption still reads 0….
A fresh USD request and a subscription with USDT selected before opening Amount accept 1.50 without Delete. Continue and reopening retain the cents. In both editors, accessibility bounds place the divider, first keypad row and Continue at y=275, y=292 and y=576 in BTC and USD after scrolling. Every Bitcoin key was tapped; clearing and entering 1 then 000 produces 1,000 sats. The visible 000 caption truncates in both editors.
| Request amount | Subscription amount |
|---|---|
j1-request-final-2x.2x.mp4 |
j1-subscription-2x.2x.mp4 |
Fresh entry, reopening, denomination switching and compact keypad checks.
Test 4: J3 — Funded warm contact Request or Pay ❌ A warm handoff exceeds three seconds; progress, one-flow navigation, closing, retry and Request work.
Four warm Pay attempts each open one SendAmount screen showing 25 fixture USDT available. Loading disables Request. Closing permits retry, and Request opens PaymentRequestAmount.
On one release-bracketed repeat, the touch-release command completes at 0.506 s. The 3.915–4.322 s accessibility observation still has no SendAmount, more than 3.40 s after release completion; SendAmount is first observed by 5.178 s from release-command start. The following repeat is first observed by 2.933 s. The initial two tap-command probes were faster, first observed by 3.505 s and 3.228 s from command start. These are sampled bounds rather than exact presentation times; the three-second assertion remains failed.
| Initial warm attempts | Release-bracketed repeats |
|---|---|
j3-warm-contact-2x.2x.mp4 |
j3-touch-timing-2x.2x.mp4 |
Warm Pay attempts, closing and retry, then Request opening the amount editor.
I retained these passing assertions where this delta leaves their implementation untouched:
- Normal-size amount controls, cents, method exclusivity, and picker locking/failure/retry.
- Opaque wallet transitions and saved USDT activity navigation.
- Paired hardware vendor-label check.
The focused Receive sharing suite and updated Receive UI regression passed locally. Changed-file SwiftFormat 0.64.0, XML and whitespace checks passed. The native run reported AppIntents metadata and QoS warnings. I did not rerun the full native suites locally; current-head unit CI and integration CI passed, while E2E CI remains in progress.
Scope: the complete contiguous 754a04c…7ff5320 delta and outstanding feedback. The changed runtime container was driven; test, journey and changelog changes were also reviewed. No planned case remains unrun. This run adds no 320-point device, fresh Figma comparison, hardware pairing or Android runtime coverage.
Twins, compared in source:
- bitkit-android#1564 fix: improve usdt payment feedback and layout
Atc1f1c0a, the address-page swipe already belongs to a full-height weighted column, matching this fix. - bitkit-android#1565 fix: stabilize usdt wallet and payment layouts
Ataba8c0f, it matches the amount-layout and compact-QR intent. Android was not rebuilt here.
Reviewer model: gpt-6.1-sol
Reasoning effort: high
Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest
|
@ben-kaufman conflicts. |
Preserve stable amount controls with the single denomination toggle. Allow UI tests to use explicitly isolated USDT fixture endpoints.
|
Resolved the master conflicts in signed merge Final local validation: 16 focused passes; 2,875 canonical unit passes plus 39 UI passes and four existing fixture skips; full SwiftFormat clean and translation diagnostics identical to the actual base. Complete runtime/build diagnostics and remaining journey gaps are in the updated QA evidence. All 17 PR commits were verified locally and on GitHub. Fresh hosted CI is pending. The existing requests to Ovi, João and Piotr remain pending, so I preserved them without duplicate pings. No changes request was dismissed and no incomplete journey was marked passed. |
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Re-reviewed the full pull request at 6cef2d2f after the comparison base moved from e4bf44a to 65d0ae38. No earlier coverage was inherited.
No new actionable code findings.
Sending to a saved contact disables every row while payment details resolve and keeps the spinner on the selected row. The existing single-flight guard still clears that state on every exit and still refuses a second payment lookup until the first returns. That matches the contact list in bitkit-android#1564 at c1f1c0a7.
The request and subscription amount screen keeps one denomination toggle, reserves the Bitcoin unit-control slot so BTC/USD changes do not move the divider, and stores a fresh USD amount as an empty string so the first keypad digit is accepted. Continue still replaces only the draft amount and preserves the note, expiry, and accepted endpoints. Android uses separate asset buttons; this change keeps the single toggle, which the updated amount journey describes.
USDT Receive keeps the QR, Edit/Copy/Share, and the flexible space above Network in the tab-swipe column, and leaves address details in a scroll view. The shared QR logo is an overlay at most 20% of the shorter side, capped at 68 points, so it no longer changes the code's layout size. The added receive tests cover square sizing, footer anchoring, logo scannability, and blank-band swipes; this review inspected them and did not run them.
Device testing: not performed. No simulator or unit/UI suite was executed.
Earlier findings reconciled at this revision: 4 verified fixed, 2 tracked separately outside this PR. Tracked separately (outside this PR; unresolved): The 000 keypad label still truncates at the largest text size. Issue #1036 is open and describes this shared 000 label. At 6cef2d2 NumberPad.swift:75 still sets the integer key text to 000, and NumberPadButton at lines 147-151 draws it at a fixed 24pt font with frame(maxWidth: .infinity) and no minimumScaleFactor. That file's blob 3ecc8a591e72b30ae6e7731bff0cbbcd4e53a2d4 is identical at base 65d0ae3, and the PR diff does not include Bitkit/Components/NumberPad.swift. CreatePaymentRequestView.swift:366-367 still selects that keypad with the same type expression the base uses, and AmountInputViewModel.swift:186-189 still returns .integer for modern Bitcoin; that view-model blob c586885eb2e70a8f644a7528d1589f069c4657a3 is identical at both SHAs. This PR does not change the shared label. The device review at 7ff5320 reported the caption as 0… on this same NumberPad blob; this pass did not run a device. This is not a runtime verification or merge-readiness declaration. Tracked separately (outside this PR; unresolved): Contact-detail Pay still exceeds the three-second journey target. Issue #868 is open. The timed path is ContactDetailView.onPayTapped at lines 170-184, which waits on eligibleTarget(publicKey:waitingAtMost: .seconds(2)) and then opens requestOrPay or payContact. ContactDetailView.swift blob 2f53f01ea491a6ca51b07dbe1e06ce426b9ab4ab is identical at base 65d0ae3 and head 6cef2d2, and that file is absent from the PR diff. journeys/payment-requests/contact-request-or-pay.xml:12 still requires SendAmount within 3 seconds after ContactPay; the file's only diff hunk is the line 19 Send → Contact action. SendContactSelectView.swift:46 adds .disabled(selectedContactKey != nil) on the Send → Contact picker, and pay() at line 59 returns immediately when a lookup is already in progress. That picker is a different entry from ContactPay. This PR does not change the contact-detail handoff. The device review at 7ff5320 recorded a warm ContactPay sample past three seconds on this same ContactDetailView blob; this pass did not time it. This is not a runtime verification or merge-readiness declaration.
Reviewer model: grok-4.7
Reasoning effort: high
piotr-iohk
left a comment
There was a problem hiding this comment.
Approved for this PR’s scope following manual retesting on iOS Debug/regtest at 6cef2d2f.
The remaining Receive swipe animation glitches are existing issues, tracked in #1029 and #1038 with fresh recording evidence. Subscription/payment-request prerequisite guidance remains tracked separately in #1075.
Blocktank integration failures are being handled separately. The Trezor E2E approval timeout remains unresolved; this approval does not classify it as unrelated or assert green CI. This is scoped manual QA approval, not a claim that every compact-screen, accessibility, hardware and failure-fixture journey passed.
|
@ben-kaufman Piotr approved this, and the only red is CI on Blocktank tests. #1130 is merged and skips them, so merging master into this branch should turn integration green; e2e already skips them through synonymdev/bitkit-e2e-tests#285. Once it's green I'll merge. |
|
Integrated master Final local canonical validation passed: 2,885 unit executions and 43 UI executions (39 passed, four optional-fixture skips), with the Receive regression and comma-region manual-entry case passing. SwiftFormat 0.64 and translations passed; complete diagnostics, including runtime warnings and current-base comparisons, are retained in the description. All 18 current-base PR commits verify locally and on GitHub. New-head hosted CI is pending; earlier failures and incomplete device/provider journeys remain explicit. |
|
@ben-kaufman Merged, as it was approved and green. |
Refs #974. Matches the contact-picker behavior in synonymdev/bitkit-android#1564.
Description
Out of Scope
Shared keypad accessibility: Keep the 000 keypad label readable at largest text #1036 tracks the existing
000label truncation at the largest text size (device review). The keypad is unchanged from master; its legibility assertion remains unpassed.Contact preparation latency remains tracked in Contact payment latency on staging with the shared Paykit runtime: pin paykit-rs#171 and re-measure #868. The earlier exact-head device review again observed a warm repeat beyond the three-second journey target; that assertion remains unpassed.
Deterministic contact-failure journey setup across the sibling PRs: Align contact-resolution failure setup across QA journeys #1031 (review thread).
This PR does not redesign the BTC/USD selector or master’s receive-network warning.
Payment conversion, subscription method exclusivity and copied address/share payloads remain unchanged; master’s USDT-specific copy confirmation is retained.
Design
Request amount — Handoff v62 and amount layout — Refactor v63. Reuses existing typography, buttons, colors and divider; master’s single denomination toggle is retained.
Preview
Before/after captures were taken at 320/375-point widths with normal and accessibility text, including the scrolled keypad/Continue view. Live Receive captures confirm the network remains visible. Screenshots are available in the author’s local review gallery.
QA Notes
Setup
Use PR commit
6cef2d2f425e86e76497fa5cd8111e1b115facc2, preserving the pinnedPackage.resolved(Core0.8.0-rc9atfab768db, Paykit0.1.0atdd8e75e5, LDK0.7.0-rc.77). Follow the repository’s simulator setup and payment-request fixture recipe resolved at mastere4bf44a2. Configure an E2E/regtest test wallet with Paykit and USDT enabled; use test identities, never a personal seed. J1 needs a saved linked test contact and a current BTC/USD rate; cancel before publishing a request. J2 needs an onboarded wallet and a paired hardware test wallet for its vendor-label step; copy/scan without creating a deposit. J3 needs two linked test installs, payable endpoints and the separate no-endpoint failure fixture described by the journey; its full recipe requires regtest funds and has not been rerun in this Receive follow-up. Preserve existing wallets and do not reset them. Review J1/J2 on compact and standard screens at normal and largest text. The deterministic J3 failure fixture remains tracked in #1031; an unavailable fixture is a gap, not a pass.The earlier October 24 local validation uses Xcode 27.0 (
27A266a), iOS 26.1,xcodebuildmcp2.7.0, one worker and the dedicated unfunded unit-test simulator; the hosted runs at754a04cbuse Xcode 16.4 and the E2E run uses Xcode 26.2, with iOS 26.2 destinations. It does not substitute for J1–J3. IDs are newly added to the existing three cases for stable references, without changing their expectations or claiming new execution.J4 uses the same pinned app on a tall iPhone at default text size with an onboarded disposable wallet and USDT enabled; no funded transfer or hardware pairing is needed. The author used Xcode 26.1 (
17B55), iOS 26.1 (23B86), iPhone 17 Pro Max (440 × 956 pt), one worker, private DerivedData/package caches and the retained UI-test wallet. Build/test with the repository'sxcodebuildmcprecipe and the six exclusions fromunit-tests.yml; do not add exclusions to hide failures. Before the full selection, check ownership of local fork listeners on 18545/18546: the unchanged funded case automatically uses reachable listeners and funds its disposable wallet. This run missed that preflight and used an existing shared fork; see the failed-check evidence below. Run only with an agreed isolated fixture and archive-capable upstream when exercising that payment case.Journeys
J1: new
paykit-amount-layout.xml— request/subscription alignment, fresh USDT amount entry, preserved cents, legible keypad keys, scrolling to Continue and enlarged-text reachability.J2: new
usdt-wallet-receive-layout.xml— cross-platform wallet transition, Receive footer and network/vendor-label parity, compact Copy hit target and displayed QR scan.J3: updated
contact-request-or-pay.xml— selected-row progress, all-row disabling and duplicate-payment protection.J4: updated
usdt-receive-swipe-tabs.xml— QR swipes and taps retain their behavior; both directions through the blank band above Network switch tabs on a tall iPhone. The two blank-band steps are new; earlier seven-step results do not cover them.Manual Tests
N/A
Automated Checks
updated
UsdtWalletUITests.swift— swipe both directions through the measured empty band, require departure from USDT, and verify the same amountless QR after returning.ran the focused amount, conversion, number-pad, display and component-sizing suites — all 44 tests passed.
ran a temporary actual-view iOS fixture — entered
150.50directly from zero and edited an existing150.50to150.55; Continue preserved the exact amount, note and expiry. The fixture was removed after validation.ran temporary UIHostingController renders — actual amount view at compact/standard widths and normal/accessibility text; verified stable BTC/USD control, divider and keypad positions, wrapping and scrolling to Continue. Captures use settled layer rendering.
Earlier validation passed three receive-sharing tests, including decoding the actual USDT and Bitcoin QR components with logos at compact and normal heights. Isolated Receive renders at 320/375 pt with normal/largest text keep actions separate from Network; all four rendered USDT codes decode correctly. Simulator Receive tab checks passed. A paired hardware-wallet journey and an end-to-end contact payment were not rerun in this follow-up; the duplicate guard and awaited navigation were source-reviewed. SwiftFormat reports no changed-file findings. At that earlier revision, translation validation reported no errors and 14 warnings verified unchanged from its base. Compiler warnings were also checked against unchanged base lines.
testQrLogoPreservesSquareLayoutInTallContainerinUsdtReceiveSharingTests.swift— QR with Bitcoin/USDT logos and without a logo stays square when its parent offers extra height, at compact and normal widths. Existing rendered QR decoding coverage remains.The focused layout validation compiled the app and all test targets with Swift warnings treated as errors. All five Receive layout/QR-sharing tests passed, including square QR layout, Bitcoin/USDT QR decoding, share contents and full-height USDT content at compact/normal widths with normal/largest text. The new full-height regression failed against the previous code and passes with the flexible spacer that anchors Network and Show Details.
The earlier device review confirms stable Receive footer positions, displayed QR decoding, plain-address copying and network switching on iPhone 17 and SE at normal/largest text sizes. The shared keypad legibility issue (#1036) and variable contact latency (#868) remain explicit exceptions; the whole journeys above are therefore not marked passed.
Synced with master
039c7b51, including the merged #1040 formatter correction. Full-repository SwiftFormat 0.64.0 passes (0/831 files need changes); translation validation has zero errors and 14 warnings checked against the base. Independent integration review confirms the Receive fixes and master's swipe behavior are preserved.CI run 39448955514 completed 2,712 tests with zero failures, plus 43 integration tests with four skips and zero failures, before cancellation during post-test cache cleanup. Its tested merge tree and this final integration differ only in Transifex docs/config/scripts and two Settings files whose changes exactly match SwiftFormat 0.64.0 output. No new local native run was performed for that formatter-equivalent integration. The CI log contains dependency linker warnings; this is not a claim of warning-free CI. Prior-head SwiftFormat CI and translation validation are green. At the prior head
7d68cf13, unit tests, the E2E build and the configured E2E CI job have passed. This does not close the outstanding manual journey assertions listed above. The separate integration run 39465856741 was cancelled at its 60-minute limit after real regtest failures: Blocktank mining/transport errors, balance waits and channel-purchase readiness assertions. It is not a passing run or a post-cache cancellation. The then-current master integration run 39459205073 independently failed the same channel-purchase flow and encountered Blocktank transport/HTTP 500 errors before timing out. That investigation found those integration tests, workflow and payment services unchanged by the PR against its then-current base. No integration assertions were weakened or unchanged jobs repeatedly rerun; this validation remains blocked.Targeted follow-up render on
7d68cf13confirms one trailing control row at 402 pt/default text and wrapping at 320 pt/accessibility5, with stable BTC/USD divider positions. A temporary helper-layout probe passed (158 × 28 pt); the fixture was removed and production code remains unchanged. This is targeted layout evidence, not a complete accessibility or payment-journey pass.Current master integration —
6cef2d2fIntegrated actual master
65d0ae38with a normal signed merge, preserving its single denomination toggle within this PR’s stable, scrollable amount layout. The Bitcoin unit control is visible only in BTC. Frozen reviewed/tested tree:1dd27f1b6e1d651b5d5801d75b36e29072b0aa27; parents7ff5320b/65d0ae38. Payment/quote/authorization code and dependency pins are unchanged. The existing UI tests now accept optionalBITKIT_TEST_USDT_RPC_URL,BITKIT_TEST_USDT_BUNDLER_URLandBITKIT_TEST_USDT_OFFLINE_RPC_URL; defaults, assertions and the prerequisite skip are unchanged. No tests were added or removed.65d0ae38. XML and whitespace checks pass. Independent fresh-context review and its bounded wording recheck found no outstanding introduced defect.testRunnerEnv. The existing funded-fork case skipped its prerequisite; the three Trezor cases also skipped their opt-in prerequisite. No shared 18545/18546 fixture access or funding occurred in this integration run. These skips do not validate those journeys or erase the earlier shared-fork incident.65d0ae38verify locally and match GitHub’s exact commit set, eachverified=true/reason=valid. GitHub reports the new head mergeable. Ovi and João remain requested. Piotr’s exact-head source review reported no new actionable code finding and retained the outside-PR Keep the 000 keypad label readable at largest text #1036/Contact payment latency on staging with the shared Paykit runtime: pin paykit-rs#171 and re-measure #868 gaps; COMMENTED is not approval.20982c2c, parents65d0ae38/6cef2d2f, with the same tree1dd27f1bas the local candidate: 2,875 unit executions; 43 UI executions (39 passed / four fixture skips). Receive passed in 101.253s and comma-region entry in 52.710s. The complete report has 286 duplicate-symbol warnings and 571 build-log warning nodes matching the prior7ffhosted message multisets; all 16 workflow/project/pin paths match actual65d0. Runtime-warning and test-failure arrays are empty. The raw log retains a staging Blocktankorder123HTTP 502 error during a passing test; this is not a blanket warning-free claim.65d0. One bounded manual retry (attempt 2) also failed: 30 of 34 unique cases passed; the four order/CJIT cases above failed twice with “Not found” (38 executions, eight failures). No further manual retry. Live iOS #1130, currently open/unmerged, documents Blocktank staging’s unavailable order/CJIT creation and skips exactly those four cases; read-only info remains tested. This dependency has not changed this head and no exclusion/assertion was changed here.14069ced: 14 unique cases, 13 eventually passed and hardware-wallet-to-Spending failed. The two boost cases failed in attempt 1 and passed in attempt 2; hardware case 3 failed in all three (18 executions: 13 passed, five failed). Full logs, verified failure artifact, screenshots and all five failure videos were inspected. The first hardware attempt included Blocktank info HTTP 502; subsequent attempts stayed on Sign With Your Device and timed out awaiting Trezor approval. Fifteen implicated app/test/workflow paths match actual65d0; there is no actual-base runtime reproduction or proven approval-timeout cause. E2E #285, also open/unmerged, proposes skipping this channel-purchase case; that would not prove the approval timeout repaired. Its removed Lightning migration/payment coverage remains a gap. No manual E2E retry. Formatter/translations passed. The required hosted gate remains failed; no new human re-review ping is sent for this unchanged failure. Earlier results below retain their original head/tree attribution.Fresh unfunded QA at
6cef2d2fused the validated binary on iPhone 17 Pro Max, 440×956 pt, iOS 26.1/default text. Explicit human consent for the disposable peer’s Terms/Privacy screen was received and applied; both profiles were created and mutually saved as contacts. Subscription USDT-first entry of 1.50, dot decimals, Continue, reopening with cents intact and exclusive USDT/Savings selection passed. A freshly entered USD 1.50 survived two BTC/USD round trips; the BTC-only unit control and single denomination toggle behaved correctly, with stationary divider/keypad/Continue. The separate Payment Requests footer remained unavailable despite both sharing preferences being enabled; mutual contact saves do not prove private-link/request eligibility, and that prerequisite remains unresolved. Full J1 was not completed. No payment, request or subscription was submitted; no funds were added. J1–J4 remain unchecked; compact/largest-text coverage, #1036, contact timing #868, deterministic fixture #1031 and complete hardware/provider/settlement gaps remain explicit. Both disposable installs and all earlier resources were preserved.Earlier evidence — Receive swipe follow-up at
7ff5320bFixed the blank-band swipe finding with a container hit shape around the existing Receive content. The shared gesture helper and payment/quote logic are unchanged. Android #1564 already attaches this gesture to a full-height weighted column; that is source parity evidence only.
usdt-monitor-20261026-0803/manual-journey-report.md.754a04cb; the same case passed after this fix (70.191 s), including both directions and the existing amount/details/invalid-recipient checks. It also passed within the canonical run (65.011 s).-32000/missing trie node on nonce and account reads). There is no evidence of a submitted transfer or settlement. The listeners were not preflighted; no retry, fixture repair/reset or service shutdown followed. Quote source, wallet manager, dependency pins and the whole failed test method match actual masterb26142bd; no actual-base runtime reproduction or successful funded journey is claimed.a2a7b6e629e75a6ee8907a283b0d7f0b943bf343matches the tested patch. All 16 PR commits against actual masterb26142bdverify locally and match the exact GitHub set, eachverified=true/reason=valid; publication was a normal additive push.79bfe64df65cf57e9b783d7bdb29b9188a9a6e44, parents current masterb26142bd/ published head7ff5320b, treebc0ee1f285bce7f331a56943c0bd441e09c98b52. These hosted results cover that combined tree; local results above cover the PR tree.The October 26 device review at
7ff5320bseparately reports all nine J4 actions passing on a 440-point iPhone: both blank-band directions select the expected tab, swipes preserve a clipboard sentinel, and QR tap still copies with the warning. It also reports fresh compact/tall Receive QR decoding, both Copy paths, Network round-trip and footer-alignment checks at normal/largest text on iOS 26.5. Full J2 is not claimed because hardware pairing was not rerun. Its fresh J1/J3 checks still fail the shared000label assertion (#1036) and one release-bracketed warm contact handoff (#868): SendAmount was absent more than 3.40s after release completion, then first observed by 5.178s from command start. These are sampled bounds, not exact presentation times. Contact checks used linked disposable identities and 25 fixture USDT; the reviewer reports no submitted payment, request or subscription. This external evidence adds no 320-point, fresh Figma, hardware-pairing or Android runtime coverage. J1–J4 remain unchecked and the changes request remains in force. The separate source review found no new actionable code defect and did not drive J4; it is COMMENTED, not approval.The October 26 reviewer comment at
754a04cbseparately reports the original seven swipe actions passing and partial J2 QR/layout/network/copy evidence. Hardware step 5 remained pending. Those earlier results are not rolled forward to this commit or expanded into a full J2 pass.Gaps
J1–J3 remain incomplete, including largest-text controls (#1036), contact timing (#868), deterministic failure setup (#1031) and the full wallet/hardware journey. No new compact-width, largest-text, Android runtime, hardware or funded/live-provider pass is claimed for this swipe follow-up. The canonical fork failure and prior startup failure remain explicit. Historical results below retain their original head/base/environment attribution.
Earlier master integration —
ac54f6e8Merged actual master
a61e0c44with a normal signed merge. The sole manual conflict retains the compact QR/scrolling-details/full-height footer layout and passes master’s USDT-specific copy confirmation through bothQrAreaandCopyAddressCard; tab wrap behavior remains intact. Other PR changes are preserved, and everything outside the PR diff matches that master. Independent frozen-tree review found no introduced correctness or repository-pattern issue.a61e0c44verify locally and match the exact GitHub PR commit set, eachverified=true/reason=valid. Current-head hosted unit, integration, SwiftFormat, translation and selected E2E workflows have passed. The E2E result does not establish that the separately tracked intermittent LNURL issue LNURL flow can crash or show a blank confirmation after shared payment data is cleared #1087 is fixed, or close unpassed manual assertions.Evidence is retained in the author checkpoint
usdt-monitor-20261022-2149(test arguments, full logs, xcresult reports, base-warning comparison and independent review). J1–J3 were not rerun as complete device journeys by the author in this integration. Subsequent external device evidence is attributed below. Existing keypad/latency/failure-fixture gaps remain open; prior paired-hardware and other device evidence stays tied to its original revisions. No payment, wallet reset or deployment was performed in the author integration.Earlier external device review —
ac54f6e8Review 5622745820 built unchanged current-head app sources and tested iPhone SE (375 pt) and iPhone 17 (402 pt) simulators on iOS 26.5. Contact checks used mutually saved, linked disposable identities and a payer with 25 fixture USDT. No payment, request or subscription was submitted. The reviewer found no new delta defect, but requested changes because the existing keypad-legibility and contact-timing assertions still fail.
000caption still renders0…, so J1 remains unchecked (Keep the 000 keypad label readable at largest text #1036).The external review retained earlier evidence for standard amount-control wrapping, prepared endpointless-contact failure/retry and all-row locking, opaque wallet transitions/saved activity and paired hardware labels. Those cases were not all rerun at this head. No fresh 320-point run, Figma comparison, hardware pairing or Android build is implied. Android #1565 journey parity was checked in source only. The review did not rerun native unit/integration suites locally and referenced passing current-head CI.
Read-only source comparison confirms
NumberPad.swift,QrArea.swiftandContactDetailView.swiftare byte-identical at this head, publication basea61e0c44and current master07610b3f. The remaining contact-picker diff disables all rows during selection; this does not establish the cause of the measured contact delay. The timing evidence was relayed to the separate payment-latency owner. No shared-control or latency patch was added to this layout integration, and no failed assertion was waived.Earlier master integration —
754a04cbIntegrated master
e4bf44a2with a normal signed merge. The request-amount conflict keeps this PR's scrolling, stable controls and exact draft amounts while retaining master's dot-decimal display, default keypad and empty-zero draft guard. The contact picker keeps master's presentation ownership checks and this PR's disabled rows during selection. The independently reviewed tree is8340d68ca39223f8dde18269cc2516029ade2b6d; the resolution adds or removes no tests.UsdtWalletUITests.testUnknownBalanceAndManualEntryUseConsistentAmountsInCommaRegionfailed at line 40 waiting forRecipientManual: Send remained on Connecting to network while the node was starting. One correctly isolated unchanged-case repeat failed identically. A fresh, verified export of actual current masterf7074d59reproduced the same test and assertion on the same simulator, with the same overlay and node-starting log. This is an inherited failure in this environment, not a green canonical suite; the underlying startup cause remains unknown. No readiness guard or assertion was weakened. The four skips remain the three local Trezor fixture cases and one funded local-Arbitrum case; the six CI-excluded integration classes stayed excluded.e4bf44a2; current masterf7074d59has the same 15 warning diagnostics and zero errors (only stdout/stderr message ordering differs).notRequested; it is not evidence of warning-free compilation.f7074d59verify locally and match the exact GitHub PR commit set; every commit reportsverified=trueandreason=valid. Normal push only; the published tree is the independently reviewed and tested tree8340d68c.notRequested, not proof of warning-free compilation.f7074d59unit run 40073078895; project, workflows and dependency pins are byte-identical. Both unit and integration test reports contain no runtime warnings. AppIntents notices remain, and package version probes emitted a broken-pipe exception before successful builds; none is being represented as a warning-free run. No manual CI retry was requested.While validation ran, master advanced to
f7074d59by merging the external camera-fixture correction #1117. Its two test-file changes are disjoint from this PR. Read-only combination072ab30bb51462266046003eeccb48ce10847ad1retains the exact PR and new master blobs. Hosted CI tested that exact combined tree as mergea70252382aac769b868bd43f3227eb758dae5c47(parentsf7074d59/754a04cb); no additional local runtime pass on it is claimed.The first focused invocation stopped before compilation because four retained package checkouts had lost manifests/Git objects. A separate cache was restored at every exact pinned revision, including its three submodules; the original cache and all pins were preserved. An initial diagnostic accidentally retained the 13 focused selections and is not counted as evidence for the failed UI case. The first canonical invocation could not access CoreSimulator from the sandbox and ran no tests; the authorized invocation with simulator access produced the result above. Complete CLI output, raw logs, xcresults, diagnostic exports, built products, base comparisons and signature evidence are retained in
usdt-1003-master-20261023/concurrent-validation-20261024. Local validation used Xcode 27.0, iOS 26.1 and one worker while other isolated builds could run; no timing comparison is claimed.These checks do not complete J1–J3. Largest-text controls (#1036), contact timing (#868), the separate failure fixture (#1031), and the full wallet/hardware journeys remain open. Earlier device and funded evidence remains tied to its original head and environment. This integration did not submit payments, reset wallets or change dependency pins.
Earlier external device review —
754a04cbReview 5637648380 reports no introduced code defect after building this head and testing iPhone SE (375 pt) and iPhone 17 (402 pt) on iOS 26.5. It still requests changes for the existing keypad-legibility and contact-timing assertions. These are attributed reviewer results, using linked disposable identities and 25 fixture USDT; no payment, request or subscription was submitted.
1then000producing 1,000 sats, but the largest-text000caption still reads0…(Keep the 000 keypad label readable at largest text #1036).The review retained earlier Receive QR/copy/network/footer, opaque-wallet/activity and paired-hardware-label evidence at their linked original heads; it did not rerun those full journeys here. No fresh 320-point, Figma, hardware-pairing or Android build is implied. Android #1565 journey parity was reported from source comparison only. The reviewer did not rerun native unit/integration suites locally; the hosted completions above supersede its then-pending CI snapshot. J1–J3 remain unchecked, and no finding was dismissed or resolved on the strength of these partial results.
October 26 master integration —
6929bf85Integrated actual master
2ce21025after maintainer #1130 retired the unavailable Blocktank creation cases. Signed merge parents are6cef2d2f/2ce21025; exact tested tree7bcb32b3. Independent complete-tree review verified the disjoint parent changes with no additional resolution edits, preserving this PR and merged #1006 work. The four Blocktank skips are exactly the maintainer changes on master; no local assertions or exclusions were weakened.Final local canonical validation on Xcode 26.1 / iPhone 17 Pro Max / iOS 26.1 passed 2,885 unit executions, plus 43 UI executions: 39 passed / 4 opt-in fixture skips / 0 failures. Receive regression passed in 58.919s and comma-region manual entry in 45.686s. Private simulator/DerivedData/packages and one worker were used; optional endpoints were reserved without listeners, and the shared fork was never contacted. SwiftFormat 0.64 reports 0/844 files needing changes (78 exclusions); translations have zero errors and the same 15-warning multiset as actual master.
Complete diagnostics retain two AppIntents metadata warnings and 56 structured QoS warnings. The 88 raw SDK/LDK backtraces are a different count; relevant wrappers, test and diagnostic-source paths match actual master. UIKit/preference, simulator and negative-case diagnostics remain documented. This is not a warning-free claim or an actual-base runtime reproduction. All 18 commits in the current-base PR range verify locally and on GitHub. Fresh hosted CI is pending. Earlier failures and J1–J4 acceptance gaps remain attributed below; no provider, funded, hardware or full journey completion is inferred.
October 26 review and CI reconciliation
Piotr approved the scope at
6cef2d2fafter manual Debug/regtest retesting. This is external reviewer evidence, not a new author-run validation. His remaining Receive-animation findings are tracked in #1029/#1038; prerequisite guidance remains #1075. The approval does not clear CI or claim the full compact/accessibility/hardware/failure matrix.Integration workflow
40341466044attempt 2 still failed on the four documented Blocktank creation cases. E2E workflow40341466580was independently rerun byovitrifat 16:45:38 UTC: attempt 2 subsequently completed successfully at 17:44 UTC on the older6cef2d2fhead; full logs show 13 cases passed and hardware_wallet_3 skipped in the first test-attempt step; steps 2/3 skipped. E2E framework ec01c02f now omits that case after #285. The built app was reused from attempt 1 (merge20982c2c, parents65d0/6cef), so this is not fresh compilation or recovery of the earlier Trezor approval timeout. No new successful-run runtime artifact was uploaded. No retry was sent by this chat for that E2E run. Earlier attempt-1 failures and coverage limits remain; a later pass does not explain the earlier failure. Ovi’s independently started integration attempt 3 also failed on that older head; it does not include the new master integration.Models used
gpt-6-astra(reasoning:xhigh)gpt-6-astra(reasoning:xhigh)gpt-6-astra(reasoning:xhigh)Model metadata for this integration and its independent frozen-tree gate was checked against the actual parent/reviewer session and turn records. Earlier review evidence remains attributed to its linked reviews.