Skip to content

fix: reporting re-grant chain, adjustment offers and rejected receipts match TS - #1806

Merged
bokelley merged 1 commit into
nextfrom
claude/reporting-ts-parity-fixes
Nov 2, 2026
Merged

bokelley merged 1 commit into
nextfrom
claude/reporting-ts-parity-fixes

Conversation

@bokelley

@bokelley bokelley commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

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 next before applying the fix.

#1772: the re-grant chain always read new.

  • While authority is lost, the account is missing from list_accounts. The snapshot acknowledged during the loss therefore had no authorizations entry for it, and account.regranted compared against nothing.
  • I reproduced it against the managed seller: a re-grant within retention kept authorized_at = 2026-10-01T00:00:00Z and still emitted chain: "new".
  • poll() now carries the previous snapshot's authorization forward for accounts that are in unauthorized and not listed now. This is TS's rememberAuthorizations.
  • The snapshot is the durable source, so the fix also holds for serverless clients that start fresh on each poll.

#1773: adjustments were never offered.

  • apply_receipt_policy reached 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.
  • Pending adjustments are now offered before that check, as in TS. An adjustment is pending when it has no adjustment_receipts entry.

#1773: rejected_by_buyer.

  • Only an accepted receipt clears MISSING_MATCHING_CONSUMER_RECEIPT. A rejected billing revision therefore kept reading receipt: owed_by_buyer / responsible_party: buyer forever, even though the policy never resends a rejection.
  • Such a period now reads receipt: {"status": "rejected_by_buyer"} with responsible_party: "seller", which corrects and re-delivers. This is TS's receiptStateOf, 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 receipts module docstring.

Test plan

  • test_losing_and_regaining_authority_are_events now asserts chain: "continued". The new test test_a_fresh_client_reads_the_chain_from_the_saved_snapshot uses a new client per poll over one store. Both fail on next.
  • 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 on next.
  • test_a_rejected_revision_is_the_sellers_to_correct_not_owed_by_the_buyer: the receipt state goes owed_by_buyer, then rejected_by_buyer, and is None once accepted.
  • The reconcile, events, receipts, TS-parity and test-kit suites pass. ruff and mypy pass on adcp.reporting.subscriptions.

🤖 Generated with Claude Code

…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>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 reads new outside retention.
  • Adjustment reorder offers pending adjustments on already-receipted revisions, filtered by an answered set.
  • _Obligation.receipt state machine routes rejected revisions to responsible_party: seller while keeping accepted → None.
  • receipt_owed → receipt rename is private with both call sites updated; rejected_by_buyer is 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.

@bokelley
bokelley merged commit 7d36906 into next Nov 2, 2026
41 checks passed
@bokelley
bokelley deleted the claude/reporting-ts-parity-fixes branch November 2, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant