healthy while the buyer’s
webhook, exact revision read, or destination access is failing.
3.2.0-rc.2 closes that loop additively with opt-in
sync_reporting_status,
and 3.2.0-rc.3 hardens it. A seller advertises consumer_status_task only
when the task and readback are implemented. The requirement itself lands only in the next eligible minor
after the notice window. Both changes remain inside the experimental
media_buy.reporting_delivery surface.
Capability change
The blocks below are excerpts ofmedia_buy.reporting_delivery showing only the
fields that change; a real declaration also carries offerings[],
automated_recovery_window_seconds, and status_retention_days.
Before (3.2.0-rc.1):
3.2.0-rc.3), and required form in the next eligible
minor after the notice window:
operations_contact is inert human-facing metadata. Agents surface it to an
operator; they never dereference the URL, send protocol traffic to it, or treat
either value as a credential or as evidence of agent identity.
The new field is optional during the notice window. When present, it commits the
seller to the complete task and readback contract below. The next eligible minor
makes the field required for Reliable Reporting Core. It does not add a buyer
endpoint or webhook: buyers call the seller-hosted task using the same
authenticated transport as other AdCP tasks.
Seller migration
- Accept batched immutable consumer status statements with ordinary AdCP idempotency, atomic current-leaf supersession, bounded per-caller resources, and caller/account isolation.
- Validate a missing obligation from the accepted configuration generation, report definition, and period; do not require a seller-issued obligation ID.
- Retain superseded status history and include it in
get_reporting_status(view: "periods"), pagination totals, andchanges_aftercheckpoints for at leaststatus_retention_days. Verify any supplied seller snapshot ID and timestamp against the scoped snapshot you actually issued. - Compare the current buyer statement with the current seller projection. A
conflict creates a caller-scoped
CONSUMER_STATUS_MISMATCH; it never rewrites either party’s evidence or contaminates another caller. Carryopened_aton that issue and keep it fixed across re-emissions and severity changes. - Apply the stale-
receivedgrace window. Areceivedstatement made stale only by your own restatement isdelayeduntil thecreated_atof the first revision that superseded the one the buyer named, plus the configuration generation’sschedule.delivery_sla, as exact elapsed time in UTC — orautomated_recovery_window_secondswhen that SLA resolves to a zero duration. It isaction_requiredafter. Later restatements do not restart the window, and an advertisedconsumer_mismatch_escalation_secondsboundary takes precedence when the two overlap. Every other conflict —obligation_missing,revision_missing,unreadable,content_mismatch— isaction_requiredimmediately. - Report
obligation_counts.consumer_status_pendingon the summary view: obligations pastexpected_at + automated_recovery_window_secondswith no current status from this caller. Count it and stop. Silence never changes health, another count, or yourreliability_statistics. - Accept
content_mismatchwith its closedmismatch_code. It is a contract-fact disagreement about the accepted configuration generation, not a measurement dispute; do not route it into billing variance handling. - Optionally advertise
consumer_mismatch_escalation_secondswith anoperations_contact. If you do, anopenoracknowledgedissue still unresolved atopened_atplus that window MUST beaction_requiredwith acontact_*recommended_action. - Treat
resolvedandwaivedas distinct terminal dispositions.resolvedmeans the consumer superseded the causing statement or your own projection stopped conflicting.waivedrequires an explicit off-protocol agreement with the authenticated consumer covering the exact caller/account,issue_id, causingreporting_status_id, and diagnosed conflict; never create it unilaterally. Retire that issue, restore the underlying summary and period health while retaining the immutable consumer statement, and emit an offeredreporting.status_changedrecovery transition when health changes. Onlyopenandacknowledgedissues appear inissues[]. - Add
consumer_status_task: "sync_reporting_status"only when every item above is deployed.
Buyer migration
- Continue retaining accepted configuration generations and deriving expected periods independently.
- After bounded retries, submit
received,obligation_missing,revision_missing,unreadable, orcontent_mismatchfor the exact period; do not wait indefinitely before making failure visible. - Meet the posting deadline. Every elapsed expected period needs a current
status by
expected_at + automated_recovery_window_seconds. If you are still retrying at that point, postrevision_missingorunreadableand supersede it later — silence is counted against you inobligation_counts.consumer_status_pendingand tells the seller nothing. - Send a new immutable statement with explicit supersession whenever status changes.
- Re-read within the grace window when a seller restatement makes your
receivedstale. You have until the first superseding revision’screated_at + delivery_slabefore the mismatch escalates toaction_required; a seller that restates again does not buy you more time. - Use
content_mismatchonly for contract facts the accepted configuration generation already fixed — a frozen media buy with no rows, a promised metric absent, the wrong currency, rows outside the period. A disagreement about counts belongs to billing authority andmakegood_policy, not to this loop. - Before closing the reporting scope, ensure every elapsed expected period has a current consumer status.
received is Core access evidence only; Reconciled Billing still
uses sync_reporting_receipts
for materialization evidence, row/control totals, and canonical billing digests.
Billing authority stays separate
None of this decides whose count governs an invoice. That remainsmeasurement_terms.billing_measurement — see
Billing Authority. Current
source reserves an optional authoritative_party (seller | consumer,
default seller) on reporting-delivery-config.json so the two models can be
unified additively later. Sellers MUST reject consumer with
UNSUPPORTED_FEATURE during configuration validation; no released minor
defines the buyer-deposited revision task it anticipates. Reserving it now means
a future buyer-basis billing feed does not have to break the existing
billing-feed constraints. Tracked in
#7440.
Already emitting
CONSUMER_STATUS_MISMATCH? opened_at and
reporting_status_id are now required on that issue code. The code itself
shipped in RC.1’s reporting-status-issue.json enum, so an RC.1 seller that
already emits it must add opened_at — fixed at first emission, never advanced
on re-emission — before validating against
3.2.0-rc.3.
No other issue code is affected.Verifying the loop
Sellers advertisingconsumer_status_task run the capability-gated
reporting_consumer_status storyboard. It uses the
reporting_core_lifecycle_probe
controller fixture, which supplies every boundary the projection depends on:
restate_after_receivedrestates a revision the caller already reported asreceivedand returns thestale_received_grace_deadline, so both sides of the grace boundary are graded live rather than only in a seller’s own integration tests.advance_past_status_deadlinecrossesexpected_at + automated_recovery_window_secondswithout recording any statement, so the storyboard can check thatobligation_counts.consumer_status_pendingrises, that it clears on any posted leaf, and that neither transition moveshealthor any other count.advance_past_escalationcrosses the open issue’sopened_atplusconsumer_mismatch_escalation_secondswhile the grace window is still open, so the precedence rule is graded rather than assumed — and the returnedissue_idandissue_opened_atprove the window is anchored to the issue rather than to the poll.
mismatch_code, rejection of a
content_mismatch that omits it, and the advertised operations_contact.
Cross-principal non-disclosure still requires an integration test — see
known limitations.