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
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
"5322-open-ended-hosted-duration",
"5332-discovery-push-notification-config",
"5359-inline-creative-capability",
"5384-community-mirror-openapi",
"a2b-testing-lab",
"aao-dashboard-failure-visibility",
"aao-directory-status-repeated-key",
Expand Down Expand Up @@ -306,6 +307,7 @@
"announcement-drafter-no-tier-label",
"anti-facade-followups-items-1-3-5",
"anti-facade-glob-substring-pinning",
"argus-opus-4-8",
"argus-smoke-test",
"artifact-worker-edge-cache",
"async-discovery-storyboards",
Expand Down Expand Up @@ -358,6 +360,8 @@
"broadcast-radio-creative-identifiers",
"broadcast-tv-training-content",
"brown-rockets-pump",
"build-creative-push-notifications",
"build-creative-recipe-hash",
"build-creative-spend-controls",
"building-decision-page",
"building-docs-sdk-rename-and-cleanup",
Expand Down Expand Up @@ -420,9 +424,12 @@
"contextual-signal-coverage-forecasts",
"controller-gated-pagination-siblings",
"crawler-sales-candidate-probe",
"create-media-buy-start-paused",
"creative-approval-mode-capability",
"creative-evaluator-auth-boundary",
"creative-lifecycle-webhook-storyboard",
"creative-template-audio-storyboard-variant",
"creative-transformer-receipt-and-param-enforcement",
"creative-transformers",
"creator-engagement-events",
"credential-issuance-name-gate",
Expand Down Expand Up @@ -611,6 +618,7 @@
"fix-security-md-stale-urls",
"fix-silent-tracks-compliance-status",
"fix-stale-stripe-customer-auto-heal-and-scheduled-invariants",
"fix-storyboard-asap-pause",
"fix-storyboard-status-scenario-keys",
"fix-stripe-drift-customer-cancellation-silent-drop",
"fix-stripe-expand-and-admin-key-invite",
Expand Down Expand Up @@ -724,6 +732,7 @@
"mock-server-normative-status",
"move-changeset-guidance-to-playbook",
"native-in-feed-storyboard-4774",
"neat-mails-cheer",
"neat-pumas-cry",
"no-fluff-advisory-www-prefix-fix",
"no-runtime-schema-tool-known-limitation",
Expand Down Expand Up @@ -764,8 +773,11 @@
"pipes-already-installed-short-circuit",
"placement-reporting-docs-followup",
"placement-surface-axes",
"plain-ads-retire",
"plays-scalar",
"playwright-e2e-smoke-tests",
"polite-falcons-juggle",
"postal-system-custom",
"prepare-3-1-rc-mode",
"preserve-hierarchy-disabled-at-forensic",
"preserve-member-buying-type",
Expand Down Expand Up @@ -798,6 +810,7 @@
"publisher-self-service-page",
"puppeteer-25",
"push-notification-config-protocol",
"quick-jokes-talk",
"quiet-slack-expected-errors",
"quiet-three-expected-errors",
"rc4-compliance-bundle-closure",
Expand Down Expand Up @@ -943,6 +956,7 @@
"sync-23505-structured-error",
"sync-adcp-protocols-measurement",
"sync-canonical-and-founding-metadata",
"sync-completion-webhook-conformance",
"sync-creatives-read-after-write",
"sync-governance-sales-tenant",
"sync-pick-membership-sub",
Expand Down Expand Up @@ -987,6 +1001,7 @@
"unify-owner-evaluate-agent-stop-legacy-write",
"unique-org-per-domain-invariant",
"universal-dev-mode-org-membership",
"update-adcp-sdk-beta-25",
"upsert-membership-seat-cap",
"upstream-attestation-mode-selection",
"upstream-traffic-portable-digest-semantics",
Expand Down Expand Up @@ -1022,6 +1037,8 @@
"webhook-receiver-envelope",
"wg-operational-charter",
"whats-new-in-3-1-restore",
"wicked-tigers-attack",
"wide-chairs-agree",
"widen-audit-resource-id",
"wild-rocks-yell",
"wire-include-properties-server",
Expand All @@ -1030,6 +1047,7 @@
"worker-watchdog",
"working-group-content-service",
"working-group-membership-service",
"working-group-post-copy-link",
"x-adcp-validation-migration-3827",
"your-hub-rename-and-three-github-surfaces"
]
Expand Down
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,54 @@
# Changelog

## 3.1.0-rc.10

### Minor Changes

- 3281278: Document the live community-mirror lifecycle endpoints in the registry OpenAPI
spec so SDKs can generate typed request and response models instead of
hand-rolling DTOs.
- a4a51bc: Add operation-scoped `push_notification_config` to `build_creative` requests and include `build_creative` in task-type enum values so async build webhooks and task polling can name the task.
- fa64db9: Add optional `recipe_hash` fields to `build_creative` success responses so creative agents can expose an opaque, agent-scoped identity for build-determining inputs without standardizing a cross-agent hash algorithm.
- 313e3a9: Add top-level `paused` to `create_media_buy` so buyers can create campaigns with
delivery held from the outset. A start-paused buy returns `media_buy_status:
"paused"` once activation prerequisites are satisfied; missing creatives and
future start dates still surface as `pending_creatives` and `pending_start`.
- 72b79ac: Replace country-fused postal targeting as the preferred shape with country-local postal systems:

- `postal-system` now adds country-local system names such as `zip`, `zip_plus_four`, `outward`, `plz`, and the fallback `postal_code`; the published enum retains existing country-fused values for 3.x compatibility.
- New postal area objects use `{ country, system, values }`.
- Country/system pairs are validated so known countries only accept their registered local systems; unknown countries use `postal_code` or `custom`.
- `get_adcp_capabilities.media_buy.execution.targeting.geo_postal_areas` now prefers an ISO 3166-1 alpha-2 country-keyed map such as `{ "US": ["zip"], "ZA": ["postal_code"] }`.
- During the 3.x migration, sellers SHOULD emit equivalent deprecated aliases such as `us_zip` alongside native country keys where an alias exists. Buyers and SDKs SHOULD normalize both forms.
- Deprecated country-fused aliases remain accepted through legacy branches for SDK backfill and existing integrations.
- Delivery geo rows now require native postal rows to include `country`.

Refs #5383.

### Patch Changes

- 402062c: Document the `build_creative.evaluator` authentication boundary: evaluator credentials and caller-supplied trust material stay on the transport/account-provisioning channel, off-list evaluator URLs are rejected before outbound calls, accepted evaluator auth failures degrade to seller-default ranking, and the new evaluator-auth storyboard covers direct `agent_url`, nested `feature_agent`, credential-in-payload, accepted-call, and unavailable-evaluator paths.
- b2e3f90: spec(creative): harden the unreleased 3.1 creative-transformer surface — make three already-documented normative rules schema-enforceable, and fix a self-contradictory `leaves_total` formula.

These refine the 3.1 transformer / `build_creative` multiplicity feature before GA. No new surface; each change makes an existing MUST checkable or corrects a description.

- **Per-leaf pricing receipt is now enforced when a build reports cost.** `BuildCreativeVariantSuccess` documents that untrafficked best-of-N / fan-out leaves are billed via the inline per-leaf `vendor_cost` _only_ (they never earn a `creative_id`, so never reach `report_usage`), and that the aggregate `vendor_cost` MUST equal the sum of the per-leaf values — but the leaf only required `[build_variant_id, creative_manifest]`, so a paid agent could bill N leaves and return no machine-readable cost for any of them. Added: (a) a branch-level `if (aggregate vendor_cost present) then` each produced leaf requires `vendor_cost` + `currency`; (b) per-leaf `dependencies` so a leaf can't carry a partial receipt (`vendor_cost`↔`currency` co-required; `pricing_option_id` ⇒ both). A genuinely free build omits the aggregate and is unaffected; a CPM-deferred leaf reports `vendor_cost: 0` (a value, not an omission).

- **`transformer-param.json` `value_source` now binds to its descriptor.** The prose already stated the rules (`inline` ⇒ `allowed_values`; `range` ⇒ `minimum`/`maximum`; `free_text` ⇒ `type: string` and `allowed_values`/`minimum`/`maximum`/`options`/`options_cursor` absent), but nothing enforced them. Added `allOf` `if/then` blocks. `enumerable` is intentionally unconstrained — its `options[]` are returned only when expanded via `expand_params`.

- **Fixed the `leaves_total` formula.** The `conditions_total` field documented the three-factor product (`items_to_produce × conditions_total × variants_per_item`) while the `leaves_total` field two lines down — and the `BuildCreativeVariantSuccess.leaves_total` description — stated the two-factor product, so an agent computing expected leaves from the field's own description under-counted by a factor of `conditions_total` whenever `signal_conditions` was present. All three now state the conditions factor consistently. Docs (`build_creative.mdx`, `creative-transformers.mdx` migration guide) updated to match.

- 725b241: Clarify that task webhooks are not emitted for synchronous completions and add
webhook-emission storyboard coverage for the sync-only invariant. Sellers MUST
NOT replay an inline terminal result to `push_notification_config.url` or invent
a `task_id`; buyer SDKs may still normalize synchronous responses into local
callbacks or handlers because those local conveniences are not AdCP webhooks.
The canonical probe sends an advertised wholesale `get_products` request with
`push_notification_config` and accepts either a terminal synchronous response
without `task_id` or a structured well-formed runtime rejection. A Submitted
async handoff is non-conformant. Any future sync-completion notification mode
would need an explicit, capability-advertised opt-in.

## 3.1.0-rc.9

### Minor Changes
Expand Down
160 changes: 160 additions & 0 deletions dist/compliance/3.1.0-rc.10/domains/brand/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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)."
Loading
Loading