Repository navigation
reporting: slice requests don't carry the owning consumer_id #1431
Description
Activity
- addedclaude-triagingTriage routine is actively working on this issue (1-3 min)Triage routine is actively working on this issue (1-3 min)
on Oct 7, 2026 Triage
Classification: Bug (SDK gap — consumer_id available at obligation construction time, not threaded through)
Bucket(s): reporting (no matching label exists — flagging gap)
Status: ready-for-human
Milestone: (omit — no version label or milestoned PR)What the experts said:
- code-reviewer: Add
consumer_id: str(required) toReportingSourceIdentityV1; populate fromobligation.consumer_idinproducer.py:1728; update two direct-construction sites:src/adcp/reporting/fixtures.py:238andtests/test_reporting_source_contract.py:1006. Defer fingerprint change to avoid beta-phase churn — the field addition alone unblocks adopters. - ad-tech-protocol-expert:
delivery_config_idis scoped to(account_id, consumer_id)per the 9.0 ledger model, so the current_logical_slice_fingerprintcan collide across consumers today; 9.0.0b1 is the right window to fix it.ReportingSourceIdentityV1is SDK-internal (not in published ADCP schema), so no spec PR needed.
My take: Field addition and producer wiring are clear and unambiguous. Experts disagree on the fingerprint: the protocol expert flags a correctness collision risk that should land in beta; the code reviewer recommends deferral. The collision's practical impact depends on one fact you can confirm.
One open question for @bokelley: Are media buy IDs globally unique (UUIDs or similar), or account-scoped integers? If globally unique, the fingerprint collision is impossible in practice and the field can ship as additive-only; if account-scoped, the fingerprint change is required for correctness and 9.0.0b1 is the right window.
Triaged by Claude Code. Session: https://claude.ai/code/session_01Uz33hyBHDbRvRmVwdh3txp
Generated by Claude Code
- code-reviewer: Add
- addedbugSomething isn't workingSomething isn't workingand removedclaude-triagingTriage routine is actively working on this issue (1-3 min)Triage routine is actively working on this issue (1-3 min)
on Oct 7, 2026 Triage
Classification: Feature request
Bucket(s): client (reporting subsystem — noreportinglabel exists in repo, gap noted)
Status: ready-for-humanWhat the experts said:
- ad-tech-protocol-expert:
consumer_idbelongs inReportingSourceIdentityV1(not the outer request type — identity is sealed into manifests, so authorization facts belong there); non-breaking for production paths; do not add tological_slice_fingerprintbecausedelivery_config_idis consumer-scoped at provisioning so the fingerprint is already implicitly consumer-distinct, and adding it would silently invalidate stored fingerprint values; manifest-binding validation atproducer.py:1596must also checkconsumer_idor the authorization guarantee is incomplete. - code-reviewer: add as
consumer_id: ExternalId | None = None(not required, to avoid breaking re-parsing of already-stored canonical manifests); do add to fingerprint — without it, two consumers who share the same obligation dimensions collapse into one dedup slot, silently suppressing re-dispatch for the distinct buyer; also needsfixtures.py:238,conformance.py:611(_sequence_scope),source.py:1716(publication_content_fingerprint_v1).
My take: Both experts agree
consumer_idbelongs inReportingSourceIdentityV1, set fromobligation.consumer_idinproducer.py:1718. The open question is_logical_slice_fingerprint(producer.py:1765): ifdelivery_config_idis guaranteed unique per consumer at provisioning, the fingerprint is already consumer-distinct and addingconsumer_idthere would only invalidate stored values with no benefit. If a delivery config can serve more than one consumer,consumer_idmust be in the fingerprint.Question for @bokelley: Is
delivery_config_idguaranteed unique perconsumer_idin your provisioning model? That settles the fingerprint question.Implementation scope (agreed items):
src/adcp/reporting/source.py— addconsumer_id: ExternalId | None = Noneafteraccount_idinReportingSourceIdentityV1src/adcp/reporting/ledger/producer.py:1718— passconsumer_id=obligation.consumer_idsrc/adcp/reporting/ledger/producer.py:1596— addconsumer_idto manifest-binding validationsrc/adcp/reporting/fixtures.py:238— addconsumer_id="consumer-redacted"src/adcp/reporting/conformance.py:611— addconsumer_idto_sequence_scopesrc/adcp/reporting/source.py:1716— addconsumer_idtopublication_content_fingerprint_v1- Pending: fingerprint at
producer.py:1765— add"consumer_id": obligation.consumer_idonly if delivery configs are not per-consumer unique
Triaged by Claude Code. Session: https://claude.ai/code/session_012zvn8hi7cnExQXYxkP5k5x
Generated by Claude Code
- ad-tech-protocol-expert:
- addedenhancementNew feature or requestNew feature or requestand removedbugSomething isn't workingSomething isn't working
on Oct 7, 2026
Summary
9.0 adds caller ownership to the reporting ledger. Every configuration generation, obligation and status row is keyed by
(account_id, consumer_id, …). The request a reporting source receives does not name that consumer.ReportingSourceSliceRequestV1.identity(ReportingSourceIdentityV1) carriesaccount_id,delivery_config_id,delivery_config_version,reporting_obligation_idandsource_scope, but noconsumer_id.Why it matters
Some sellers authorize delivery reads per caller: a buyer principal may only read its own media buys. Such a seller needs to know whose obligation it is fetching for. There are two ways to get that today:
source_scope. The account context resolver receives theReportingConfiguration, which hasconsumer_id. This works, butsource_scopeis documented as the opaque place a source pins itself (provider, network, credential binding). The caller is an authorization fact, not a pin. Every adopter has to remember to add it. If one forgets, its source fetches with no caller, or with the wrong one, and nothing in the SDK notices.account_id(for exampletenant:account:principal). This is what we did before 9.0. It duplicates what caller ownership now does, and then responses have to be rewritten so buyers see their own account id.A third option is to look the obligation up by
reporting_obligation_idthrough the store. That costs an extra read per slice and depends on store internals.Ask
Add
consumer_idtoReportingSourceIdentityV1(or toReportingSourceSliceRequestV1), set from the obligation the slice is for. If it belongs in the logical slice fingerprint, fingerprint it there too.Context
Hit while moving the Embedded Sales Agent (scope3data/embedded-sales-agent) to 9.0.0b1. It keeps option 2 for now (
core/reporting.py,ledger_account_id). It would drop the fold once the slice request names the consumer.