Skip to content

fix: stabilize contact selection and amount layouts - #1003

Merged
ovitrif merged 18 commits into
masterfrom
fix/usdt-payment-layout-stability
Oct 26, 2026
Merged

ovitrif merged 18 commits into
masterfrom
fix/usdt-payment-layout-stability

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Refs #974. Matches the contact-picker behavior in synonymdev/bitkit-android#1564.

Description

  • Disable every contact row while payment details resolve, keeping loading on the selected row and retaining the duplicate-payment guard through navigation.
  • Keep request and subscription amounts leading-aligned, group the existing controls directly below without shifting when BTC/USD changes, and separate the keypad with the existing divider.
  • Wrap controls and allow scrolling at enlarged text sizes so the keypad and Continue remain reachable.
  • Allow fresh USD request and USDT subscription amounts to accept digits immediately, preserving cents when reopening existing amounts.
  • Fit the USDT QR above Edit/Copy/Share on compact screens, keeping Network separate. Scale the shared QR logo down when needed so the displayed code stays scannable, while keeping it inside the QR image’s layout bounds; address details still scroll. Keep the USDT Receive content full-height so switching tabs does not shift the header or footer, and make the empty space above Network respond to tab swipes.

Out of Scope

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 pinned Package.resolved (Core 0.8.0-rc9 at fab768db, Paykit 0.1.0 at dd8e75e5, LDK 0.7.0-rc.77). Follow the repository’s simulator setup and payment-request fixture recipe resolved at master e4bf44a2. 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, xcodebuildmcp 2.7.0, one worker and the dedicated unfunded unit-test simulator; the hosted runs at 754a04cb use 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's xcodebuildmcp recipe and the six exclusions from unit-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.50 directly from zero and edited an existing 150.50 to 150.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.

  • added testQrLogoPreservesSquareLayoutInTallContainer in UsdtReceiveSharingTests.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 7d68cf13 confirms 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 — 6cef2d2f

Integrated actual master 65d0ae38 with 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; parents 7ff5320b / 65d0ae38. Payment/quote/authorization code and dependency pins are unchanged. The existing UI tests now accept optional BITKIT_TEST_USDT_RPC_URL, BITKIT_TEST_USDT_BUNDLER_URL and BITKIT_TEST_USDT_OFFLINE_RPC_URL; defaults, assertions and the prerequisite skip are unchanged. No tests were added or removed.

  • Local focused: 15 unit tests and one Receive UI test passed, zero failures/skips. Local canonical workflow selection: 2,875 unit executions passed; 43 UI executions: 39 passed, four fixture skips, zero failures. Receive passed in 56.674 s; comma-region manual entry in 44.049 s. Thirty launch configurations are grouped in the summary; counts above are executions. No retry or new exclusion.
  • Full SwiftFormat 0.64: 0/844 files require formatting (77 skipped). Complete translations: zero errors/15 warnings, exactly equal to fresh actual-base 65d0ae38. XML and whitespace checks pass. Independent fresh-context review and its bounded wording recheck found no outstanding introduced defect.
  • Used Xcode 26.1/17B55, iOS 26.1/23B86, one Xcode job and serial tests on a dedicated iPhone 17 Pro Max, with private DerivedData/packages. After preflighting listeners, the test runner used reserved, non-listening private endpoints 28545/28546/28549 through 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.
  • Complete raw logs, xcresults, all execution counts, action/process diagnostics and 47 relevant case summaries were inspected. 18 structured QoS warnings remain (four unit, 14 UI); raw process logs separately contain 21 backtraces through Paykit/Pubky initialization and LDK status/synchronization. Relevant wrappers/pins match actual base; no runtime base reproduction or equality to earlier warning counts is claimed. Both build invocations retain two AppIntents metadata notices. Xcode physical-device discovery notices, ten UIKit appearance notices, simulator haptic errors and negative-case diagnostics remain documented. Forty diagnostic-source files and 11 relevant tests match actual base. No blanket warning-free claim.
  • All 17 PR commits against actual 65d0ae38 verify locally and match GitHub’s exact commit set, each verified=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.
  • Hosted unit/UI validation passed on merge 20982c2c, parents 65d0ae38 / 6cef2d2f, with the same tree 1dd27f1b as 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 prior 7ff hosted message multisets; all 16 workflow/project/pin paths match actual 65d0. Runtime-warning and test-failure arrays are empty. The raw log retains a staging Blocktank order123 HTTP 502 error during a passing test; this is not a blanket warning-free claim.
  • Hosted integration attempt 1 failed: 26 of 34 unique cases passed; eight failed again in the existing built-in retry (42 total executions, 16 failed). Seven cases received Blocktank staging HTTP 502 responses; channel purchase received “Not found” while creating an order. Full logs, verified result ZIP, all failed-case details and source frames were inspected; the 16 implicated test/service/workflow/pin paths are byte-identical to actual 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.
  • Hosted E2E failed after three built-in attempts at suite revision 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 actual 65d0; 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 6cef2d2f used 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 7ff5320b

Fixed 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.

  • J4: all nine author-driven actions passed on the tall-phone/default-text setup above. QR and blank-band swipes selected the expected Spending/Savings tabs without a copy tooltip; a QR tap showed the address-copy/network warning. No funds were needed for J4. Full logs and 11 screenshots are retained in usdt-monitor-20261026-0803/manual-journey-report.md.
  • Regression red/green: the new first blank-band assertion failed against unchanged 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).
  • Canonical result is partially failed: 2,874 unit executions passed; 43 UI executions comprise 39 passed, one failed and three Trezor fixture skips. The unchanged local-Arbitrum payment case discovered an existing shared fork, credited its disposable wallet with 1,000 fake USDT, then failed during quote preparation before confirmation. Matching Anvil logs show the upstream RPC cannot serve historical state at fork block 517943110 (-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 master b26142bd; no actual-base runtime reproduction or successful funded journey is claimed.
  • The comma-region UI case passed in 43.624 s on this run; this does not explain or erase its historical local startup failure. No assertion or exclusion was weakened.
  • Full SwiftFormat 0.64.0: 0/843 files need changes (75 skipped). Translations: zero errors, 15 existing warnings, with inputs/pins checked against actual base. Updated XML parses; whitespace checks pass.
  • Complete raw and structured diagnostics were inspected. Four AppIntents metadata warnings and 18 structured QoS warnings remain. Raw stacks point to unchanged Paykit/Pubky initialization and LDK status/synchronization paths; counts are not claimed to match the earlier run. UIKit transition and simulator haptic notices, plus negative-test log errors, remain documented. This is not a warning-free claim.
  • Independent frozen-diff review found no verified material issue. The published tree a2a7b6e629e75a6ee8907a283b0d7f0b943bf343 matches the tested patch. All 16 PR commits against actual master b26142bd verify locally and match the exact GitHub set, each verified=true / reason=valid; publication was a normal additive push.
  • Hosted unit, integration and E2E validation completed successfully on workflow attempt 1. Each checked out merge 79bfe64df65cf57e9b783d7bdb29b9188a9a6e44, parents current master b26142bd / published head 7ff5320b, tree bc0ee1f285bce7f331a56943c0bd441e09c98b52. These hosted results cover that combined tree; local results above cover the PR tree.
  • Full downloaded reports show 2,875 unit executions passed and 43 UI executions: 39 passed / 4 fixture skips / 0 failures. The updated Receive case passed in 101.261s; comma-region manual entry passed in 76.624s. The 30 launch configurations are grouped under one summary case. The skipped cases are three opt-in Trezor cases and the local Arbitrum-fork payment case; hosted success does not validate the failed local funded journey or explain the historical local startup failure. Integration passed 34/34 cases and executions, with no retry execution. E2E passed all 14 selected cases in its first test-attempt step; steps 2/3 were skipped. Its log-stream wrapper restarted and an address-type toast helper retried; neither was a second suite attempt. The passing E2E run uploaded the app only, with no separate runtime-report artifact.
  • Full build/action diagnostics were inspected. The 286 structured duplicate-symbol warnings and all 571 build-log warning entries exactly match current-master unit run 40294937751; all 13 project/workflow/pin paths compared are byte-identical. Hosted runtime-warning and test-failure arrays are empty. Tool/dependency notices, an AppIntents metadata notice, E2E stale-element messages and the integration package-version probe's broken-pipe exception remain recorded; no warning-free claim. Unit/integration compilation used Xcode 16.4 paths with an iPhone 16 / iOS 26.2 destination; E2E used Xcode 26.2 / iOS 26.2. These results do not erase the local diagnostics or shared-fork incident above.

The October 26 device review at 7ff5320b separately 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 shared 000 label 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 754a04cb separately 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 — ac54f6e8

Merged actual master a61e0c44 with 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 both QrArea and CopyAddressCard; 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.

  • Focused hosted tests: 33 passed, including all five Receive layout, rendered QR decoding and sharing tests, plus exact-amount/conversion and contact lifecycle tests.
  • CI-selected local suite: 2,833 unit executions and 39 UI executions passed, zero failures. Three Trezor cases and one funded local-Arbitrum case were explicitly skipped. The six CI-excluded integration classes remain excluded. The result bundle groups the 30 launch configurations under one test; execution counts above come from the full XCTest log and device report.
  • Full SwiftFormat 0.64.0: 0/841 files require formatting. Translation validation: zero errors, with all 15 warnings exactly reproduced by the actual-base export. Complete build/test diagnostics and both xcresult reports were inspected. The build includes AppIntents extraction notices; runtime includes thread-priority notices in passing tests and simulator haptic-resource messages. Relevant test/production sources and project configuration were compared with master; this is not a warning-free-run claim or a speculative fix for those warnings.
  • All 14 commits against actual master a61e0c44 verify locally and match the exact GitHub PR commit set, each verified=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 — ac54f6e8

Review 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.

  • J1 partial pass: At largest text, fresh USD request and USDT-first subscription entry accepted 1.50 without Delete and retained cents after Continue/reopening. Scrolling, stationary divider/keypad/Continue, all Bitcoin keypad taps and exclusive Bitcoin/USDT methods worked. The shared 000 caption still renders 0…, so J1 remains unchecked (Keep the 000 keypad label readable at largest text #1036).
  • J2 Receive checks passed: On both screen sizes at normal/largest text, displayed Bitcoin/USDT QR captures decoded, both USDT Copy paths showed the network warning and copied the plain address, details scrolled, and the network round-trip retained Arbitrum One. Savings/USDT footer positions stayed equal. End-wrap and adjacent tab swipes left a sentinel clipboard unchanged without a copied tooltip. Existing largest-text Edit/Copy/Share caption clipping remains tracked under Keep the 000 keypad label readable at largest text #1036. This pass did not repeat the complete wallet/hardware journey, so J2 remains unchecked.
  • J3 partial pass: Both Pay attempts showed progress, disabled Request during lookup and opened one SendAmount with the fixture balance; closing allowed retry, and Request opened its amount editor. The warm repeat still exceeded the three-second target: tap completion at 0.792 s, an observation spanning 4.027–4.420 s still lacked SendAmount, and SendAmount was first observed by 5.491 s from command start. These are sampled bounds, not exact presentation times; J3 remains unchecked (Contact payment latency on staging with the shared Paykit runtime: pin paykit-rs#171 and re-measure #868).

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.swift and ContactDetailView.swift are byte-identical at this head, publication base a61e0c44 and current master 07610b3f. 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 — 754a04cb

Integrated master e4bf44a2 with 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 is 8340d68ca39223f8dde18269cc2516029ade2b6d; the resolution adds or removes no tests.

  • Focused tests: 13 passed, zero failures or skips, covering all five Receive layout/QR-sharing tests plus exact amount conversion, parsing and display.
  • CI-selected canonical suite: 2,874 unit executions passed; 43 UI executions: 38 passed, four fixture skips and one failure. UsdtWalletUITests.testUnknownBalanceAndManualEntryUseConsistentAmountsInCommaRegion failed at line 40 waiting for RecipientManual: 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 master f7074d59 reproduced 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.
  • Full SwiftFormat 0.64.0: 0/843 files require formatting (75 skipped). Translation validation has zero errors and 15 warnings, with the complete output identical to a fresh export of integration base e4bf44a2; current master f7074d59 has the same 15 warning diagnostics and zero errors (only stdout/stderr message ordering differs).
  • Complete raw build/test logs, xcresults, failure videos/hierarchies and runtime diagnostics were inspected. Canonical execution reported 10 QoS runtime warnings; the five unit-test backtraces run through pinned SDK/wrapper paths unchanged from the base, while the five UI records retain address-only stacks. Negative-case log errors match passing retry, invalid-image and synthetic-order tests and unchanged base sources. AppIntents metadata notices occur in both candidate and current-master builds. The test-without-building report says notRequested; it is not evidence of warning-free compilation.
  • All 15 commits against current master f7074d59 verify locally and match the exact GitHub PR commit set; every commit reports verified=true and reason=valid. Normal push only; the published tree is the independently reviewed and tested tree 8340d68c.
  • Current-head unit, integration, E2E, SwiftFormat and translation workflows completed successfully on workflow attempt 1. Optional migration jobs were skipped and the review check is neutral; this does not clear outstanding review assertions.
  • Hosted unit results: 2,874 unit executions passed; 43 UI executions: 39 passed and four fixture skips, zero failures. The comma-region manual-entry UI case passed in 55.042 s on the hosted iPhone 16 / iOS 26.2 destination. It also passed in the hosted current-master run. These results do not erase the reproduced local iOS 26.1 startup failure above or establish its cause or permanent recovery. The xcresult groups 30 launch configurations under one case; execution counts come from its device report and full XCTest log.
  • Hosted integration: 34 cases / 34 executions passed, zero failures or skips, with no built-in case retry. Complete logs and xcresults were inspected. The test-without-building report is notRequested, not proof of warning-free compilation.
  • Hosted E2E: all 14 selected cases passed in the first test-attempt step; attempts 2 and 3 were skipped. This includes all four hardware cases, LNURL msat pay/withdraw and the selected Lightning, send, boost, address-type and channel-transfer cases. Startup log-stream wrappers restarted and an address-type toast helper retried; those are distinct from suite/case retries. The successful run uploaded only the built app, so separate runtime/test-report artifacts are unavailable. This is hosted evidence, not author device or complete J1–J3 validation.
  • Complete hosted build diagnostics report 286 duplicate-native-symbol warnings. Their full structured multiset and all 571 repeated warning nodes in the build log exactly match actual master f7074d59 unit 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 f7074d59 by merging the external camera-fixture correction #1117. Its two test-file changes are disjoint from this PR. Read-only combination 072ab30bb51462266046003eeccb48ce10847ad1 retains the exact PR and new master blobs. Hosted CI tested that exact combined tree as merge a70252382aac769b868bd43f3227eb758dae5c47 (parents f7074d59 / 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 — 754a04cb

Review 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.

  • J1 remains partial: Fresh USD request and USDT-first subscription entry accepted 1.50 without Delete and preserved cents through Continue/reopening. BTC/USD controls, divider, keypad and Continue stayed stationary; standard controls fit one row and the unit wrapped at largest text. Comma-region input retained dot decimals and method exclusivity. Every Bitcoin keypad key worked, including 1 then 000 producing 1,000 sats, but the largest-text 000 caption still reads 0… (Keep the 000 keypad label readable at largest text #1036).
  • J3 remains partial: Pay displayed progress, disabled Request and opened one amount screen; closing allowed retry and Request opened its amount editor. One warm physical tap completed at 0.778 s, while the 3.956–4.392 s observation still lacked SendAmount; it was first observed by 5.448 s from command start. A repeat was first observed by 3.345 s. These are sampled bounds, not exact presentation times; the three-second assertion remains unpassed (Contact payment latency on staging with the shared Paykit runtime: pin paykit-rs#171 and re-measure #868).
  • Current-head picker subset passed: Only the selected row showed progress, all rows disabled during lookup, and physical taps on another row opened no second flow. Full dismissal/reopening allowed a prepared endpointless peer to error and re-enable the rows; a subsequent payable selection opened one amount screen. This fixture result does not close the separately tracked deterministic setup issue Align contact-resolution failure setup across QA journeys #1031.

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 — 6929bf85

Integrated actual master 2ce21025 after maintainer #1130 retired the unavailable Blocktank creation cases. Signed merge parents are 6cef2d2f / 2ce21025; exact tested tree 7bcb32b3. 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 6cef2d2f after 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 40341466044 attempt 2 still failed on the four documented Blocktank creation cases. E2E workflow 40341466580 was independently rerun by ovitrif at 16:45:38 UTC: attempt 2 subsequently completed successfully at 17:44 UTC on the older 6cef2d2f head; 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

  • Planning/scoping: gpt-6-astra (reasoning: xhigh)
  • Implementation: gpt-6-astra (reasoning: xhigh)
  • Review: 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.

@ovitrif ovitrif added this to the 3.0.5 milestone Oct 18, 2026
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[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

  1. P2 Journey lacks required setup ▶

Summary

This PR disables contact rows while payment details resolve and updates the shared request/subscription amount screen.

  • Payment request and subscription amounts stay reachable on small screens.
  • The contact picker locks every row while a payment lookup runs.
  • The receive journey checks wallet tabs, network labels, and bottom controls.

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

Comment thread journeys/paykit-amount-layout.xml Outdated

@talosmachina talosmachina left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: pay clears selectedContactKey in a defer, 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: amountActions emits the asset HStack plus the optional unit button, and both ViewThatFits branches 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.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 flexible Spacer that 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 USDBase, 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 000 key reads "0…" on both builds; NumberPad is 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.

Comment thread Bitkit/Views/PaymentRequests/CreatePaymentRequestView.swift
@ben-kaufman

Copy link
Copy Markdown
Contributor Author

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 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code scan: no findings.

Checked and clean:

  • USD input uses Locale.current.decimalSeparator and BTC uses .. handleInput and parsePaykitAmountInput are unchanged.
  • The hidden amountControls copy 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 and copyValue are unchanged. Only the logo's sizing source moved, and both QrArea callers now have a bounded height.
  • SendContactSelectView.disabled: pay() clears selectedContactKey in a defer, 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.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: ⚠️ Not verified: the contact handoff exceeds the journey’s three-second target.

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.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

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.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Resolved the master conflict while retaining the compact amount layout and master’s inputValue conversion fix.

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 b45b22ad. All six PR commits are signed and verified locally and on GitHub. Existing device journeys still apply; this integration did not rerun funded flows.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread journeys/payment-requests/contact-request-or-pay.xml Outdated

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: ⚠️ Not verified: the contact handoff still exceeds the journey’s three-second target.

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

@jvsena42

Copy link
Copy Markdown
Member

Device gate (partial): 2297f2c, iPhone 17 (402 pt), English. paykit-amount-layout.xml steps 1–4 pass:

  • At $1.50 the amount leads near the header, with BTC/USD trailing and the divider above the keypad.
  • Three BTC↔USD switches at default text gave identical screenshots, and 1.50 USD → ₿1,827 → $1.50 shows no drift.
  • At accessibility-XXXL the unit button wraps, but the divider and keypad keep the same y. The keypad scrolls so every key and Continue are reachable, and Continue shows $1.50.

⚠️ Not run yet: amount-layout steps 5–7 (subscription amount, accepted methods; compact width needs a compact simulator), usdt-wallet-receive-layout.xml, and contact-request-or-pay.xml.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

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.

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

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.

@ben-kaufman
ben-kaufman requested a review from jvsena42 October 20, 2026 01:25
@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Published signed normal merge 754a04cbd143a263e788246cdbffce3c86667ce0, resolving the request-amount conflict while preserving the layout/scrolling changes and master’s dot decimals, empty-zero draft and contact presentation ownership. All 15 commits match the exact GitHub range and verify locally and on GitHub.

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 f7074d59 with the same simulator and unchanged test; it remains a failed path, with root cause unconfirmed. No readiness guard or assertion was weakened. SwiftFormat is clean, and all 15 translation warnings match 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.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.mp4

Entry, reopening, denomination switching and keypad.
J1-compact-subscription2x.2x.mp4

USDT-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:

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:

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 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code scan of b17f49c..754a04c: one LOW inline, reproduced on the simulator.

  • The QR-logo and USDT-footer thread is fixed (b17f49c overlay, 17e6533 spacer). That spacer is what causes the LOW, so it's a new thread, not a reopen.
  • The merge resolutions are correct: ac54f6e keeps copyMessage on both copy paths and the ProgressView; 754a04c keeps the PR layout with master's logic.
  • The SendContactSelectView .disabled guard, the hidden BTC field and QR.swift are unchanged since b17f49c.

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.

Comment thread Bitkit/Views/Wallets/UsdtReceiveView.swift
@jvsena42

Copy link
Copy Markdown
Member

Device gate (partial): 754a04c — the swipe-band LOW is verified (inline thread above). journeys: usdt-receive-swipe-tabs.xml 7/7 passed (iPhone 17 Pro, funded wallet); usdt-wallet-receive-layout.xml steps 3, 4, 6 and 7 passed, with 1-2 carried from my earlier run since this head's delta doesn't touch those views. perf: not relevant; l10n: no UI text changed.

  • Step 3: switching Savings / Auto / Spending / USDT keeps Show Details at the same bottom position.
  • Step 4: Network shows Arbitrum One, and Show Details opens the Arbitrum One address card.
  • Step 6 (iPhone 17e): the QR sits above Edit/Copy/Share. Copy puts the plain address on the clipboard, and the QR decodes to ethereum:0xFd08…cbb9@42161/transfer?address=0xEc32…C919.
  • Step 7 (17e at AX5): Network and Show Details stay reachable. Edit/Copy/Share break mid-word, which is Keep the 000 keypad label readable at largest text #1036 on master.

Still pending: step 5 (hardware vendor label), which needs the Trezor emulator paired. It wasn't free this pass and will run next.
⚠️ Not run: the 320/375-pt compact checks, because no SE-size simulator exists on this setup (the 17e at 390 pt is the smallest).

Skipped — already driven at this head: J1 paykit-amount-layout.xml and J3 contact-request-or-pay.xml (iOS, by ovi-reviewer: #1003 (review)).

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread Bitkit/Views/Wallets/UsdtReceiveView.swift

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

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:

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

@piotr-iohk

Copy link
Copy Markdown
Collaborator

@ben-kaufman conflicts.

Preserve stable amount controls with the single denomination toggle.
Allow UI tests to use explicitly isolated USDT fixture endpoints.
@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Resolved the master conflicts in signed merge 6cef2d2f425e86e76497fa5cd8111e1b115facc2 (actual master 65d0ae38). GitHub now reports this PR mergeable. The resolution preserves the single denomination toggle and stable amount layout.

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 piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@ovitrif

ovitrif commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

@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.

Copy link
Copy Markdown
Contributor Author

Integrated master 2ce21025 in signed merge 6929bf85c68ba1d005abef09106b8e17cf4e0c1c, preserving both parents and the exact reviewed tree. The Blocktank test changes are the four skips already merged through #1130; no additional assertions or test exclusions were changed here.

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.

@ovitrif ovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, approved on 6929bf8 with all checks green.

@ovitrif
ovitrif merged commit 536abb4 into master Oct 26, 2026
20 checks passed
@ovitrif

ovitrif commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

@ben-kaufman Merged, as it was approved and green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants