Skip to content

feat(schema): audience activation method declarations (#4324) - #6549

Open
bokelley wants to merge 6 commits into
mainfrom
sync-audiences-capability-negotiation
Open

feat(schema): audience activation method declarations (#4324)#6549
bokelley wants to merge 6 commits into
mainfrom
sync-audiences-capability-negotiation

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Implements #4324 (audience activation method declarations) per the WG review, decision comments, and the #6540 alignment notes.

What this adds

  • core/audience-activation-method.json (new) — discriminated union over six patterns: sync_audiences, tmp_identity_match (per-buyer buyer_agent, per Decision 1), file_transfer (flat transport enum s3/gcs/azure_blob via $ref to cloud-storage-protocol.json, SFTP dropped per Decision 2; directions as array, absent = unspecified), dataset_query (with optional consumer_identities[] — cloud/region-qualified principals, framed as routing/cost hints), clean_room (informational; output binds through RFC: external audience sources — pass-through references on sync_audiences #6540 sources), and platform_distribution (destination_ref + optional bind_expiry_days).
  • core/product.jsonaudience_activation { methods[], preferred_method?, notes? }. Absence = undeclared, methods: [] disallowed.
  • protocol/get-adcp-capabilities-response.jsonaudience_targeting.supported_activation_methods as the seller-level union for fast-fail discovery; per-product declarations remain the source of truth.
  • core/product-filters.jsonaudience_activation_methods filter: OR across entries, AND within an entry, omitted optional fields as wildcards, directions on non-empty intersection; sellers MUST exclude undeclared products when the filter is present (reported via filter_diagnostics.excluded_by).
  • Docs — new [Audience activation] section in media-products, capability table row + example in get_adcp_capabilities, filter row in get_products, cross-reference in sync_audiences.

Editorial fix list from triage — all folded in

directions enum values defined; transport $refs the existing enum; per-branch additionalProperties: false enforces the forbidden-field table (stronger than not: {required} guards); destination_ref typed with maxLength: 256 and documented as seller-declared config ref; buyer_agent.agent_url carries format: uri mirroring trusted_match.providers[]; consumer_identities[].identity documented as vendor-specific and opaque.

Decisions encoded

  1. buyer_agent kept on tmp_identity_match (Option A), with a consistency note against trusted_match.providers[].
  2. SFTP dropped from v1 (Option B), matching the cloud-storage-protocol.json precedent.
  3. consumer_identities[] on dataset_query only (Option A) — clean-room output binds through dataset/platform_segment sources per RFC: external audience sources — pass-through references on sync_audiences #6540, so the granted identity is always the dataset_query entry's.
  4. Grantee-identified admissibility rule stated per-flow, not per-vendor; {cloud, region} qualifiers documented as routing/cost hints, not grant-reachability or compliance boundaries.

Not in this PR

The runtime leg — external source references on sync_audiences — is #6540, deferred pending this. Sibling reporting-direction RFC remains separate.

Validation

Schema build clean; schema-validation (29), doc-example validation (79), JSON-block validation (259), and oneOf-discriminator audit all pass on a base rebased to current main.

Closes #4324.

🤖 Generated with Claude Code

Products declare how buyer audience data can reach them via
audience_activation.methods; the seller-level union surfaces as
audience_targeting.supported_activation_methods, and buyers filter
discovery with filters.audience_activation_methods.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated schema paths require human/CODEOWNERS approval.

This PR modifies/adds files under static/schemas/source/**, which are under a deterministic hard approval gate (gated_paths: true), and review_decision is REVIEW_REQUIRED (not APPROVED). Per decision rule row 2, the outcome must be escalate.

The reviewer found no Medium-or-worse issues: the new audience_activation declarations are additive and optional, the new core/audience-activation-method.json oneOf is cleanly const-discriminated (audit passes), schema↔docs coherence holds across all four docs pages, the filter_diagnostics.excluded_by reference resolves, and a minor/non-breaking changeset is present. On the merits this is a clean, Normative (non-breaking) additive change — but ratification of protocol surface changes is a human act and the gate is not yet satisfied.

Escalation reasons

  • static/schemas/source/core/audience-activation-method.json (added), product-filters.json, product.json, protocol/get-adcp-capabilities-response.json (modified) — gated protocol-schema paths require human/CODEOWNERS review; review_decision is REVIEW_REQUIRED, not APPROVED.

Why human review

  • Gated paths under static/schemas/source/** are modified/added and review_decision is REVIEW_REQUIRED (not APPROVED) — human/CODEOWNERS approval required before merge (decision rule row 2).
  • Files: audience-activation-method.json (added), product-filters.json, product.json, get-adcp-capabilities-response.json (modified).
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/audience-activation-method.json (added) matches static/schemas/source/**; static/schemas/source/core/product-filters.json (modified) matches static/schemas/source/**; static/schemas/source/core/product.json (modified) matches static/schemas/source/**; static/schemas/source/protocol/get-adcp-capabilities-response.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated paths under static/schemas/source/** require human/CODEOWNERS approval.

This PR adds/modifies protocol source-of-truth schema files under static/schemas/source/**, which are under a hard, non-overridable approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so decision rule row 2 applies and the PR cannot be auto-approved regardless of how clean the diff is.

The reviewer found no critical/high/medium findings. The delta since the prior pass is a single editorial change in docs/media-buy/task-reference/get_products.mdx (linking the sync_audiences enum value to its task-reference page). Link target exists; no schema or wire-shape change. Schema↔docs coherence, changeset scope, oneOf discriminator, and artifact-immutability checks are all clean.

The diff is mechanically clean, but the schema-source gate is satisfied only by a recorded human/CODEOWNERS approval, which is not yet present.

Escalation reasons

  • Files under static/schemas/source/** are added/modified (audience-activation-method.json added; product-filters.json, product.json, get-adcp-capabilities-response.json modified) and review_decision is REVIEW_REQUIRED, not APPROVED. Human/CODEOWNERS approval is required before merge.

Why human review

  • Gated paths under static/schemas/source/** are added/modified (audience-activation-method.json added; product-filters.json, product.json, get-adcp-capabilities-response.json modified) and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required before merge (decision rule row 2).
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/audience-activation-method.json (added) matches static/schemas/source/**; static/schemas/source/core/product-filters.json (modified) matches static/schemas/source/**; static/schemas/source/core/product.json (modified) matches static/schemas/source/**; static/schemas/source/protocol/get-adcp-capabilities-response.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@bokelley
bokelley enabled auto-merge (squash) August 15, 2026 15:32
@bokelley
bokelley disabled auto-merge August 16, 2026 15:39
@aao-secretariat

Copy link
Copy Markdown
Contributor

Ladon cannot review this PR until merge conflicts are resolved.

@bokelley

bokelley commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Vendor implementation review requested before merge.

This update now makes the activation contract account-safe and separates the capability from account setup:

  • dataset_query.identity is the required opaque principal; cloud and region are optional but must appear together when deployment routing matters.
  • platform_distribution.destination_ref is optional in seller-level capabilities and, when present, is explicitly account-scoped and non-secret.
  • clean_room is declared only when the collaboration can produce an audience targetable on the advertised product. Analytics-only or measurement-only clean rooms do not qualify.
  • Clean-room collaboration can compose with dataset_query or platform_distribution; it does not by itself promise portability.
  • Seller-level capability summaries may omit operational coordinates until account setup and must never expose coordinates belonging to another account.

Please validate these points against real implementations:

  1. Snowflake, BigQuery, and Databricks: is opaque identity plus optional paired cloud/region enough to describe consumer access without baking vendor-specific auth into the protocol?
  2. LiveRamp, InfoSum, and other destination platforms: is an optional account-scoped destination reference the correct pre-setup/post-setup model?
  3. AMC, Ads Data Hub, PAIR, and other clean rooms: is the targetable-output boundary accurate, including composition with a separate output rail?
  4. Are there any common activation flows that cannot be represented without adding a new method or exposing credentials?

Local validation after merging current main: 818 schemas, all 270 schema-backed documentation examples, focused activation contract tests, and both current and 3.0-compatible training-agent storyboard matrices pass.

Holding merge for vendor feedback.

@bokelley

Copy link
Copy Markdown
Contributor Author

Vendor implementation review — triage pass. Four questions assessed against real vendor flows; protocol and product experts consulted. Q1 and Q2 confirmed solid; Q3 mostly solid with one documentation gap; Q4 surfaces one schema correctness issue worth resolving before merge plus two known deferrals.

Q1 — Snowflake, BigQuery, Databricks: confirmed sufficient.

The opaque identity field maps correctly to all three formats (Snowflake ORGNAME.ACCOUNTNAME, Databricks recipient identifier, BigQuery IAM principal) without baking in auth. The paired cloud/region is functionally load-bearing, not decorative — Snowflake Direct Share is same-cloud-only, and cross-cloud Auto-Fulfillment requires knowing the consumer's cloud and region; BigQuery and Databricks both have region-scoped fulfillment paths. The schema dependencies constraint correctly enforces their co-appearance. Principal declaration is in-protocol; actual share setup remains bilateral. No schema gap.

Q2 — LiveRamp, InfoSum, destination platforms: confirmed sufficient.

LiveRamp and InfoSum issue destination identifiers only after bilateral setup, matching the "omit until account setup establishes the destination" lifecycle exactly. The MUST NOT publish one global reference when the vendor configuration is buyer- or account-specific constraint closes the account-leakage risk for multi-buyer configurations. bind_expiry_days maps to real platform SLA parameters (24–72h distribution windows are common). No schema gap.

Q3 — AMC, Ads Data Hub, PAIR: mostly confirmed; one documentation gap.

AMC and Ads Data Hub both map correctly: clean_room + platform_distribution for DSP-targeted output, clean_room + dataset_query for queryable output. The targetable-output boundary is accurate for both.

PAIR is the ambiguous case. PAIR is a cryptographic hashing protocol — coordinated hashing with no centralized query environment — not a shared-compute clean room. In practice PAIR outputs activate via DV360, so the correct composition is clean_room (pairing provider as vendor) + platform_distribution (DSP vendor). tmp_identity_match would be wrong here — it covers request-time TMP matching, not batch cryptographic reconciliation. The activation method table in media-products.mdx should include a note clarifying this composition for PAIR-style flows before merge. No schema change needed; documentation only.

Q4 — Flows that cannot be represented:

Pre-merge schema correctness issue: the audience_activation_methods filter items in product-filters.json use a flat object with additionalProperties: false, which allows semantically incoherent filter entries (e.g., { "pattern": "sync_audiences", "transport": "s3" }) to pass schema validation. The activation method schema itself uses per-branch additionalProperties: false; the filter items should mirror that discriminated shape per pattern. This is a restructuring rather than a field addition; filter shape changes after adoption are breaking, so resolving before merge is preferable to a follow-up patch.

Known deferrals (not blockers):

  • SFTP: Intentionally excluded (Decision 2), but creates real friction for traditional media publishers (broadcast streaming, linear convergent, OOH). A follow-up issue with explicit v2 scope commitment would signal intent to potential implementers.
  • Proprietary API push: Retail media sellers whose only ingest path is a platform-specific API appear indistinguishable from "undeclared." A future managed_integration or proprietary_push pattern would address this without credential exposure; not a v1 blocker for the programmatic and data-cloud use cases this PR targets.

The merge conflict flagged by the bot is a separate operational blocker. The schema design holds up to these four validation tests pending the filter-schema restructuring and the PAIR documentation note.


Generated by Claude Code

…#6549)

Restructure audience_activation_methods filter items in product-filters.json
from a flat object into a per-pattern oneOf discriminated union, mirroring
the activation-method schema. This prevents semantically incoherent filter
entries (e.g. { "pattern": "sync_audiences", "transport": "s3" }) from
passing schema validation.

Add a note in media-products.mdx clarifying that PAIR-style cryptographic
hashing protocols are not shared-compute clean rooms: the correct composition
is clean_room (pairing provider as vendor) + platform_distribution (DSP vendor),
not tmp_identity_match.
@bokelley

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough vendor review. Two fixes applied in bd610a5:

Q3 — PAIR documentation gap (fixed). Added a sentence to the clean-room composition paragraph in media-products.mdx clarifying that PAIR-style cryptographic hashing protocols compose as clean_room (pairing provider as vendor) + platform_distribution (DSP vendor), and that tmp_identity_match is the wrong pattern here (it covers request-time TMP matching, not batch cryptographic reconciliation).

Q4 — Filter-items schema correctness (fixed). Restructured audience_activation_methods.items in product-filters.json from a flat object with a shared properties block into a per-pattern oneOf discriminated union, mirroring the activation-method schema. Each branch now carries only the fields valid for its pattern plus additionalProperties: false, so an entry like { "pattern": "sync_audiences", "transport": "s3" } fails schema validation rather than silently passing.

Q4 — Known deferrals (not actioned here). The SFTP and proprietary-API-push gaps are understood. Two follow-up issues would be worth filing — one scoping SFTP for v2 and signaling intent to traditional media publishers, one sketching a managed_integration or proprietary_push pattern for retail media ingest paths. Neither blocks this PR.

Merge conflict. The branch already carries a merge-of-main commit (a8a63597a) that predates this fix; no remaining conflict visible.


Generated by Claude Code

…#6549)

Add media-products.mdx clarifying that PAIR-style cryptographic
hashing protocols are not shared-compute clean rooms: the correct composition
is clean_room (pairing provider as vendor) + platform_distribution (DSP vendor),
not tmp_identity_match.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ladon/needs-human-review Ladon has escalated this PR for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: audience activation method (Snowflake / LiveRamp / direct API / clean rooms)

3 participants