Repository navigation
fix: reporting re-grant chain, adjustment offers and rejected receipts match TS - #1806
Merged
Merged
Conversation
…s match TS Three parity notes from the TS ports of the buyer reporting slices: - events: an unauthorized account is missing from list_accounts, so the snapshot taken during the loss had no authorization for it and a re-grant always read chain new. poll now keeps the previous authorization of an account that is unauthorized now, so a re-grant within retention reads continued, also across fresh clients. - receipts: pending adjustments, those without an adjustment receipt, are offered before the already-receipted check. An adjustment corrects a revision after it was receipted, so the hook never saw one before. - reconcile: a delivered billing period whose current revision the buyer rejected reads receipt rejected_by_buyer with responsible_party seller, instead of owed_by_buyer forever. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean three-bug parity fix against the TS buyer-reporting ports, no blocking or Medium findings.
Checked:
- Re-grant chain fix (
_remember_authorizations) correctly carries prior authorization forward for currently-unauthorized accounts and still readsnewoutside retention. - Adjustment reorder offers pending adjustments on already-receipted revisions, filtered by an
answeredset. _Obligation.receiptstate machine routes rejected revisions toresponsible_party: sellerwhile keeping accepted → None.receipt_owed→receiptrename is private with both call sites updated;rejected_by_buyeris an internal dict value (no published-enum/wire-pathway concern).- Each fix ships a reproducing test.
No high-risk flag, no gated paths, no author-team gate, no prior decision. Zero findings → decision table falls through to row 9.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes the three real Python bugs that the TS agent's parity notes on #1772 and #1773 pointed to (decision 4). I reproduced each one with a test that fails on
nextbefore applying the fix.#1772: the re-grant
chainalways readnew.list_accounts. The snapshot acknowledged during the loss therefore had noauthorizationsentry for it, andaccount.regrantedcompared against nothing.authorized_at = 2026-10-01T00:00:00Zand still emittedchain: "new".poll()now carries the previous snapshot's authorization forward for accounts that are inunauthorizedand not listed now. This is TS'srememberAuthorizations.#1773: adjustments were never offered.
apply_receipt_policyreached the adjustments loop only after the "already receipted"continue. An adjustment corrects a revision after the buyer has receipted it, so the hook never saw one.adjustment_receiptsentry.#1773:
rejected_by_buyer.MISSING_MATCHING_CONSUMER_RECEIPT. A rejected billing revision therefore kept readingreceipt: owed_by_buyer/responsible_party: buyerforever, even though the policy never resends a rejection.receipt: {"status": "rejected_by_buyer"}withresponsible_party: "seller", which corrects and re-delivers. This is TS'sreceiptStateOf, keyed on the obligation and the current revision.The other #1772/#1773 notes needed no change: event ids and receipt ids are already equal across SDKs.
Docs: the buyer guide's status table and receipts rules, and the
receiptsmodule docstring.Test plan
test_losing_and_regaining_authority_are_eventsnow assertschain: "continued". The new testtest_a_fresh_client_reads_the_chain_from_the_saved_snapshotuses a new client per poll over one store. Both fail onnext.test_pending_adjustments_are_offered_on_an_already_receipted_revision: only the unanswered adjustment is offered and deferred, and nothing is re-receipted. It fails onnext.test_a_rejected_revision_is_the_sellers_to_correct_not_owed_by_the_buyer: the receipt state goesowed_by_buyer, thenrejected_by_buyer, and isNoneonce accepted.ruffandmypypass onadcp.reporting.subscriptions.🤖 Generated with Claude Code