Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Changelog

## 3.2.0-beta.6

### Minor Changes

- 4439b18: Add AdCP 3.2 CTV experience profiles implementing the IAB CTV Ad Portfolio without new channel-named canonicals. A shared `ctv_ad_experience` vocabulary (menu, pause, screensaver, overlay, squeezeback, in_scene) pairs each experience with the canonical contracts sellers ingest in practice: `native_in_feed` gains a menu profile with a Native 1.2 video slot, `menu_placement` (tile or headline banner), and `focus_behavior`; `video_vast` gains an explicit `creative_type` (linear, nonlinear, either) superseding `linear_required`, with nonlinear required for the five video-backed experiences and per-experience constraints (overlay and squeezeback need 10s minimum duration, in_scene needs 3s and forbids interactivity, pause is unfloored). SIMID remains available to ordinary Linear VAST but is rejected on every nonlinear CTV profile because VAST serializes `InteractiveCreativeFile` only under Linear `MediaFiles`. `image` accepts pause and screensaver (the image-plus-copy contract major pause-ad sellers ingest); `video_hosted` accepts screensaver; `sponsored_placement` accepts catalog-derived menu tiles, squeezeback, and in_scene. Shared `motion_level` (AdCOM attributes 21–23) and `activation_method` (QR, deep link, push, email, tune-in, SMS) vocabularies carry the interaction layer; activations are engagement events, never impressions. Pairings outside the experience matrix fail validation. The docs distinguish buyer-selected experience commitments from player-resolved runtime eligibility and define multi-canonical cells as sibling format options, not an implicit fallback field. The IAB OpenRTB/AdCOM signaling mapping (plcmt 5–9, playbackmethod 8–11, motion attributes, Native plcmttype) is documented as a bridge annex. Includes worked menu/pause/overlay examples and compliance vectors.
- 56f9224: Add capability-gated delivery breakdowns by canonical creative `format_kind` to `get_media_buy_delivery`, including explicit GET-only scope, `custom` aggregation, truncation disclosure, independent reconciliation from creative-level rows, and the full sort contract (`sort_direction` plus the `by_format_sorted_by`/`by_format_sort_direction` applied-sort echo with row-grain fallback and nulls-last semantics).
- 9a7cf31: Add the experimental AdCP 3.2 `seller_rendered_stateful_display` and `coordinated_placements` canonical creative formats. The stateful display declaration is an executable template contract — slots with limits, safe zones, breakpoints, and a disclosed state/transition graph — with `supply_mode` selecting who renders: `components` (buyer supplies focal-pointed imagery and copy; seller assembles every state × breakpoint deterministically), `rendered_canvases` (buyer authors externally against the published contract), or transitional `layered_source`. Sellers offering the canonical must support deterministic `preview_creative` rendering of every state × breakpoint. Sizing supports fixed, range, aspect-ratio, and fluid (`full_bleed`, `gutter_residual`, viewport-percent) breakpoints; single-state reveal units declare a `reveal` mechanic and `underlay` anchoring instead of fabricated states; transitions add `in_view_timer`, `media_event`, scroll `direction`, and `hover` inputs with per-state click URLs and a `clickthrough` policy. Validation enforces a dismissibility floor for overlay anchoring, an anti-strobe floor on timer cycles, per-canonical slot asset-type whitelists, and non-blocking LEAN policy warnings for IAB-prohibited trigger/anchoring combinations. `coordinated_placements` binds multiple product placements atomically with shared slots, optional `sequence` ordering, and per-component `serving_policy`, with placement-reference normalization and schema validation of referenced sibling options. Includes compliance scenarios, worked product declarations, and adopter documentation.
- a35dfdb: Add `time_based_views` to delivery reporting: an array of time-threshold video view counts, each entry keyed by (threshold_seconds, basis). The new `view-threshold-basis` enum distinguishes play-time counting (platform 2s/6s video views) from in-view counting (IAB/MRC viewable video), which are materially different numbers at the same threshold and must not be conflated or summed. Capability-gated via the `time_based_views` token in available-metric. Implements RFC #6430 with the basis discriminator the RFC's open questions pointed toward.
- 465cb85: Add `requested_metrics` to `get_media_buy_delivery`, giving the GET path the same metric narrowing the reporting webhook already has. Omitted means unchanged full payloads; impressions and spend are always included; requesting a leaf metric identity returns its canonical nested carrier; and `missing_metrics` MUST NOT flag absences caused solely by request narrowing. Implements RFC #6624.
- 6a5ceb9: Add leaf metric identities so nested delivery values are individually declarable, committable, aggregatable, and sortable: `quartile_25`–`quartile_100` (resolving to `quartile_data.q1_views`–`q4_views`) and `viewable_rate`, `viewable_impressions`, `measurable_impressions`, `viewed_seconds` (resolving to the same-named `viewability` fields) join `available-metric` and `sort-metric`. This closes an existing contradiction: `committed-metric` qualifier rules and `delivery-metric-aggregate` conditionals already referenced these metric_ids, and the shipped `committed_metrics` / `metric_aggregates` examples were invalid against their own schemas. Also adds the missing flat transactional scalars (`commissionable_value`, `plays`, `cost_per_completed_view`, `cpm`, `downloads`, `units_sold`, `new_to_brand_units`) to `sort-metric`, with survey/model-based lift scalars documented as intentionally sort-excluded. Leaf identities resolve to the nested canonical values — no duplicate flat response fields are introduced. A metric-identity coherence contract test now enforces enum/schema/example agreement.

**Seller conformance note:** the `viewability` description now requires (MUST) that sellers populate `standard` on reported viewability objects when `committed_metrics` carry a `viewability_standard` qualifier — upgraded from a SHOULD. The trigger is any `committed_metrics` entry with `qualifier.viewability_standard` set, including the container `metric_id: "viewability"` (already in the enum). Sellers with an existing `{metric_id: "viewability", qualifier: {viewability_standard: "mrc"}}` commitment who were sometimes omitting `standard` become non-conformant. Sellers who never use the `viewability_standard` qualifier are unaffected.
- a793fc8: Fix the vendor-scope qualifier on `delivery-metric-aggregate` (previously a closed object with no properties, so only `{}` could validate) and add the optional 5-key qualifier to the vendor branches of `committed-metric`, `missing-metric`, `package-request` committed_metrics, the performance-feedback surfaces, and — critically — the `vendor-metric-value` delivery carrier, whose row uniqueness re-keys from `(vendor, metric_id)` to `(vendor, metric_id, qualifier)` so a vendor metric committed under two attribution windows is representable in the delivery report. Container tokens (`viewability`, `quartile_data`, `dooh_metrics`) are barred as value-bearing aggregate `metric_id`s — leaf identities exist for that. Matches what `canonical-reporting-commitment` already allows; a qualifier parity contract test now enforces an identical closed key set across every hand-maintained copy.
- 6b4525e: Make the automatic delivery breakdowns (creative, keyword, catalog_item) optionally negotiable: including their keys in `reporting_dimensions` adds `limit`/`sort_by`/`sort_direction` control and makes the new `by_X_truncated` and applied-sort echo fields binding, so "top creatives by quartile_100" is answerable with a completeness contract. Omitting the keys preserves today's automatic behavior exactly. Implements RFC #6623.
- be68a66: Add `sort_direction` (asc/desc, default desc) to the six sortable delivery breakdown dimensions and a per-breakdown applied-sort echo (`by_X_sorted_by` / `by_X_sort_direction`, MUST whenever the breakdown is present) so the existing silent fallback-to-spend becomes visible to buyers. Ascending sort enables bottom-N optimization queries (worst placements by viewable_rate) that cannot be recovered from a truncated descending pull.

### Patch Changes

- 350a22c: Map the standard unsuffixed `display_160x600` legacy format to a canonical 160×600 image declaration.
- 3c8fbfd: Correct the OpenRTB/AdCOM `cattax` mappings documented for IAB Content Taxonomy 3.0 and 2.2.
- a1e72ec: Fix 17 compliance storyboards that incorrectly included `get_adcp_capabilities` in `required_tools` alongside capability-specific tools. Because `required_tools` uses OR semantics, listing a universal tool made the storyboard-level gate trivially satisfied for every conformant agent — agents lacking the actual capability tool (e.g. `sync_accounts`, `build_creative`, `get_products`) would enter the storyboard and fail at the first capability-specific step instead of receiving a clean coverage-gap skip.

Affected storyboards: `billing_gate_dispatch`, `agent_notification_configs`, and 15 scenarios across the `media-buy` and `creative` protocol families.
- b407de6: Require a human approval on the final release PR head before publishing committed protocol artifacts, and leave generated documentation snapshots open for human review.
- dea443d: Allow request-signing capability declarations to name both A2A 0.3 slash-path methods, including the nested push-notification-config family, and A2A 1.0 PascalCase methods. Matching remains exact and case-sensitive, so dual-stack agents advertise each supported wire name independently.

## 3.2.0-beta.5

### Minor Changes
Expand Down
196 changes: 196 additions & 0 deletions dist/compliance/3.2.0-beta.6/domains/brand/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
id: brand_baseline
version: "1.0.0"
title: "Brand baseline"
protocol: brand
category: brand_baseline
summary: "Baseline protocol storyboard — every brand agent must declare the brand protocol in capabilities and return a schema-valid brand identity."
track: brand
required_tools:
- get_brand_identity

narrative: |
Brand protocol agents are the identity layer of AdCP. Their job is to hold
brand identity data (names, logos, colors, fonts, tone) and expose it to
other agents — buyer agents, creative agents, DSPs — that need to render
on-brand creative or verify who a campaign is for.

The baseline tests the minimum contract that every brand agent honors,
regardless of what additional capabilities (rights licensing, creative
approval) it layers on top:

1. Declare `brand` in `supported_protocols` on `get_adcp_capabilities`.
2. Respond to `get_brand_identity` with a schema-valid identity manifest.
3. Reject unknown `brand_id` values with a structured error.

Rights licensing (`get_rights`, `acquire_rights`, `update_rights`,
`creative_approval`) ships experimentally in 3.0 and is covered by the
`brand-rights` specialism storyboard, not this baseline.

agent:
interaction_model: brand_agent
capabilities: []
examples:
- "Any brand agent (simple identity host or full rights platform)"
- "Brand-owned agents (Acme Outdoor)"
- "Third-party brand identity platforms"
- "Agency-hosted brand agents"

caller:
role: buyer_agent
example: "Any buyer, creative agent, or DSP needing brand identity"

prerequisites:
description: |
The test kit provides a sample brand (Nova Motors) that any brand agent
can serve identity for.
test_kit: "test-kits/nova-motors.yaml"

phases:
- id: capability_discovery
title: "Capability discovery"
narrative: |
The buyer calls `get_adcp_capabilities` to confirm the agent declares
the brand protocol before issuing any brand-identity call.

steps:
- id: get_capabilities
title: "Check agent capabilities"
narrative: |
Verify that the agent declares `brand` in `supported_protocols`.
Without this claim the buyer MUST NOT send `get_brand_identity`.
task: get_adcp_capabilities
schema_ref: "protocol/get-adcp-capabilities-request.json"
response_schema_ref: "protocol/get-adcp-capabilities-response.json"
doc_ref: "/protocol/get_adcp_capabilities"
comply_scenario: capability_discovery
stateful: false
expected: |
Return capabilities declaring `brand` in `supported_protocols`.

sample_request:
context:
correlation_id: "brand_baseline--get_capabilities"
validations:
- check: response_schema
description: "Response matches get-adcp-capabilities-response.json schema"
- check: field_present
path: "supported_protocols"
description: "Response declares supported_protocols"

- id: brand_identity_retrieval
title: "Brand identity retrieval"
narrative: |
The buyer calls `get_brand_identity` to retrieve the brand's identity
manifest. The minimum contract is a schema-valid response that echoes
the requested `brand_id` and carries at least one name.

steps:
- id: get_brand_identity
title: "Retrieve brand identity"
narrative: |
The buyer calls `get_brand_identity` with a known `brand_id`. The
response MUST match the brand-identity schema and echo the
requested `brand_id`. Rich fields (logos, colors, fonts, tone,
visual_guidelines) are optional at the baseline level — the
minimum bar is that identity resolution works and is schema-valid.
task: get_brand_identity
schema_ref: "brand/get-brand-identity-request.json"
response_schema_ref: "brand/get-brand-identity-response.json"
doc_ref: "/brand-protocol/tasks/get_brand_identity"
stateful: false
expected: |
Return a schema-valid brand identity that echoes the requested
brand_id and includes at least one name.

sample_request:
brand_id: "nova_motors"
context:
correlation_id: "brand_baseline--get_brand_identity"
context_outputs:
- path: "brand_id"
key: "brand_id"

validations:
- check: response_schema
description: "Response matches get-brand-identity-response.json schema"
- check: field_present
path: "brand_id"
description: "Response includes brand_id"
- check: field_value
path: "brand_id"
value: "nova_motors"
description: "Returned brand_id echoes the requested brand"
- check: field_present
path: "names"
description: "Response includes brand names"

- id: unknown_brand_rejection
title: "Unknown brand rejection"
narrative: |
Agents MUST reject unknown `brand_id` values with a structured
AdCP error rather than returning an empty or fabricated manifest.

steps:
- id: get_brand_identity_unknown
title: "Reject unknown brand ID"
narrative: |
The buyer calls `get_brand_identity` with a `brand_id` the agent
does not serve. The response MUST be a structured error with a
recovery classification — not a success response with empty
fields.
task: get_brand_identity
schema_ref: "brand/get-brand-identity-request.json"
response_schema_ref: "brand/get-brand-identity-response.json"
doc_ref: "/brand-protocol/tasks/get_brand_identity"
stateful: false
expected: |
Return an AdCP error response indicating the brand is not known
to this agent.

sample_request:
brand_id: "brand_that_does_not_exist_12345"
context:
correlation_id: "brand_baseline--get_brand_identity_unknown"

expect_error: true
negative_path: payload_well_formed
validations:
- check: error_code
value: "REFERENCE_NOT_FOUND"
description: "Error code indicates brand-not-found. REFERENCE_NOT_FOUND is the canonical fallback per error-handling.mdx (brands lack a dedicated *_NOT_FOUND code)."

- id: brand_discovery
title: "Optional brand discovery"
narrative: |
Agents that implement the experimental `search_brands` task expose a
schema-valid discovery surface without making that task part of the
baseline brand protocol requirement.

steps:
- id: search_brands
title: "Search the agent's brand roster"
task: search_brands
schema_ref: "brand/search-brands-request.json"
response_schema_ref: "brand/search-brands-response.json"
doc_ref: "/brand-protocol/tasks/search_brands"
comply_scenario: brand_discovery
requires_tool: search_brands
stateful: false
expected: |
Return a schema-valid ranked array of brand stubs. An empty array is
valid when the agent has no matching roster entry.
sample_request:
query: "automotive brands with electric vehicles"
countries: ["US"]
context:
correlation_id: "brand_baseline--search_brands"
validations:
- check: response_schema
description: "Response matches search-brands-response.json schema"
- check: field_present
path: "brands"
description: "Discovery response includes the brands array"
- check: field_value
path: "context.correlation_id"
value: "brand_baseline--search_brands"
description: "Context correlation_id returned unchanged"
Loading
Loading