Repository navigation
feat: accept allowance offers automatically on the allowee's wallet - #1385
Conversation
…t/allowance-auto-accept
…t/allowance-auto-accept # Conflicts: # app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
…t/allowance-auto-accept
|
Pushed 528fb7b: merged the updated
|
|
Added the device result and media to the description.
|
…t/allowance-auto-accept
|
Pushed e7a8bd3: merged the updated
|
|
Pushed df5105a: merged the updated
|
…t/allowance-auto-accept # Conflicts: # journeys/allowances/README.md
…t/allowance-auto-accept # Conflicts: # app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt
…t/allowance-auto-accept
|
Pushed 211d4c2: merged the updated
|
…t/allowance-auto-accept
…t/allowance-auto-accept
|
Pushed 1b61baa: merged the updated
|
…t/allowance-auto-accept
|
Pushed ba1c9bc: merged the updated
|
|
Pushed f93e1bc:
|
f93e1bc to
cda8521
Compare
|
Force-pushed cda8521: rewrote this branch so every commit in it is signed and verified, which master requires. It is now the single signed commit that #1340 became, plus one signed commit for this feature. The tree is identical to the previous head f93e1bc, so the code under review is unchanged; the history no longer carries the two unsigned sync commits from 2 Oct. @ben-kaufman @coreyphillips could you review? The approval this PR had was dismissed only because its base changed to master. |
coreyphillips
left a comment
There was a problem hiding this comment.
Two independent reviews, nothing blocking a merge.
worth doing, does not block
- Offer accepted as the polling job is cancelled is never announced (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer whose SDK accept completes while backgrounding cancels the polling job becomes active but is never announced. On
ON_STOP,ContentView.kt:323-324callsstopPaykitPaymentRequestPolling(). That cancelspaykitPaymentRequestPollingJob(AppViewModel.kt:1458), the coroutine running the 10 srefreshIncomingPaykitPaymentRequestsand soacceptOffersFromAllowers.PaykitSdkService.completeSdkCall(PaykitSdkService.kt:1687-1693) runsacceptAllowanceunderNonCancellableand then callsensureActive(). So the SDK records the acceptance and aCancellationExceptionis thrown.runSuspendCatchinginrespondandsendQueuedMessagesrethrows it,accepted += offernever runs, andannounceAcceptedAllowanceOffersis skipped. On the next refresh the record is ACCEPTED, so it is not answerable and is never announced. This contradicts the PR's claim that it "still announces what it accepted". ThecanContinueflag only helps callers outside the polling job, andoffer accepted as the app backgrounds is still announced...tests the flag, not cancellation. Process death right after the accept ends the same way. Impact is small: the allowance is active and listed, and only the "Allowance Added" notice is lost. Confirmed by reading the code paths above; I could not run the unit tests locally (no GitHub Packages access for Paykit). Possible fixes: persist accepted-but-unannounced keys inPaykitAllowanceLocalStateand announce from there, or run each accept and its bookkeeping inwithContext(NonCancellable). - Backgrounding can permanently suppress the allowance notification (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer accepted while the app backgrounds can become active without notifying the allowee.
stopPaykitPaymentRequestPolling()cancels the poll, whilecompleteSdkCall()lets acceptance commit before rethrowing cancellation. The announcement is then skipped. Later refreshes exclude the accepted offer, so the notification is permanently lost. A focused reproduction using the copied acceptance methods confirmed one committed acceptance and zero announcements after refresh. - Journal pruning erases allowance totals and payment history labels (app/src/main/java/to/bitkit/repositories/PaykitAllowanceExecutor.kt:840). Allowance totals decrease when settled payments leave the 200-entry journal.
PaykitAllowanceRepo.publish()derives both “Paid so far” and the “Auto-paid” labels solely from this journal, which also holds manual attempts. One automatic payment followed by 200 manual request payments therefore erases that allowance's displayed total and the older payment's label. A focused reproduction using the copied journal function confirmed the total dropping from 200 sats to zero.
nits
- Allowance journeys still describe a payee accept step (journeys/allowances/monthly-cap-reached.xml:11).
monthly-cap-reached.xmlstep 2 still says "and have the payee accept it". The payee no longer does anything, so a runner may look for a review sheet that never opens. Inpin-gates-allowance-changes.xml, the step starting "On the payee instance" ends with "and the payer's row turns Active", which mixes the two instances. Suggest "wait for the payee's wallet to accept it" and naming which instance's row is checked. - Test for an ask joining an offer's entry cannot fail (app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt).
offer accept never answers an ask that joined the entry after the checkadds the ask under a different counterparty (asker). Entries are keyed by counterparty and allowance id, so the ask lands in its own entry and the assertion holds even with the old lookup by entry id. Groups only ever hold the single allowance id this wallet proposed (PaykitAllowanceRepo.kt:266), so a received ask cannot join an offer's entry. The scenario in the test name and in the comment atPaykitAllowanceRepo.kt:305is unreachable. Consider dropping or renaming the test and the comment.
|
@coreyphillips thanks. With the 3.1.0 freeze, the non-blocking points go to 3.2.0 rather than this PR:
|
coreyphillips
left a comment
There was a problem hiding this comment.
Rechecked after replies, with no new commit since cda8521. Nothing blocking remains.
still open
- Offer accepted as the polling job is cancelled is never announced (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer whose SDK accept completes while backgrounding cancels the polling job becomes active but is never announced. On
ON_STOP,ContentView.kt:323-324callsstopPaykitPaymentRequestPolling(). That cancelspaykitPaymentRequestPollingJob(AppViewModel.kt:1458), the coroutine running the 10 srefreshIncomingPaykitPaymentRequestsand soacceptOffersFromAllowers.PaykitSdkService.completeSdkCall(PaykitSdkService.kt:1687-1693) runsacceptAllowanceunderNonCancellableand then callsensureActive(). So the SDK records the acceptance and aCancellationExceptionis thrown.runSuspendCatchinginrespondandsendQueuedMessagesrethrows it,accepted += offernever runs, andannounceAcceptedAllowanceOffersis skipped. On the next refresh the record is ACCEPTED, so it is not answerable and is never announced. This contradicts the PR's claim that it "still announces what it accepted". ThecanContinueflag only helps callers outside the polling job, andoffer accepted as the app backgrounds is still announced...tests the flag, not cancellation. Process death right after the accept ends the same way. Impact is small: the allowance is active and listed, and only the "Allowance Added" notice is lost. Confirmed by reading the code paths above; I could not run the unit tests locally (no GitHub Packages access for Paykit). Possible fixes: persist accepted-but-unannounced keys inPaykitAllowanceLocalStateand announce from there, or run each accept and its bookkeeping inwithContext(NonCancellable).acceptOffersFromAllowersat PaykitAllowanceRepo.kt:301-310 is unchanged, so cancellation after a committed accept still skipsaccepted += offerand the announcement. The deferral to #1672 is real: it is open, milestoned 3.2.0, and describes this exact path. It does not show the point wrong, so the point stays open as a tracked non-blocking item. The Description bullet "still announces what it accepted" is still only accurate outside the cancellation path. - Allowance journeys still describe a payee accept step (journeys/allowances/monthly-cap-reached.xml:11).
monthly-cap-reached.xmlstep 2 still says "and have the payee accept it". The payee no longer does anything, so a runner may look for a review sheet that never opens. Inpin-gates-allowance-changes.xml, the step starting "On the payee instance" ends with "and the payer's row turns Active", which mixes the two instances. Suggest "wait for the payee's wallet to accept it" and naming which instance's row is checked. Monthly-cap-reached.xml:11 still says "have the payee accept it". The payee step in pin-gates-allowance-changes.xml still ends with the payer's row turning Active. Both are listed as follow-ups in #1672, but the files are unchanged. - Test for an ask joining an offer's entry cannot fail (app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt).
offer accept never answers an ask that joined the entry after the checkadds the ask under a different counterparty (asker). Entries are keyed by counterparty and allowance id, so the ask lands in its own entry and the assertion holds even with the old lookup by entry id. Groups only ever hold the single allowance id this wallet proposed (PaykitAllowanceRepo.kt:266), so a received ask cannot join an offer's entry. The scenario in the test name and in the comment atPaykitAllowanceRepo.kt:305is unreachable. Consider dropping or renaming the test and the comment. The test at PaykitAllowanceRepoTest.kt:425 still adds the ask under a different counterparty (asker), so it cannot fail. The comment at PaykitAllowanceRepo.kt:305 still describes an unreachable case. Tracked in #1672, unchanged here. - Backgrounding can permanently suppress the allowance notification (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer accepted while the app backgrounds can become active without notifying the allowee.
stopPaykitPaymentRequestPolling()cancels the poll, whilecompleteSdkCall()lets acceptance commit before rethrowing cancellation. The announcement is then skipped. Later refreshes exclude the accepted offer, so the notification is permanently lost. A focused reproduction using the copied acceptance methods confirmed one committed acceptance and zero announcements after refresh. This is the same defect as point 1, and the code at PaykitAllowanceRepo.kt:306 and AppViewModel.kt:1192-1196 is unchanged. Deferring it to #1672 tracks it but does not answer it. - Journal pruning erases allowance totals and payment history labels (app/src/main/java/to/bitkit/repositories/PaykitAllowanceExecutor.kt:840). Allowance totals decrease when settled payments leave the 200-entry journal.
PaykitAllowanceRepo.publish()derives both “Paid so far” and the “Auto-paid” labels solely from this journal, which also holds manual attempts. One automatic payment followed by 200 manual request payments therefore erases that allowance's displayed total and the older payment's label. A focused reproduction using the copied journal function confirmed the total dropping from 200 sats to zero. Codex still sees it:PaykitAllowanceExecutor.kt:840-841still prunes the shared journal to 200 entries, whilePaykitAllowanceRepo.kt:559-572derives totals and labels from retained entries. The response references #1584 without fixing or rebutting the defect.
cda8521 to
e45f4ec
Compare
|
Force-pushed e45f4ec: re-signed the whole branch history instead of the earlier two-commit rewrite (cda8521), so every commit is signed and verified while keeping its tree, message, author and dates. The commits shared with #1340 are exactly its re-signed ones. The head's tree is identical to f93e1bc and cda8521, so the code under review is unchanged. |
coreyphillips
left a comment
There was a problem hiding this comment.
Rechecked after the branch was rewritten, so every earlier point was verified again from scratch. Nothing blocking remains.
still open
- Offer accepted as the polling job is cancelled is never announced (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer whose SDK accept completes while backgrounding cancels the polling job becomes active but is never announced. On
ON_STOP,ContentView.kt:323-324callsstopPaykitPaymentRequestPolling(). That cancelspaykitPaymentRequestPollingJob(AppViewModel.kt:1458), the coroutine running the 10 srefreshIncomingPaykitPaymentRequestsand soacceptOffersFromAllowers.PaykitSdkService.completeSdkCall(PaykitSdkService.kt:1687-1693) runsacceptAllowanceunderNonCancellableand then callsensureActive(). So the SDK records the acceptance and aCancellationExceptionis thrown.runSuspendCatchinginrespondandsendQueuedMessagesrethrows it,accepted += offernever runs, andannounceAcceptedAllowanceOffersis skipped. On the next refresh the record is ACCEPTED, so it is not answerable and is never announced. This contradicts the PR's claim that it "still announces what it accepted". ThecanContinueflag only helps callers outside the polling job, andoffer accepted as the app backgrounds is still announced...tests the flag, not cancellation. Process death right after the accept ends the same way. Impact is small: the allowance is active and listed, and only the "Allowance Added" notice is lost. Confirmed by reading the code paths above; I could not run the unit tests locally (no GitHub Packages access for Paykit). Possible fixes: persist accepted-but-unannounced keys inPaykitAllowanceLocalStateand announce from there, or run each accept and its bookkeeping inwithContext(NonCancellable). The code is unchanged: HEAD's tree is identical to cda8521's.respond(offer)at PaykitAllowanceRepo.kt:306 still runs the bookkeeping outsideNonCancellable.completeSdkCall(PaykitSdkService.kt:1687-1693) still callsensureActive()after the SDK commits, andstopPaykitPaymentRequestPolling(AppViewModel.kt:1454-1459) still cancels the polling job. The PR description now states the limitation accurately and tracks it in #1672 (milestone 3.2.0). That is a reasonable deferral for a non-blocking point, but the code does not answer it yet. - Allowance journeys still describe a payee accept step (journeys/allowances/monthly-cap-reached.xml:11).
monthly-cap-reached.xmlstep 2 still says "and have the payee accept it". The payee no longer does anything, so a runner may look for a review sheet that never opens. Inpin-gates-allowance-changes.xml, the step starting "On the payee instance" ends with "and the payer's row turns Active", which mixes the two instances. Suggest "wait for the payee's wallet to accept it" and naming which instance's row is checked. Monthly-cap-reached.xml:11 still ends with "and have the payee accept it". The pin-gates-allowance-changes.xml step "On the payee instance, verify 'Allowance Added' arrives..." still ends by checking "the payer's row turns Active". Both are listed in #1672 for later; the journeys themselves are unchanged. - Test for an ask joining an offer's entry cannot fail (app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt).
offer accept never answers an ask that joined the entry after the checkadds the ask under a different counterparty (asker). Entries are keyed by counterparty and allowance id, so the ask lands in its own entry and the assertion holds even with the old lookup by entry id. Groups only ever hold the single allowance id this wallet proposed (PaykitAllowanceRepo.kt:266), so a received ask cannot join an offer's entry. The scenario in the test name and in the comment atPaykitAllowanceRepo.kt:305is unreachable. Consider dropping or renaming the test and the comment.offer accept never answers an ask that joined the entry after the check(PaykitAllowanceRepoTest.kt:425) still adds the ask underasker, a different counterparty, so it lands in its own entry and the assertion cannot fail. The comment at PaykitAllowanceRepo.kt:305 is also unchanged. The rename and comment removal are deferred to #1672. - Backgrounding can permanently suppress the allowance notification (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer accepted while the app backgrounds can become active without notifying the allowee.
stopPaykitPaymentRequestPolling()cancels the poll, whilecompleteSdkCall()lets acceptance commit before rethrowing cancellation. The announcement is then skipped. Later refreshes exclude the accepted offer, so the notification is permanently lost. A focused reproduction using the copied acceptance methods confirmed one committed acceptance and zero announcements after refresh. This is the same path as point 1 and is unchanged at PaykitAllowanceRepo.kt:303-310. An accept that commits while the polling job is cancelled still skipsaccepted += offerand the announcement. Later refreshes filter it out withisAnswerable, so the notice is never shown. It is tracked in #1672, not fixed. - Journal pruning erases allowance totals and payment history labels (app/src/main/java/to/bitkit/repositories/PaykitAllowanceExecutor.kt:840). Allowance totals decrease when settled payments leave the 200-entry journal.
PaykitAllowanceRepo.publish()derives both “Paid so far” and the “Auto-paid” labels solely from this journal, which also holds manual attempts. One automatic payment followed by 200 manual request payments therefore erases that allowance's displayed total and the older payment's label. A focused reproduction using the copied journal function confirmed the total dropping from 200 sats to zero.journal()at PaykitAllowanceExecutor.kt:837-841 still keeps onlytakeLast(MAX_JOURNAL_ENTRIES)(200, line 151). Manual attempts still share that list.publish()(PaykitAllowanceRepo.kt:559-575) still derives "Paid so far" and the "Auto-paid" ids only from it. This comes from the stacked base and is tracked in #1584 (milestone 3.2.0); the code is unchanged.
The merge-base changed after approval.
|
@coreyphillips the master merge after #1340 landed dismissed your approval; the feature code is unchanged since your recheck at e45f4ec. Could you re-approve? |
coreyphillips
left a comment
There was a problem hiding this comment.
Rechecked after 17 new commits (0d06387, 493b7de, 85ff0d1, and 14 more). Nothing blocking remains.
still open
- Offer accepted as the polling job is cancelled is never announced (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer whose SDK accept completes while backgrounding cancels the polling job becomes active but is never announced. On
ON_STOP,ContentView.kt:323-324callsstopPaykitPaymentRequestPolling(). That cancelspaykitPaymentRequestPollingJob(AppViewModel.kt:1458), the coroutine running the 10 srefreshIncomingPaykitPaymentRequestsand soacceptOffersFromAllowers.PaykitSdkService.completeSdkCall(PaykitSdkService.kt:1687-1693) runsacceptAllowanceunderNonCancellableand then callsensureActive(). So the SDK records the acceptance and aCancellationExceptionis thrown.runSuspendCatchinginrespondandsendQueuedMessagesrethrows it,accepted += offernever runs, andannounceAcceptedAllowanceOffersis skipped. On the next refresh the record is ACCEPTED, so it is not answerable and is never announced. This contradicts the PR's claim that it "still announces what it accepted". ThecanContinueflag only helps callers outside the polling job, andoffer accepted as the app backgrounds is still announced...tests the flag, not cancellation. Process death right after the accept ends the same way. Impact is small: the allowance is active and listed, and only the "Allowance Added" notice is lost. Confirmed by reading the code paths above; I could not run the unit tests locally (no GitHub Packages access for Paykit). Possible fixes: persist accepted-but-unannounced keys inPaykitAllowanceLocalStateand announce from there, or run each accept and its bookkeeping inwithContext(NonCancellable). The code is unchanged:completeSdkCall(PaykitSdkService.kt:1687-1692) still callsensureActive()after the NonCancellable accept, andstopPaykitPaymentRequestPolling(AppViewModel.kt:1459) still cancels the job, soaccepted += offerat PaykitAllowanceRepo.kt:306 is skipped. The description now admits the gap and #1672 (3.2.0) tracks it, so it need not hold the merge, but a deferral does not close it. - Allowance journeys still describe a payee accept step (journeys/allowances/monthly-cap-reached.xml:11).
monthly-cap-reached.xmlstep 2 still says "and have the payee accept it". The payee no longer does anything, so a runner may look for a review sheet that never opens. Inpin-gates-allowance-changes.xml, the step starting "On the payee instance" ends with "and the payer's row turns Active", which mixes the two instances. Suggest "wait for the payee's wallet to accept it" and naming which instance's row is checked. Monthly-cap-reached.xml:11 still ends with "and have the payee accept it". In pin-gates-allowance-changes.xml, the "On the payee instance" step still ends with "and the payer's row turns Active". The description defers both to #1672, but the wording has not changed. - Test for an ask joining an offer's entry cannot fail (app/src/test/java/to/bitkit/repositories/PaykitAllowanceRepoTest.kt).
offer accept never answers an ask that joined the entry after the checkadds the ask under a different counterparty (asker). Entries are keyed by counterparty and allowance id, so the ask lands in its own entry and the assertion holds even with the old lookup by entry id. Groups only ever hold the single allowance id this wallet proposed (PaykitAllowanceRepo.kt:266), so a received ask cannot join an offer's entry. The scenario in the test name and in the comment atPaykitAllowanceRepo.kt:305is unreachable. Consider dropping or renaming the test and the comment.offer accept never answers an ask that joined the entry after the check(PaykitAllowanceRepoTest.kt:425) is unchanged, and so is the comment at PaykitAllowanceRepo.kt:305. Both still describe a case that cannot happen. The description defers this to #1672. - Backgrounding can permanently suppress the allowance notification (app/src/main/java/to/bitkit/repositories/PaykitAllowanceRepo.kt:306). An offer accepted while the app backgrounds can become active without notifying the allowee.
stopPaykitPaymentRequestPolling()cancels the poll, whilecompleteSdkCall()lets acceptance commit before rethrowing cancellation. The announcement is then skipped. Later refreshes exclude the accepted offer, so the notification is permanently lost. A focused reproduction using the copied acceptance methods confirmed one committed acceptance and zero announcements after refresh. This is the same path as point 1, and nothing in PaykitAllowanceRepo.kt, AppViewModel.kt or PaykitSdkService.kt changed since e45f4ec (I checkedgit diff e45f4ec..HEADon those files). An accept that the poll cancellation cuts off is still never announced. It is tracked in #1672. - Journal pruning erases allowance totals and payment history labels (app/src/main/java/to/bitkit/repositories/PaykitAllowanceExecutor.kt:840). Allowance totals decrease when settled payments leave the 200-entry journal.
PaykitAllowanceRepo.publish()derives both “Paid so far” and the “Auto-paid” labels solely from this journal, which also holds manual attempts. One automatic payment followed by 200 manual request payments therefore erases that allowance's displayed total and the older payment's label. A focused reproduction using the copied journal function confirmed the total dropping from 200 sats to zero. Codex still sees it: PaykitAllowanceExecutor.kt:840 still prunes the shared journal to 200 entries. PaykitAllowanceRepo.kt:559 still derives totals and payment labels solely from its remaining successful automatic entries.
|
QA: 🟢 Tests 1, 2Test 1 The payer sets a $5 a payment, $50 a month allowance; the allowee's wallet accepts it by itself. "Allowance Added" ("AlwPayer set up an allowance for you") posted on Android 24 s after Save, with no review sheet and no tap. The payer's row read "Active · $5 a payment" 41 s after Save; the allowee's row reads the same.
Test 2 The allowee sends a one-time $2 Payment Request ("Devlog"); the payer pays it automatically over Lightning. The payer's spending dropped by 2,454 sats (2,452 plus a 2-sat fee) within 56 s of the request, its Activity lists "Sent to AlwPayee $2.00", and the allowee showed "Received Instant Bitcoin ₿2,452" 72 s after sending.
Note The recordings are choppy (7 to 20 fps of motion) because the shared test host was under load (load 16 to 43 on 10 cores) during the run; the results above are read from the devices' state and logs. |
Closes #1353
Twin: synonymdev/bitkit-ios#841
Refs: #1351
This PR makes the allowee's wallet accept an allowance the allower offers, so the allowee taps nothing and the allowance is active as soon as the offer arrives.
Description
Out of Scope
Design
N/A — no design available.
Preview
ba1353-set-and-accept-1b61baa-2x.mp4
QA Notes
Journeys
set-and-accept.xml— the allowee's wallet accepts the offer with no review sheet, posts "Allowance Added", and both rows turn Active1b61baa, Paykit0.1.0, recorded side by side above (payer left, allowee right, 2x): no review sheet opened, "Allowance Added" posted once, the payer's row read "Waiting for an answer" and then "Active · $5 a payment", and the allowee's row read "Active · $5 a payment". The proposal went out 11 s after Save and the allowee's wallet accepted it and posted "Allowance Added" 17 s later (28 s from Save). The later headba1c9bconly adds base changes outside the offer accept.1b61baa).pin-gates-allowance-changes.xml— with a payment PIN on the payer, the payee's wallet still accepts the offer by itself: "Allowance Added" arrives with no review sheet, no tap and no PINManual Tests
N/A
Automated Checks
PaykitAllowanceTest.kt— only a received proposal from the allower counts as an offerPaykitAllowanceRepoTest.kt— an allower's offer is accepted without the sheet and not announced twice; a failed accept is retried and announced once; an allowee's ask still goes to the sheet; sent and answered proposals are never accepted; an ask that joins an offer's entry mid-accept is never accepted; an offer accepted as the app backgrounds is still announced and no further offer is accepted; nothing is announced once the identity changed or the profile is being deletedAppViewModelSendFlowTest.kt— the send flow stubs the new acceptance call./gradlew testDevDebugUnitTest detektat8113f8e(Paykit0.1.0, merged with the updatedfeat/paykit-allowances): unit tests pass and detekt is clean