Repository navigation
fix: defer passive proof display refreshes - #1066
Conversation
c361c6b to
6153563
Compare
There was a problem hiding this comment.
Verdict: ✅ Approve
Findings: No actionable findings.
QA for 6153563: iPhone 17 Pro simulators on iOS 26.5 (payer) and 26.4 (requester), disposable Bitcoin-regtest wallets and staging Pubky.
Test 1: proof display admission and foreground recovery ✅ Background admission deferred during review, preserved warm rows, and foreground return recovered the display.
I emitted a local debug proof event and paused the actual AppScene observer after its idle check, before its display refresh. Opening a separate 1,000-sat Bitcoin confirmation and releasing the pause made real Background SDK admission defer. The trace confirmed that the observer returned false without cancellation, keeping one pending request and one history row.
The requester sent a second, 2,000-sat request while confirmation stayed open. After cancelling review and backgrounding/reopening the app, the retained observer completed a fresh read and cleared its token. Existing inbox refresh discovered the second request. The recording ends with both unpaid rows visible; no payment was authorized. SDK responses and request records were real; the hook supplied the event, pause and diagnostic lines.
proof-display.2x.mp4
Payer on the left, requester on the right, synchronized at 2×. Admission and row retention were checked in the SDK/observer trace.
Local test controls and deciding trace
Not pushed. These debug controls emit the proof event, hold the observer until an explicit release, and log the existing admission decision. They do not replace SDK results or request records.
trigger warm=1 history=1
held after idle before display refresh
released review=true
SDK admission deferred Background
observer refreshed=false warm=1 history=1 cancelled=false
...
observer refreshed=true warm=1 history=1 cancelled=false
token completed
The successful observer read occurred after foreground return; the second incoming request was then loaded by existing inbox refresh.
diff --git a/Bitkit/AppScene.swift b/Bitkit/AppScene.swift
index a0163e0b..6d880974 100644
--- a/Bitkit/AppScene.swift
+++ b/Bitkit/AppScene.swift
@@ -639,6 +639,18 @@ struct AppScene: View {
private var paykitEventContent: some View {
configuredContent
+ .task { // test hook
+ #if DEBUG
+ guard ProcessInfo.processInfo.arguments.contains("-proof-display-qa") else { return } // test hook
+ while !Task.isCancelled { // test hook
+ if ProofDisplayQAHook.consume("proof-trigger") { // test hook
+ ProofDisplayQAHook.log("trigger warm=\(paykitPaymentRequestManager.pendingRequests.count) history=\(paykitPaymentRequestManager.historyRequests.count)") // test hook
+ PaykitPaymentProofService.qaTriggerProofStateChange() // test hook
+ } // test hook
+ try? await Task.sleep(for: .milliseconds(250)) // test hook
+ } // test hook
+ #endif
+ }
.onChange(of: pubkyProfile.authState, initial: true) { _, authState in
receivedPaymentBackfillCache.invalidate()
if authState == .authenticated, let pk = pubkyProfile.publicKey {
@@ -701,11 +713,20 @@ struct AppScene: View {
try await PaykitPaymentActivity.shared.waitUntilIdle(includingReviews: true)
} catch { return }
guard !Task.isCancelled, request == pendingPaykitProofRefresh else { return }
+ #if DEBUG
+ await ProofDisplayQAHook.holdIfArmed() // test hook
+ #endif
let refreshed = await refreshIncomingPaykitPaymentRequests(
mode: .stored, forceFresh: true, refreshPriority: .background, automatic: false
)
+ #if DEBUG
+ ProofDisplayQAHook.log("observer refreshed=\(refreshed) warm=\(paykitPaymentRequestManager.pendingRequests.count) history=\(paykitPaymentRequestManager.historyRequests.count) cancelled=\(Task.isCancelled)") // test hook
+ #endif
guard !Task.isCancelled, refreshed else { return }
paykitProofRefresh.complete(request)
+ #if DEBUG
+ ProofDisplayQAHook.log("token completed") // test hook
+ #endif
}
.onReceive(PaykitPaymentProofService.onchainPaymentResolutionPublisher) { resolution in
Task { @MainActor in
@@ -2091,3 +2112,33 @@ struct AppScene: View {
)
}
}
+
+#if DEBUG
+@MainActor
+final class ProofDisplayQAHook { // test hook
+ static var enabled: Bool { ProcessInfo.processInfo.arguments.contains("-proof-display-qa") } // test hook
+ static let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] // test hook
+ static func consume(_ name: String) -> Bool { // test hook
+ let path = directory.appendingPathComponent(name) // test hook
+ guard enabled, FileManager.default.fileExists(atPath: path.path) else { return false } // test hook
+ try? FileManager.default.removeItem(at: path) // test hook
+ return true // test hook
+ } // test hook
+ static func log(_ text: String) { // test hook
+ guard enabled else { return } // test hook
+ let url = directory.appendingPathComponent("proof-qa.log") // test hook
+ let data = Data(("\(Date().timeIntervalSince1970) \(text)\n").utf8) // test hook
+ if let handle = try? FileHandle(forWritingTo: url) { // test hook
+ _ = try? handle.seekToEnd(); try? handle.write(contentsOf: data); try? handle.close() // test hook
+ } else { try? data.write(to: url) } // test hook
+ } // test hook
+ static func holdIfArmed() async { // test hook
+ guard consume("proof-arm") else { return } // test hook
+ log("held after idle before display refresh") // test hook
+ while !Task.isCancelled, !consume("proof-release") { // test hook
+ try? await Task.sleep(for: .milliseconds(100)) // test hook
+ } // test hook
+ log("released review=\(PaykitPaymentActivity.shared.shouldDeferRefresh)") // test hook
+ } // test hook
+} // test hook
+#endif
diff --git a/Bitkit/Services/PaykitPaymentProofService.swift b/Bitkit/Services/PaykitPaymentProofService.swift
index d86f7905..b047d1ee 100644
--- a/Bitkit/Services/PaykitPaymentProofService.swift
+++ b/Bitkit/Services/PaykitPaymentProofService.swift
@@ -234,6 +234,10 @@ actor PaykitPaymentProofService {
private static let proofStateChangedSubject = PassthroughSubject<Void, Never>()
private static let onchainPaymentResolutionSubject = CurrentValueSubject<PaykitOnchainPaymentResolution?, Never>(nil)
+ #if DEBUG
+ static func qaTriggerProofStateChange() { proofStateChangedSubject.send() } // test hook
+ #endif
+
nonisolated static var proofStateChangedPublisher: AnyPublisher<Void, Never> {
proofStateChangedSubject.eraseToAnyPublisher()
}
diff --git a/Bitkit/Services/PubkyService.swift b/Bitkit/Services/PubkyService.swift
index 1fd0a3b4..ee21b12f 100644
--- a/Bitkit/Services/PubkyService.swift
+++ b/Bitkit/Services/PubkyService.swift
@@ -1545,6 +1545,9 @@ actor PaykitSdkService {
private func checkRefreshAdmission(_ priority: PaykitSdkOperationLock.Priority) async throws {
if priority == .background, await PaykitPaymentActivity.shared.shouldDeferRefresh {
+ #if DEBUG
+ await ProofDisplayQAHook.log("SDK admission deferred Background") // test hook
+ #endif
throw PaykitPaymentRequestError.operationInProgress
}
}Test 2: pristine build and affected hosted regressions ✅ The unmodified head built and its focused suites passed.
I ran PaykitPaymentRequestServiceTests, PaykitPaymentActivityTests, PaykitSdkOperationLockTests and PaykitSdkIdentityCheckTests. The XCTest result bundle confirms execution without failures or skips, covering forced Background reads after admitted snapshots, preserved warm rows, stale identity/cancellation, explicit Ordered recovery and review-scoped refresh tokens.
focused.2x.mp4
Simulator recording during hosted execution, at 2×; assertion outcomes were checked in the XCTest result bundle, not inferred from the recording.
Coverage: full 7ed5b69…6153563, all five changed files and reached lifecycle/admission callers. Concurrent snapshots, identity/cancellation and explicit Ordered recovery have source and automated coverage. The native case used an injected proof event; it does not establish payment settlement or a latency improvement.
Twin: synonymdev/bitkit-android#1639 fix: defer passive proof display refreshes at ce4db93 matches forced Background display admission, warm-state retention and existing-refresh recovery. iOS also waits for review idle and retains a session-scoped refresh token.
Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest
|
Milestone 3.2.0: it is the base of the Paykit latency stack (#1070, #1081, #1084), all on 3.2.0. Twin: synonymdev/bitkit-android#1639. |
[Medium impact] The PR appears safe to merge; no actionable issues were found. SummaryAutomatic proof-triggered request updates now wait for payment review and submission to finish.
ben-kaufman explicitly leaves immediate-idle retries and preemption of admitted work out of scope. No finding is raised for those choices. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Proof state changes] --> B[Keep session-scoped refresh token]
B --> C[Wait for payment and review to end]
C --> D[Request a fresh Background read]
D --> E{Read succeeds?}
E -->|Yes| F[Update request rows and complete token]
E -->|No| G[Keep token and usable rows]
G --> H[Existing foreground or new-event opportunity]
H --> C
Reviews (1) · Last reviewed commit: "merge: carry current send routes through..." · Reviewed by Greptile |
talosmachina
left a comment
There was a problem hiding this comment.
No findings. Makes the proof-triggered display refresh wait for review as well as submission, and run with Background admission so a payment that starts in between wins. Reviewed 5e801df, full tier, reasoned from the code and CI: iOS does not build on this host.
What I checked, and 4 candidates I ruled out
Read in full: the diff; PaykitPaymentActivity (waitUntilIdle, endReview, resumeIdleWaiters), PaykitPaymentProofRefreshState, PaykitPaymentRequestManager.refresh (public and private), PubkyService.checkRefreshAdmission
Call sites traced: refreshIncomingPaykitPaymentRequests(refreshPriority:) (only the proof task passes it), beginReview (SendSheet, SendPendingScreen)
CI: SwiftFormat and Greptile green; Run Tests still pending at review time
Ruled out
- Waiter never woken when review ends while a payment is active:
endReviewresumes waiters only throughresumeIdleWaiters, which returns while payments remain;endresumes them later and thewhileloop re-checks reviews, so no lost wake-up. - Token consumed by a deferred read:
paykitProofRefresh.completenow runs only when the refresh returned true, so a Background deferral keeps the session-scoped request for the next pass. - Forced freshness lost behind an in-flight refresh: the private
refreshwaits for the running task, then re-runs becauseforceFreshis set, carrying the resolved Background priority; the new test checks it. - Explicit recovery downgraded:
refreshPriority ?? (forceFresh ? .ordered : messagePriority)leaves every caller that does not pass it unchanged.
Merge confidence: 4/5, Run Tests is still pending and the branch was reasoned about, not built here.
Reviewer model: claude-opus-5-5
Reasoning effort: Not exposed (the runner does not report it)
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Full review of the complete PR diff against merge base 2bbd2d2, at 5e801df.
No new actionable code findings.
The proof-display task waits until payment submission and payment review are both idle, then forces a stored read at Background priority. A deferred or cancelled read leaves the session-scoped token and the current request rows in place, and a forced read still runs again after an in-flight snapshot. Forced reads that do not pass a priority stay on the Ordered lane, so explicit recovery is unchanged.
Source comparison with bitkit-android#1639 at ce4db93 matches that Background yield and Ordered recovery. iOS also waits for review idle and keeps the same session token until a later successful read; Android's observer waits on submission and uses Background admission while a review is open.
unit-tests and SwiftFormat passed on this revision. The new service and activity tests were inspected here; that unit-tests run executed the suite that contains them. Device testing: not performed in this review. The author's regression case needs admission fault injection, which is outside the journey capabilities, so this pass leaves that runtime race unexecuted.
|
The current test failure is The CI merge tree matches This is the test tracked in #1033, although this occurrence timed out at a different expectation from the previously reported appearance assertion. The evidence does not yet establish the same underlying cause or justify a production change. Update: the single retry passed at the unchanged |
There was a problem hiding this comment.
Verdict: ✅ Approve
Findings: No actionable findings.
QA for 2051eee: Xcode 27.0, iPhone 17 Pro simulator on iOS 26.5. Manual case 1 retains its passing evidence from 6153563; the affected hosted suites were run again on 2051eee.
Test 1: proof display admission and foreground recovery ✅ Carried from the recorded passing run at 6153563; the exercised behavior is unchanged.
The earlier review proved that the paused proof observer yields to real Background admission during Bitcoin review, preserves warm pending/history rows, and recovers current requests after cancellation and foreground return. It used a local proof-event/pause hook with real regtest request data and staging Paykit responses; no payment was authorized.
I compared the observer, activity/token state, admission check, request-manager refresh, foreground recovery and dependency pins with 6153563. They are unchanged. The send-route merge preserves the Bitcoin review path used in that run. This case was not replayed on 2051eee; its recording and deciding trace remain in the linked review.
Test 2: current-head build and affected hosted regressions ✅ The pristine head built and the affected hosted suites passed.
I ran PaykitPaymentRequestServiceTests, PaykitPaymentActivityTests, PaykitSdkOperationLockTests and PaykitSdkIdentityCheckTests. The result bundle and individual test results confirm execution without failures, skips or runtime warnings, including fresh Background reads after admitted snapshots, preserved warm rows after deferral, stale identity/cancellation rejection, explicit Ordered recovery and review-scoped refresh tokens.
hosted.2x.mp4
Hosted XCTest execution at 2×; assertion outcomes were checked in the result bundle, not inferred from the recording.
Coverage: delta 6153563…2051eee, merge integration and the five-file net PR diff. No open review findings remained. Merge-inherited USDT routing, warning, PIN and biometric UI changes were inspected in source; those native flows were not driven in this review. No payment-settlement or latency result is claimed.
Twin: synonymdev/bitkit-android#1639 fix: defer passive proof display refreshes at ce4db93 matches forced Background display admission, warm-state retention and existing-refresh recovery. iOS also waits for review idle and retains a session-scoped refresh token.
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
left a comment
There was a problem hiding this comment.
QA review
Scope: Re-reviewed the full PR at 2051eee after the comparison base changed from 2bbd2d2 to a161084d. That base range changes the payment-request refresh implementation and its tests, so this pass covered the complete diff and inherited no earlier coverage.
No new actionable code findings.
The proof-display task waits until payment submission and payment review are both idle, then forces a stored read at Background priority. A deferred or cancelled read leaves the session token and the current request rows in place. Forced reads without an explicit priority stay on the Ordered lane, so explicit recovery is unchanged. After a failed automatic read the token stays pending; the next read is a foreground return, a new proof event, or the existing 10-second inbox poll.
Source comparison with bitkit-android#1639 at ce4db93 matches the Background yield and Ordered recovery. iOS also waits for review to be idle before starting the read and keeps the same session token until a later read succeeds. Android starts the Background read once submission is idle, including while a review remains open. This is source comparison only; the Android PR was not assessed.
unit-tests and SwiftFormat passed on this revision. The changed service and activity tests were inspected here; that unit-tests run executes the suite that contains them. They were not run again locally. Device testing: not performed in this review. Manual case 1 still needs an admission pause that the journey capabilities do not provide.
Ready for device testing.
Suggested additional test cases
- iOS. Setup: the manual case 1 wallet, with one warm incoming request and the proof-display read paused before SDK admission. Action: open payment review, release the paused read so Background admission defers, cancel the review, dismiss the send sheet, and stay in the foreground for one 10-second inbox poll without backgrounding the app or emitting another proof event. Expected: the warm rows remain when the read defers, and the current request state appears from that poll.
Reviewer model: grok-4.7
Reasoning effort: Not exposed (run metadata names grok-4.7 and does not record a reasoning setting).
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Re-reviewed the full PR at 50a33e3 after the comparison base changed from a161084d to 753a4c6b. That base range changes the payment-request refresh implementation and its tests, so this pass covered the complete diff against merge base 753a4c6b and inherited no earlier coverage.
No new actionable code findings.
The proof-display task waits until payment submission and payment review are both idle, then forces a stored read at Background priority. A deferred or cancelled read leaves the session token and the current request rows in place. Forced reads without an explicit priority stay on the Ordered lane, so explicit recovery is unchanged. After a failed automatic read, the next read is a foreground return, a new proof event, or the existing 10-second inbox poll. Allowance processing from the new base runs in the shared refresh after that read; the token still completes only when the proof read itself succeeds.
Source comparison with bitkit-android#1639 at ce4db93 matches the Background yield and Ordered recovery. iOS also waits for review to be idle before starting the read and keeps the same session token until a later read succeeds. Android starts the Background read once submission is idle, including while a review remains open. This is source comparison only; the Android PR was not assessed.
unit-tests and SwiftFormat passed on this revision. The changed service and activity tests were inspected here; that unit-tests run executes the suite that contains them. They were not run again locally. Device testing: not performed in this review. Manual case 1 still needs an admission pause that the journey capabilities do not provide.
Suggested additional test cases
- iOS. Setup: the manual case 1 wallet, with one warm incoming request and the proof-display read paused before SDK admission. Action: open payment review, release the paused read so Background admission defers, cancel the review, dismiss the send sheet, and stay in the foreground for one 10-second inbox poll without backgrounding the app or emitting another proof event. Expected: the warm rows remain when the read defers, and the current request state appears from that poll.
Reviewer model: grok-4.7
Reasoning effort: xhigh
jvsena42
left a comment
There was a problem hiding this comment.
Code scan: no findings.
Checked and clean:
- The proof-state observer, the manager refresh, SDK admission and the operation lock: a passive proof display refresh waits for review to go idle instead of competing with the pay path.
- Review begin and end, including cancel and Back: the held token is released on every exit and the deferred refresh runs then; scene reactivation and the 10 s poll recover any miss.
- Cancellation and the allowance auto-pay guards are unchanged.
- Twin synonymdev/bitkit-android#1639 reads immediately and relies on the poll and Home resume, as discussed in the threads.
Performance-relevant (re-timed background read on the payment path). The gate times Continue → confirm and Confirm → success with a proof event arriving during the payment, head vs master, and how long a row takes to update after review is cancelled (bound 10 s).
Device gate (partial): 50a33e3 — not run yet; pending that measurement against master. Device results will follow in a separate review.
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Full reassessment of the complete pull request diff against merge base 7c8c302, at 5ab68459. The comparison base changed from d404f925 to 7c8c302 and that range touches BitkitTests/PaykitPaymentRequestServiceTests.swift, so this pass re-read the whole diff and inherited no earlier coverage.
No new actionable code findings.
The passive proof-display task waits until payment review and submission are idle, then force-reads stored requests at Background priority. A deferred or cancelled read leaves the session token pending. Allowance refresh, automatic payment, backfill, and presentation run only after that read returns success, so warm rows are not treated as a fresh snapshot. That early return still covers the resolved warm-row payment thread. Compared with bitkit-android#1639 at 86688287, both success-gate allowance and presentation. This app waits for the review token before starting the read. Android's proof collector proceeds once submission is idle, and Background admission rejects the read while a review is held.
Validation: unit tests and SwiftFormat passed on this head. This review inspected those tests and did not run them. Device testing: not performed in this review. An attributed simulator replay of Background deferral is recorded at 5277d37 in the resolved thread. Manual case 1 remains the runtime check for the admission race.
Ready for device testing.
Reviewer model: grok-4.7
Reasoning effort: high
There was a problem hiding this comment.
Verdict: ✅ Approve
Findings: No new actionable findings. The deferred-refresh fix still returns before allowance processing, backfill and presentation when the read defers.
QA for 5ab6845: Reviewed 5ab6845 with Xcode 27.0, iPhone 17 Pro simulators on iOS 26.5. I built the pristine head and ran the affected hosted suites. Test 6 is a new native run with a disposable regtest wallet and a linked fixed-conversion issuer. Tests 1, 3, 4 and 5 retain their passing evidence at 5277d37; they were not replayed.
Test 1: proof display admission and foreground recovery ✅ Carried from the passing run at 5277d37; its exercised path is unchanged.
The recorded run held the real proof observer before admission, opened an unrelated Bitcoin review, and confirmed Background deferral with warm rows retained and downstream work skipped. Foreground return completed the retained fresh-read token. Its unpushed event/pause/log controls and deciding trace are in that report.
I checked the unchanged observer, refresh helper, admission, token and dependency sources. That run dismissed its ordinary URI send before backgrounding, so it does not enter the incoming-request app-lock branch changed by this delta.
Test 2: pristine build and affected hosted regressions ✅ The exact-head build and selected suites passed, checked in the XCTest result bundle.
I ran the request-service, activity, SDK operation-lock, SDK identity and send-sheet/confirmation lifecycle suites without failures or skips. The new testAppLockKeepsOtherRequestScreensUntilDismissal passed, including state preservation on remount and cleanup on actual dismissal. This also covers forced Background reads, warm rows, cancellation, identity changes and explicit Ordered recovery.
The result bundle contains no runtime warnings. The execution log retains intentional offline/node-unavailable fixture messages and simulator diagnostics; this is not a warning-free log claim. SwiftFormat passed for both Swift files in the delta.
hosted.2x.mp4
Hosted execution at 2×; assertion outcomes were checked in the result bundle.
Test 3: poll-only recovery after cancelling review ✅ Carried from the passing run at 5277d37; the poll and cancellation path are unchanged.
The recording and poll trace show a real request arriving during review and appearing on the next foreground inbox poll after Back and dismissal, without another proof event or background return. It shares Test 1's admission sequence and does not exercise the changed app-lock branch.
Test 4: contact payment with concurrent proof events ✅ Carried from the passing run at 5277d37; the ordinary contact-payment path is unchanged.
The recorded payment reached Bitcoin Sent for 10,000 sats after ordinary payment PIN approval, with proof events at Continue and submission. This was a contact Pay flow without incoming-request preparation or app lock. The paired master comparison remains historical evidence; I make no fresh latency or mined-finality claim.
Test 5: covered in-flight request during deferral ✅ Carried from the passing run at 5277d37; protection, filtering and the downstream success gate are unchanged.
The recorded allowance/proof case confirmed that the deferred read skipped every downstream stage and recovery excluded a locally protected request from the payable list. Its unrelated URI review was dismissed before backgrounding. The protected attempt was staged without broadcast; this does not establish allowance execution or settlement.
Test 6: request Amount, confirmation and payment-PIN app locks ✅ The request retained 1,000 sats through app unlock, remained reopenable, and left Sent empty, checked in UI snapshots and the recording.
I used a real one-time request for 1 USD with fixed rates btc=0.00001 and usdt=1, offering regtest Bitcoin and USDT endpoints. The app was the pristine reviewed build; ordinary app and payment PINs remained enabled.
On Amount, Savings and 1,000 sats survived backgrounding and app-unlock PIN entry. Continue opened the same request's confirmation. Locking from confirmation restored that amount, a prepared fee, working Show Details and the swipe control. I then swiped to payment PIN, backgrounded without entering it, and entered only the app-unlock PIN. The app returned to unpaid confirmation requiring another swipe.
After closing, I reopened the same request from its pending-list Pay action, repeated the Amount lock/unlock and Continue steps, and closed without paying. The request remained pending and reopenable. Activity contained only the funding receipt, its Sent tab was empty, and the issuer still recorded the request as proposed with no payment proof. No payment was authorized in this case.
request-lock.2x.mp4
Reopened Amount → app unlock → confirmation at 2×. The additional confirmation/payment-PIN lock sequences and empty Sent were checked in the full recorded run and saved UI snapshots; the issuer status confirmed the request remained proposed with no proof.
Coverage: contiguous delta 5277d37..5ab6845, all four changed paths, the five-file net PR diff, reached cleanup/admission callers and the resolved finding. No planned case remains unrun. Native USDT warning/authorization, biometric and hardware-wallet flows were not driven; the hosted USDT-flagged cleanup fixture is state coverage, not native USDT UI coverage.
Twin: bitkit-android#1639 fix: defer passive proof display refreshes at 8668828 matches Background admission, warm-state retention and successful-refresh gating of downstream work. iOS also waits for review idle and retains a session-scoped token. This was a source comparison.
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
left a comment
There was a problem hiding this comment.
QA review
Scope: Full reassessment of the complete pull request diff against merge base 5a58bbdb, at 18ef46bf. The comparison base changed from 7c8c302 to 5a58bbd and that range touches the proof-refresh files, so this pass re-read the whole diff and inherited no earlier coverage.
No new actionable code findings.
The passive proof-display task waits until payment review and submission are idle, then force-reads stored requests at Background priority. A deferred or cancelled read leaves the session token pending. Allowance refresh, automatic payment, contact backfill, and presentation run only after that read returns success. Explicit ordered recovery stays on the Ordered lane. The resolved warm-row payment thread remains addressed by that early return.
Compared with bitkit-android#1639 at 86688287: both yield the passive read to review or submission and continue allowance and presentation only after success. iOS waits for review idle before starting the read. Android's proof collector gates on submission and recovers through background admission and a later home refresh.
Validation: this review did not execute tests. Inspected PaykitPaymentActivityTests.swift and PaykitPaymentRequestServiceTests.swift. Hosted unit/UI run 40412989532 and SwiftFormat run 40412989539 passed for this head. Device testing: not performed in this review. Manual case 1 still needs an admission pause the journey capabilities do not provide.
Suggested additional test cases
- iOS, disposable regtest wallet, Paykit UI enabled, linked fixture issuer, one incoming request already listed. Open review for a different payment, change the listed request's proof state while that review stays open, then dismiss review without backgrounding the app. The list shows the updated request state, and no automatic allowance payment runs during review. A read that defers during review keeps the previous rows until foreground return, a new proof event, or the 10-second inbox poll.
Reviewer model: grok-4.7
Reasoning effort: high
There was a problem hiding this comment.
Verdict:
Findings: No new actionable findings. The deferred-refresh guard still returns before allowance processing, backfill and presentation when the read defers.
QA for 18ef46b: iPhone 17 Pro simulators, iOS 26.5, disposable regtest wallets and linked Paykit profiles.
Test 1: proof display admission and foreground recovery ✅ Background admission deferred during review, retained the warm request and skipped downstream work; the foreground poll loaded both requests and foreground return completed the token, checked in the admission and observer trace.
An unpushed debug hook emitted the proof event and paused the observer after its idle check, before real SDK admission. I opened an unrelated Bitcoin confirmation and released the observer; the trace showed Background admission deferred, one warm request retained and the early return before allowance processing, backfill or presentation. A second real request arrived during review. After closing review, the foreground poll loaded both requests; returning to the foreground completed the retained token. No payment was authorized in this case. The first recording stutters; the separate recovery recording shows the two rows.
deferral-paired.2x.mp4
recovery-2x.2x.mp4
Check 1: pristine build and hosted regressions ✅ The pristine head built, and the published hosted unit/UI run passed.
I built the app at this head. The hosted unit/UI run and SwiftFormat run passed for the published head. My duplicate local hosted-suite build was interrupted before it ran tests.
Test 2: foreground recovery after a proof-state change ⏭️ A locally staged in-flight request was filtered after review, but the submitted-proof and active-allowance combination remains untested.
With another payment in review, a debug control used the real proof service to mark an existing request in flight without broadcasting. The trace showed Background polls defer and skip downstream work while review was open; after closing review, the next foreground poll removed the protected request from the payable list. The run had no active allowance and did not submit a payment proof, so it does not fully cover the additional QA case.
proof-change-replay-2x.2x.mp4
Test 3: contact payment with concurrent proof events ✅ A 10,000-sat regtest contact payment reached Bitcoin Sent after payment PIN; the observer trace confirmed Background deferral during review.
An unpushed hook emitted proof events at Continue and submission. The recording shows confirmation and PIN entry; the success screen is shown separately because the recording ended during the transition. No latency improvement or confirmation-finality claim is made.
contact-pin-2x.2x.mp4
Test 4: covered in-flight request during deferral ✅ Carried from the recorded run at 5277d37; the exercised one-time request path is unchanged.
The recorded active-allowance run staged an unbroadcast in-flight request, observed the deferred read skip downstream work, and confirmed foreground recovery excluded that protected request. The delta changes recurring-request expiration handling; this test used non-expired one-time requests.
Test 5: request amount and confirmation app locks ✅ Carried from the recorded run at 5ab6845; the exercised one-time request and app-lock paths are unchanged.
The recorded run retained the amount across app unlock, restored unpaid confirmation and left the request reopenable. It did not switch wallets or submit a payment, the send branches changed by this delta.
Coverage: contiguous delta 5ab6845..18ef46b, merged changes and the previously resolved finding. Native USDT authorization, biometric and hardware-wallet flows were not driven in this review.
Twin: bitkit-android#1639 fix: defer passive proof display refreshes matches the Background admission and successful-refresh gate; iOS also waits for review idle and retains a session-scoped token. This is a source comparison.
Reviewer model: gpt-6.1-terra
Reasoning effort: high
Reviewed by gpt-6.1-terra-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest
piotr-iohk
left a comment
There was a problem hiding this comment.
QA review
Scope: Follow-up review of changes since 18ef46bf, including the recreated send-confirmation path, at 342b5841. Inherited baseline: QA review. Proof-display waiting, success-gated allowance and presentation, ordered recovery, warm-row admission, review-token release, and the Android background-yield comparison at 86688287 remain inherited.
No new actionable code findings.
A recreated confirmation uses the retained submission flag for the swipe busy state. submitPayment returns before a second PIN or biometric prompt, leaves that flag with the original attempt, and does not append a failure route. The in-flight attempt still completes on the shared navigation path, and the swipe cannot start another payment while the flag is set.
bitkit-android#1639 at 12ce9d27 keeps confirmation loading in saved screen state. Its send confirmation screen is unchanged in that pull request, and this delta does not change the shared proof-refresh yield. The resolved warm-row payment thread remains covered by the unchanged early return.
SwiftFormat passed on this revision. Unit tests were still running; the new confirmation tests were inspected and not executed here. Device testing: not performed in this review.
Suggested additional test cases
- iOS, regtest wallet, payment PIN on and biometrics off. Open a fixed-amount Lightning confirmation, swipe to pay, and lock the app after authorization while the payment is still in flight. Unlock. The confirmation stays busy, Bitkit does not ask for PIN again, and the original attempt still ends on success or failure. An unpaid failure can be retried once after that result.
Ready for device testing.
Reviewer model: grok-4.7
Reasoning effort: high
The base branch was changed.
|
@ben-kaufman Retargeted this PR to master: its base, #1054, merged as 32ad197 and its branch was deleted. |
There was a problem hiding this comment.
Verdict: ✅ Approve
Findings: No new actionable findings in 18ef46b..981aa2e. The resolved deferred-refresh finding remains fixed.
QA for 981aa2e: Xcode 27.0, iPhone 17 Pro simulators on iOS 26.5, disposable Bitcoin-regtest wallets and linked staging Pubky identities. The pristine branch built with its committed bitkit-core 0.8.0-rc10 and Paykit 0.1.1-rc1 pins. Native runs used the unpublished debug holds described in the case notes; app PIN and payment PIN stayed enabled, with biometrics disabled.
Pristine build and affected hosted regressions ✅ The exact head built and the selected suites passed, checked in the XCTest result bundle.
The request-service, payment-activity, SDK lock/identity and confirmation lifecycle/swipe suites passed without failures or skips. The recreated-confirmation regression refuses another authorization and preserves submission ownership. The busy-swipe regression keeps the manual swipe busy while submission is retained. SwiftFormat passed for the changed Swift files.
new-regressions-2x.2x.mp4
The recording shows the two new regressions at 2×; the broader suite outcomes were checked in the result bundle. Its runtime-warning list is empty; raw logs retain offline, unavailable-node and other intentional fixture messages plus simulator diagnostics.
Submitted proof while another payment is under review ✅ Real SDK proof submission occurred during review, and foreground recovery showed the completed request without Pay, checked in the SDK trace and UI snapshots.
This closes the submitted-proof/active-allowance gap in the previous review. A real SDK allowance was active. I manually authorized an on-chain request payment, held its actual proof delivery, opened an unrelated Bitcoin confirmation, and released the proof. The SDK queued it with review active. Background polls returned before downstream processing; after closing review, Payments contained the paid request and its detail status was “Proof submitted,” without a Pay action. SDK inspection still showed the active allowance and no pending request.
This passed with a 1,000-sat request created before allowance acceptance, then with a 20,000-sat request above its 10,000-sat per-payment cap. Both were manual payments; this does not establish automatic allowance execution.
submitted-proof-repeat-2x.2x.mp4
The second execution at 2×, ending at Payments. The subsequent detail status and allowance state were checked in UI snapshots and the SDK trace.
The unpushed control pauses PaykitPaymentProofService.submitReady before its SDK read and submission. Proofs, SDK responses and on-chain sends remain real; no record or admission decision is substituted.
App unlock during an authorized Lightning payment ✅ Restored confirmation stayed busy and ignored another swipe; the original operation reached its unpaid result, checked in the recording, UI snapshots and native logs.
I drove the requested in-flight lock case with a fixed 1,000-sat invoice. After payment PIN, a local hook held the real operation immediately before LightningService.send. Backgrounding and entering only app-unlock PIN restored the busy spinner. Another swipe opened no payment PIN. Release reached the native RouteNotFound result expected from this fixture's invoice without a receiving channel.
One intentional retry then required fresh payment PIN and reached the same unpaid result; that retry was checked in saved UI snapshots and native logs. Spending balance stayed unchanged. The lock/reswipe sequence also passed in a separate fresh presentation.
lightning-lock-repeat-2x.2x.mp4
The separate lock/unlock execution at 2×. The confirmation source is unchanged by the hook; it only delays the authorized operation before native dispatch. This is failure/retry evidence, not successful Lightning settlement.
Contact payment with concurrent proof updates ✅ The current-head contact request payment completed after ordinary payment PIN while real proof invalidations were pending.
This shares the submitted-proof case's authorized on-chain execution and recording above. The new submission guard allowed idle authorization, while real proof preparation and completion emitted invalidations. The ordinary Contact Pay route's earlier passing evidence remains in the review at 18ef46b; endpoint resolution is unchanged. No new head-versus-master timing result is claimed.
Check: proof display admission and foreground recovery ✅ Retained passing evidence covers the unchanged admission and recovery path.
The recorded run at 18ef46b held the observer before SDK admission, proved Background deferral with warm rows retained and downstream work skipped, and recovered on the foreground poll and foreground return. The observer, refresh helper, activity/token state, SDK admission and dependency pins are unchanged in this delta. Manual case 1 was not replayed.
Check: covered in-flight request during deferral ✅ The earlier recorded allowance/protection case still covers its unchanged one-time request path.
The run at 5277d37 retained a real allowance-covered, locally in-flight request during deferral and excluded it from Pay after recovery. This case was not replayed; proof protection, filtering, the downstream success gate and the exercised review path remain unchanged.
Check: unpaid request amount and confirmation app locks ✅ Earlier passing evidence covers the unchanged unpaid restoration path.
The recorded run at 5ab6845 retained amount and unpaid confirmation across app unlock and left the request reopenable. This delta changes busy-state handling during submission; the unpaid preparation and cleanup paths remain unchanged. This case was not replayed.
Check: E2E merge checkout provenance ✅ The extracted guard accepted matching parents and rejected stale, malformed and failed-API inputs.
Shell boundary checks covered stale base/head, reversed or missing/extra parents, invalid PR input, empty/malformed revisions and each API failure. A live read confirmed merge 04fb1673 has ordered parents 32ad1977 and 981aa2e4, matching current master and this head. This is a workflow check, not a device test.
Coverage: contiguous 18ef46b..981aa2e delta, all five changed files and reached submission, navigation, swipe and workflow callers, plus the previously resolved finding. Native fixtures used Debug regtest configuration with geoblocking disabled for setup. The guard's automatic subscription re-entry received hosted coverage; native USDT, biometrics, hardware, successful Lightning settlement and latency measurements were not driven by these cases. No planned case remains unrun.
Twin: bitkit-android#1639 fix: defer passive proof display refreshes, checked at 12ce9d27, matches Background display admission and the successful-refresh gate before allowance/presentation work. iOS additionally waits for review idle and keeps a session-scoped refresh token. This was a source comparison.
Reviewed by gpt-6.1-sol-high
|
@ben-kaufman Merging this PR: approved on head 981aa2e with a device run of its cases on that head, checks green and no open threads. It merges before the 3.1.0 cut, so it ships in 3.1.0. |
|
@ben-kaufman Merged, as it was approved and green. |
|
Placing this PR in 3.1.0: it merged before the 3.1.0 cut, so it ships in 3.1.0. |
This PR lets automatic proof-triggered payment-request display updates yield to payment review and submission while retaining a fresh read on the next existing refresh opportunity.
Follows merged #1054 and targets master. No measured latency improvement is claimed.
Description
Out of Scope
Design
N/A — no UI changes.
Preview
N/A — no visual changes.
QA Notes
Setup
Use the journey guide and Payment Request setup at
2051eee1with disposable regtest wallets and a saved, linked fixture issuer. This source uses Paykit0.1.1-rc1and Core0.8.0-rc10. Keep ordinary payment authorization enabled. The controlled transport/admission faults named below require additional fixture control; compatible issuer/homeserver access and staging merchant/Locks unlock must be established separately. Missing prerequisites leave the affected cases blocked.Current hosted validation uses merge
04fb1673(master32ad1977plus this PR), with inherited Core0.8.0-rc11, LDK0.7.0-rc.78, Paykit0.1.1-rc2and VSS0.5.26-rc1. The branch-only reports below retain their older pins and are not current-composition runtime evidence.Journeys
N/A — not drivable; see Manual Tests.
Manual Tests
Automated Checks
e2e-tests.yml(after merge) — rejects stale merge checkouts before building; local shell/API boundary checks cover valid, stale, malformed and failed-read inputs.PaykitPaymentRequestServiceTests.swift— forced Background reads wait for admitted snapshots, preserve warm rows after deferral, reject stale identity/cancellation, and let explicit Ordered recovery and a later current read succeed.PaykitPaymentActivityTests.swift— the session-scoped refresh token survives while review remains active after submission ends and newer invalidations coalesce.Evidence
Final integration validation
Integration with master
4530ff7bat2051eee1retains both USDT-warning cancellation and the payment-review callback. Bottom #1025: all-target build passed (19 targets), 378 focused tests passed, 0 failures/skips. One combined #1070 + #1081 + #1084 + #1085 composition, tree8f1e72d3from #10707cd096f4plus #10818967dff8, #1084b7561c08and #1085310e689a, covers the stack and those sibling changes: all-target build passed (19 targets) and 2,779 tests passed, 0 failures/skips using the six existing CI exclusions. These are not eight independent full-suite runs.All eight heads passed SwiftFormat. Translation checks report 0 errors and 15 warnings, all matching master by key, language, file and occurrence count, including
security__use_pin. Final toolchain/runtime and complete diagnostic disposition: Xcode 26.1 / iOS 27.0 simulator, serial tests; complete diagnostics inspected, with six SDK-construction QoS warnings and retained fixture/appearance messages matching baseline evidence; no introduced finding. Final-head CI after publication: pending.October 23 maintenance validation
Current head
5277d370includes masterf650cbb4and the deferred-refresh guard from review 4356902947. Both 19-target builds passed; exact #1025 passed 2,897 canonical hosted unit tests and the pure combined stack passed 2,923, with zero failures/skips and the six existing CI live-integration exclusions. The 16 focused lifecycle cases also passed. All eight SwiftFormat scans are 0/843; translation checks have zero errors and 15 warnings matching executed master f650cbb. Complete runtime diagnostics were inspected, including retained SDK-construction QoS and hosted-view/fixture notices; this is not a warning-free runtime claim.Combined tree
d8fd91e2includes #1070974be754and sibling heads #1081c26e8b82, #10842b3b7aa2, #108589f05b40. These are two full suites, not eight independent full-suite runs. The first bottom attempt exposed an inactive-scene setup in the new lifecycle test; only that fixture was corrected, and the final focused/canonical runs passed. Independent source review covered the deferred guard and presentation/token/app-lock integration. Native case 1 remains unexecuted; new-head CI is pending.Gaps
Earlier full-suite SDK-construction QoS and hosted UIKit appearance warnings remain recorded separately. Recovery uses the existing foreground/remount/new-event paths and regular polling; this change does not promise immediate retry when review ends.
The final Android 206 / iOS 215 comparison cohort was blocked before financial authorization, with no debit. It supplies no final payment-latency result or native benefit claim. Case 1 remains unchecked and unexecuted on this integrated head. Native USDT-warning, PIN and biometric flows have not been verified by this integration validation.
October 26 master integration
Published integration head
3ba0785f1766de1d67b6dc3bf6e4f7173ae41dfcincorporates actual master2ce210252be8ae7ea130d2422a2fa704a191ef7dthrough ordinary signed merges, preserving the stacked base and prior child changes. The two parent conflict resolutions retain review-state ownership, root-safe subscription Back navigation, one outer USDT status task, prompt recipient-scoped proof reconciliation and the terminal-refresh exit. No local assertion/timeout weakening or new test suppression was introduced.Validation: two 19-target builds and two canonical hosted unit suites passed: 2,910 on the parent and 2,937 on the combined tree, zero failures/skips with the exact six CI integration exclusions. This PR is covered by the combined stack/sibling tree
f471e8a6(2,937 units), including this branch; these are two suites, not eight independent runs. Full SwiftFormat0.64 passed on all eight final trees (0/844); translations had zero errors and 15 warnings matching execution on complete actual-master source. Independent frozen integration review found no actionable issue. Local environment: Xcode26.1/SDK26.1, iOS26.5 simulator.Complete diagnostics retained: two App Intents notices per build; parent1,082/combined1,099 runtime scan records, including four/six SDK/Pubky/DHT QoS stacks, fault fixtures and hosted appearance/identity notices. Source comparisons are not exact-base runtime replay or warning-free evidence. New-head hosted CI follows publication. Master's #1130 retired Blocktank skips and E2E285 channel/migration omissions remain coverage gaps, not fixes for original #1109. Historical #1054 preparation/UI-launch causes remain unresolved. Existing QA case IDs and native gaps remain: no new payment, funded USDT, physical hardware, latency or merchant-unlock journey was run by this integration.
October 26 lifecycle integration and camera fixture
Published head
18ef46bfb08be5170bbc7a72ed232be038448153includes approved and merged #1098 through masterb8b7f3d9902de5669c025bb839232c1d7e98d2df. The overlapping lifecycle test retains one owned switch task per current context, holds resumed preparation until confirmation is visible, and keeps the exact-one-success assertion, cancellation/context guards and retained presentation/app-lock behavior. Seven ordinary signed merges preserve stacked bases and prior child changes; #1084/#1085 also retain merged #1081 through #1070.The existing unfunded USDT UI case now resets camera permission before launch, explicitly waits for the camera alert, allows it and waits for dismissal before manual entry. This adds six test-only lines and two assertions; no test, assertion or timeout was removed or weakened. Production camera behavior and the merged Receive blank-band checks are preserved.
Final validation passed two 19-target builds, 2,913 parent canonical units and 2,940 units on combined tree
db653be56e228dec55f4e6c0fd57e42e7d8475c2, with zero failures/skips and the exact six CI integration exclusions. This branch is included in that combined composition. These are two suites, not seven independent runs. The combined unfunded UI case passed, including both Receive blank-band swipes and explicit camera handling, using runner-only loopback endpoint settings. Full SwiftFormat 0.64 passed on all seven final trees (0/844); translations are unchanged. Independent final review found no actionable issue. All 151 distinct commits across local current/prospective base ranges verified cryptographically; all 129 distinct commits across the final published GitHub ranges report verified=true/reason=valid.Complete diagnostics remain retained: parent 1,107/combined 1,125 broad records, including four/six SDK/Pubky/DHT QoS stacks, App Intents notices, hosted appearance and fault-fixture messages. The combined shared-keychain -25300 publication diagnostic has unchanged emitter/test source; its exact runtime cause remains unestablished. Source comparison is not exact-base runtime replay or warning-free evidence.
The old Receive-dismissal failure in #1070 run 40394372963 did not reproduce in the controlled original-head run; that run passed and used XCTest's default camera interruption handler. The camera-fixture comparison passed with an identical 157-file production app bundle. Neither result establishes the Receive failure's cause or repair. Local Xcode 26.1/SDK 26.1/iOS 26.5 differs from hosted CI; the completed hosted results are recorded below. The daemon launch issue #1110, original hardware-transfer issue #1109 and earlier coverage gaps remain. No new payment, funded USDT, physical-hardware, final latency or merchant-unlock result is claimed. João, Piotr and Ovi remain requested; existing pending requests are preserved without duplicate notification.
October 26 hosted CI confirmation
For published head
18ef46bfb08be5170bbc7a72ed232be038448153, unit/UI run 40412989532 passed on attempt 1: 2,929 unit tests, zero failures/skips; 43 UI executions, 39 passed and four existing prerequisite skips, zero failures. SwiftFormat 40412989539 passed: 0/844 files require formatting, 82 skipped. These workflows tested GitHub's PR merge commit using Xcode 16.4/SDK 18.5 on iOS 26.2.Complete workflow diagnostics were inspected. The 571 duplicate-symbol records match the retained passing #1025 run 40376172874; workflow/cache-action sources match actual master. This is retained-log/source comparison, not an exact-base runtime replay or warning-free result. The remaining red aggregate contains cancelled duplicate jobs; the replacement unit/UI and formatting workflows passed.
No manual CI rerun was requested. Historical Receive-dismissal/daemon-launch causes and original hardware-transfer issue #1109 remain unresolved; existing skipped/deferred coverage and final Android 206/iOS 215 payment/unlock measurement remain incomplete. Existing human review requests are preserved.
October 29 inherited app-lock guard
Published 342b5841 carries the four-file R1 correction from #1039 at aae380fc: a recreated confirmation uses the retained submission flag for its busy state and refuses another authorization while the original payment is in flight. This branch retains its existing Core 0.8.0-rc10, LDK 0.7.0-rc.77, Paykit 0.1.1-rc1 and VSS 0.5.23 pins, stacked base and prior history.
On this exact branch candidate, Xcode 27.1 / SDK 27.1 / iOS 26.5 passed the complete selected unit suite: 2,931 passed, 0 failed, 0 skipped, with the six existing unit-workflow integration exclusions. Both new confirmation regression tests passed. Full SwiftFormat 0.64.0 passed: 0/844 files require formatting; 82 existing exclusions. The shared private package copy was corrected before this branch's first test invocation; the sibling #1054's initial build-path failure remains retained. This branch's first full test run passed with unchanged source and dependency pins.
Complete diagnostics were inspected: two AppIntents metadata notices, five native SDK/Pubky/mainline DHT construction QoS warnings, and retained fault-fixture/host output. Forty-six emitter/test files were compared with this branch's pre-fix head; the warning-emitting methods in the two changed files are unchanged. No introduced actionable finding was found. This is source and retained-evidence comparison, not an exact-base runtime replay or warning-free claim. The identical production patch reuses the two independent R1 source reviews and guard negative control from #1039, with the older child callers inspected separately.
All 16 commits in the actual current-parent PR range verified cryptographically locally and report GitHub verified=true / reason=valid after the normal push. Hosted unit/UI run 40847784788 passed on merge
93a4f328into actual parented755997: 2,970 passed executions, 0 failed, 4 explicit fixture skips (2,941 unique passing definitions; one parameterized test has 30 runs). Both new confirmation regression tests passed. Hosted formatting passed 0/844. Full artifact and nested diagnostics were audited: the 286 duplicate native-symbol warning set matches retained history; a native keyboard queue timeout in the unchanged, passing tags-resume lifecycle test is retained. This hosted merge retains the same committed package pins; its result is separate from the local Xcode 27.1 branch run. Existing QA and timing gaps below remain.Existing manual case 1 and its documented admission-pause prerequisite remain unchecked on this new head. The external native R1 report on #1039 reports two successful controlled regtest executions at aae380f; it is attributed parent evidence, not a device run on this child. No new financial journey, binary adoption or final Android206/iOS215 latency/unlock result is claimed.
October 29 retarget CI provenance and completed current-base validation
The automatic retarget run completed 13 E2E passes, 0 failures and 1 explicit hardware-transfer skip on trial 1. Its producer built stale synthetic merge
93a4f328, parentsed755997and342b5841, whose tree equals the old child source with Core rc10 / LDK rc77 / Paykit rc1 / VSS 0.5.23. Complete producer and consumer diagnostics are retained, including 72 stale-element warnings and four collector restarts. That result does not validate the current master composition.Published 981aa2e4 adds a pre-build guard that checks the checkout's ordered parents against the live master ref and current PR head. It rejects malformed inputs and API failures; 11 extracted-shell boundary cases and a read-only live replay of the stale checkout passed their expected outcomes. One independent affected review found no actionable issue. Full local SwiftFormat passed 0/844, 82 exclusions; the unchanged branch app/unit inputs reuse their inspected 2,931 passed / 0 failed / 0 skipped result. All 17 current-base PR commits verify locally and on GitHub (
verified=true,reason=valid). App source and branch dependency pins are unchanged by this CI commit.The existing run was allowed to finish before the normal push. GitHub then regenerated merge
04fb1673, parents master32ad1977and head981aa2e4, tree7ba1f743, matching the clean local composition. The new producer passed the guard. Unit/UI passed 2,890 executions, 0 failures, 5 explicit prerequisite skips: 2,866 distinct cases, 2,861 passing definitions and 30 executions of the launch test. The selected unit suite passed 2,851 cases with one recovery-fixture skip; UI passed 39 executions with four Trezor/Arbitrum prerequisite skips. Integration passed 30 cases, 0 failures/skips. Neither result used a whole-test retry. Complete current-run structured and nested diagnostics were audited and their evidence hashes verified; older branch or parent passes were not transferred. E2E passed 13 cases, 0 failures and 1 explicit hardware-transfer skip on trial 1, with no completion-lock skips or observed test-body retries; trials 2 and 3 were skipped. The actual suite was0affc996. E2E retains 76 stale-element warnings, four native collector restarts and npm’s 76 vulnerability reports (8 low, 15 moderate, 46 high, 7 critical); individual advisories were not inspected by this result audit, and no security-clean or uninterrupted-native-log claim is made.Current hosted SwiftFormat passed 0/824, 96 exclusions; translations report 0 errors / 15 verified existing warnings. Nine optional migration jobs were skipped, not passed. Retained diagnostics include 571 native-link warnings (286 unique symbols) in each unit/integration build, 72 CoreGraphics NaN messages in UI output, LDK/Pubky/runtime messages, three passing integration cases with approximately 40-second wallet-sync timeouts, and one 5.32-second shutdown call. The six UI wait timeouts were two expected negative ContactImport waits and four optional onboarding/enable controls. Causes of the other runtime diagnostics remain unestablished; this is not an exact-base replay or warning-free result. Xcode supplied no separate console-log category; the nonzero export and successful complete diagnostic export are retained.
Companion bitkit-e2e-tests#288 supplies the already-validated row refresh and close-navigation checks. The actual consumer checked out
0affc99619cafe524583fe1be6c2b1129d8a597eand downloaded fresh app artifact12922499104; producer upload, API metadata and consumer download digests match. This does not attest the installed executable separately. Unit/integration used Xcode 16.4 / SDK 18.5 on iOS 26.2; the E2E app producer used Xcode 26.2 / 17C52 and the consumer ran on iPhone 17 / iOS 26.2. Manual case 1 and its admission/active-allowance fixture requirements remain unchanged; no new manual or latency result is claimed.Models used
gpt-6-astra(reasoning:xhigh),gpt-6.1-sol(reasoning:xhigh)gpt-6-astra(reasoning:xhigh),gpt-6.1-sol(reasoning:xhigh)gpt-6-astra(reasoning:xhigh),gpt-6.1-sol(reasoning:high)