Skip to content

feat(reporting): prototype reliable ledger reconciliation - #1095

Draft
bokelley wants to merge 6 commits into
mainfrom
feat/reporting-reconciliation
Draft

feat(reporting): prototype reliable ledger reconciliation#1095
bokelley wants to merge 6 commits into
mainfrom
feat/reporting-reconciliation

Conversation

@bokelley

@bokelley bokelley commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Prototype the reliable reporting reconciliation loop in the Python SDK against the current protocol work:

  • add typed client methods for get_reporting_status and sync_reporting_receipts over MCP and A2A
  • add an SDK-managed reconciler that exhausts a stable ledger snapshot, restarts on snapshot drift, deduplicates immutable records, validates history counts, selects the current revision, verifies finality and producer evidence, inspects the destination, submits an authenticated receipt, and confirms receipt readback
  • require an independent expected-period denominator keyed by configuration generation, report definition, feed, profile, exact campaign set, and period before returning a definitive result
  • deduplicate totals by canonical revision so one revision delivered to several destinations is counted once
  • expose the reporting wire types through the supported adcp.types surface
  • add nine end-to-end reconciliation tests

Why this is a draft

This intentionally validates two pending protocol changes together:

The Python SDK is currently pinned to AdCP 3.2.0-beta.6. Generating from the current combined schema exposed substantial unrelated beta.8 type churn, so this draft does not change the runtime schema pin or commit a new schema cache. It should be rebased/regenerated from the released protocol bundle before merge.

The proof-of-concept generation also refreshes the SDK's generated-contract expectations for the current schema: collision inventory, canonical error-code inventory, and the split between the legacy versioned reporting webhook and the new delivery-ready webhook. The released beta.6 schema cache remains unchanged.

What the prototype proved

A buyer can only call a period definitive when the seller returns a closed, retained denominator and the SDK independently finds every expected period, complete associated history, one current revision at the requested finality, matching materialization evidence, and an accepted consumer receipt when required.

The seller learns that a buyer or governance consumer agrees by reading the authenticated receipt back from the same ledger. Availability alone does not prove consumer agreement or downstream ingestion.

Validation

  • ruff check on the reporting implementation, public type exports, and tests
  • mypy --strict src/adcp/reporting.py
  • 22 reporting and affected generated-contract tests
  • 236 broader client/protocol/public-surface tests passed, 1 expected xfail

Comment thread src/adcp/reporting.py
class ReportingReconciliationClient(Protocol):
async def get_reporting_status(
self, request: GetReportingStatusRequest
) -> TaskResult[GetReportingStatusResponse]: ...
Comment thread src/adcp/reporting.py

async def sync_reporting_receipts(
self, request: SyncReportingReceiptsRequest
) -> TaskResult[SyncReportingReceiptsResponse]: ...
Comment thread src/adcp/reporting.py


class ReportingCheckpointStore(Protocol):
async def get(self, reporting_materialization_id: str) -> ReportingReceipt | None: ...
Comment thread src/adcp/reporting.py
class ReportingCheckpointStore(Protocol):
async def get(self, reporting_materialization_id: str) -> ReportingReceipt | None: ...

async def put(self, receipt: ReportingReceipt) -> None: ...
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