Repository navigation
fix(reporting): compare set-valued subscription arrays without order on readback - #1764
Conversation
…on readback The buyer facade's readback check compared every array element for element, but account_ids, exclude_account_ids, operator_domains and selector dimensions are uniqueItems sets. A conformant seller returning them in another order, such as sorted, tripped a terminal SUBSCRIPTION_READBACK_MISMATCH on a write that succeeded. Those arrays now compare as sets, and destination_overrides, whose entries select disjoint accounts, compare as an unordered collection. Any other array still compares element for element. Addresses the Ladon finding on adcp-client-python#1737. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean, correct fix with no blocking or medium findings.
This PR fixes differing_path in the reporting subscription facade so that the four uniqueItems subscription arrays (account_ids, exclude_account_ids, operator_domains, dimensions) are compared as sets and destination_overrides as an unordered membership match. This resolves a spurious terminal SUBSCRIPTION_READBACK_MISMATCH triggered by a conformant seller that reorders these arrays. The buyer check now agrees with registry._semantic (registry.py:86–110), which already canonicalizes these arrays.
Checked:
- No public-surface or wire-shape change; no semver signal required;
fix:prefix is correct. - No forward-compat / discriminated-union, import-layering, generated-code, or credential concerns (changes limited to the facade + tests).
- Tests cover reordered sets passing, changed members failing, reordered vs. altered
destination_overrides, and other arrays staying order-sensitive. The only theoretical edge (greedy override matching) does not bite given disjoint, destination_id-keyed entries.
Gates: high_risk false, gated_paths false, no author team gate. No critical/high/medium findings. Falls through to row 9 → approve.
The producer row-upload test stamps its worker lease with the settling harness clock, fixed at 2026-11-01T02:10Z, while the row store checks the lease against the database's clock_timestamp. Once the wall clock passed that instant plus the lease, every run failed with STATE_UNAVAILABLE: no live worker lease covers this row upload, on next and on every pull request. The test now runs its turn at the wall clock, so the lease lies in the real future. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit ae15bab)
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — test-only delta since prior approve, no blocking or medium findings.
This subsequent review covers a single test-only change in tests/conformance/reporting/test_reporting_row_storage_object_pg.py: it captures the harness clock and sets clock[0] = datetime.now(timezone.utc) before run_worker(). This is a correct fix for a time-dependency — the previously fixed 2026-11-01 02:10 UTC instant would go stale as real time passes, and the PG row-upload path compares the worker lease against the database's clock_timestamp(), so a past-stamped lease would read as expired. The clock is a mutable list read lazily by both store and producer, so the mutation propagates correctly.
No production surface touched, no public-API/wire-shape changes, no new findings. Prior run had no blockers and none re-surface.
Decision table: no critical/high findings (row 1 no), gated_paths false (row 2 no), high_risk false (rows 3/5 no), no medium findings (rows 4/8 no), prior decision was approve (row 6 no), no author team gate (row 7 no). Falls through to row 9 → approve.
Loose end from the reporting-subscriptions DX plan: the open Ladon finding on #1737.
Problem
client.reporting.subscribe()verifies the seller's readback withdiffering_path, which compared every array element for element. The subscription'sscope.account_ids,exclude_account_ids,operator_domainsandselector.dimensionsareuniqueItemssets. A conformant seller that returns them in another order, such as sorted, tripped a terminalSUBSCRIPTION_READBACK_MISMATCH("The seller altered a submitted field; contact the seller") on a write that succeeded.Fix
destination_overridesentries select disjoint accounts, so their order carries no meaning either. Each sent entry now matches any stored entry.The registry's own versioning (
registry._semantic) already treats these arrays as sets, so the buyer check now agrees with it.Tests
subscribe(). Before the fix this test fails.differing_path: reordered sets pass, changed members fail, reordereddestination_overridespass, a changed override names its index, and other arrays stay ordered.pytest tests/test_reporting_subscription_facade.py: 15 passed. ruff and mypy are clean.🤖 Generated with Claude Code