Skip to content

feat(reporting): apply the AdCP 3.3 reporting rules - #1707

Merged
bokelley merged 1 commit into
nextfrom
claude/issue-1669-reporting-rules
Oct 29, 2026
Merged

bokelley merged 1 commit into
nextfrom
claude/issue-1669-reporting-rules

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Refs #1669

The SDK does not produce get_media_buy_delivery itself. I checked each AdCP 3.3 reporting rule against the SDK code that reads, derives, injects, validates or stores delivery and reporting data, and fixed only the real gaps.

Rule What the SDK owns Change
impressions optional on delivery rows Generated row types (already optional). InlineReportingSource projects delivery into rows and control totals. No code change was needed. _metrics already omits absent metrics, and _control_totals skips a metric that some rows lack. New tests pin both. No SDK reader derives cpm, ctr, completion_rate or viewable_rate.
Multi-qualifier metrics omit flat totals Nothing. No SDK code builds buy, window or webhook totals. Docs only.
Receivers ignore aggregated_totals in delivery webhooks WebhookReceiver, ADCPClient.handle_webhook Both already accept the field (the webhook result is open) and never read it. New tests pin this.
Property and installment-property rollups comply_test_controller dispatch of simulate_delivery The dispatcher now rejects property_delivery, installment_delivery and installment_property_delivery with INVALID_PARAMS when they come with delivery_date (the arrays are undated). It also rejects them when the store override does not declare the parameter. Before, it dropped those rows and reported success. Docstring updated for injected rows and optional impressions.
source.custody on report definitions ReportingRevisionVerifier, which hashes and parses the installed definition bytes New ReportingRevisionVerifier.custody property. A custody value outside the enum fails construction with UNSUPPORTED_VERIFICATION. The digest already covered the claim because raw bytes are hashed. The reference definition asset is unchanged, so its digest does not move.
Superseded snapshot compaction Ledger retention and exact reads Docs only. Compaction is not permitted under 3.3, and keeping the full chain stays conformant. The plan is in docs/reporting-release-notes.md.

No adcp-reporting-storage change was needed. Its row codecs are opaque JCS rows, and its definition column stores the URI and digest binding, not the definition document.

Tests

  • tests/test_reporting_33_rules.py (new, 22 tests)
  • make lint, make typecheck-all: pass
  • pytest tests -n 8 -k "reporting or test_controller or webhook": 4942 passed, 4 failed. All 4 are subprocess import tests (test_lazy_types x2, test_reporting_staging_reuse, test_reporting_production_public). They pass when re-run on their own, so they appear to be load flakes. tests/test_server_dx.py and tests/test_account_mode_gate.py: 141 passed.

🤖 Generated with Claude Code

Surface a report definition's source.custody claim on
ReportingRevisionVerifier and fail closed on a value outside the enum;
the claim stays covered by report_definition_sha256 because the SDK
hashes the installed bytes.

The test-controller dispatcher now rejects injected property_delivery,
installment_delivery and installment_property_delivery rows sent with
delivery_date, and rejects them when the store override cannot accept
them instead of dropping rows the seller must report verbatim.

Pin that rows without impressions are never zero-filled or totalled by
the inline source, and that delivery webhooks carrying
aggregated_totals are accepted. Document each 3.3 rule and the
snapshot compaction plan in the reporting release notes.

Refs #1669

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 — AdCP 3.3 reporting rules applied cleanly across three surfaces with no blocking or medium findings.

What I checked:

  • high_risk: true is driven solely by src/adcp/server/test_controller.py (modified) matching src/adcp/server/**. Per the risk model, a modification with no medium-or-higher finding is presumed safe. The reviewer confirmed simulate_delivery dispatch correctly reads the bound override's real signature via getattr(store, scenario) + inspect.signature — _LegacyStore is refused, **kwargs store passes (test_controller.py:1413-1420) — and refuses undated injected rows rather than silently dropping and reporting success. That satisfies the DR-0032/DR-0033 pathway rule (Refused path, with a test).
  • ReportingRevisionVerifier.custody fail-closes on malformed claims via _runtime() in __post_init__ (verification.py:96, 127-135), covered by the definition digest.
  • Semver signal feat(reporting): is correct: additive property, no public export removed/renamed/retyped, no required↔optional flip.
  • New 488-line test file pins real behavior (control-total omission at tests/test_reporting_33_rules.py:351; digest coverage at :522). The 4 disclosed test failures are documented subprocess-import load flakes on an unchanged path, not validation gaps.

Gates: gated_paths false; no no-auto-approve team match; no prior decision. Decision table rows 1–8 do not fire (no critical/high, no medium, high_risk modification with zero findings) → row 9 approve.

@bokelley
bokelley merged commit 3e177e6 into next Oct 29, 2026
41 checks passed
@bokelley
bokelley deleted the claude/issue-1669-reporting-rules branch October 29, 2026 22:05
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