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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/creative-review-business-clock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"adcontextprotocol": minor
---

Keep `core/sla-window.json` at its 3.2.8 shape and move business time onto the creative review SLA (adcp#8501, entries 11 and 12). `sla-window` stays closed with the commercial terms until 4.0 (DR-0036), and 3.3.0-beta.1 added `clock` and `business_calendar_id` to it, so a strict 3.2 validator rejected a 3.3 SLA. The signed terms use `sla-window` through `change_terms[].processing_sla`, and 3.2.8 says sellers include weekends and non-working periods in the maximum, so the signed SLA stays elapsed time.

**`clock` and `business_calendar_id` now live on `core/creative-review-sla.json`.** `creative_policy.review_sla` keeps business time: `clock` defaults to `elapsed`, `clock: "business"` requires a `business_calendar_id` that resolves to `get_adcp_capabilities.business_calendars[]`, and the calendar and snapshot rules are unchanged. The schema no longer composes `sla-window`. It defines `response_max` and `completion_max` itself, keeps `completion_max` required, and is open.

**Every other SLA is elapsed time, as in 3.2.** An action's `sla` on `allowed_actions[]` and `available_actions[]`, a change term's `processing_sla`, and `core/escalation-sla.json` reject `clock` and `business_calendar_id`. `escalation-sla` loses the `clock: "elapsed"` it accepted in the betas.

**This changes 3.3.0-beta.1 through beta.5.** The betas are immutable. A seller or SDK built from one stops sending `clock` and `business_calendar_id` anywhere except `creative_policy.review_sla` and the `review_sla` on a creative action. The 3.3 release notes, What's new, the 3.2 to 3.3 migration guide, and the service commitments page are updated.
3 changes: 1 addition & 2 deletions docs/media-buy/product-discovery/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,7 @@ envelope use [`control_media_buy`](/docs/media-buy/task-reference/control_media_

When the seller offers post-acceptance change rights, it places them in
`commercial_terms.change_terms[]`. Each entry names one canonical action, its
service mode, lifecycle statuses, an optional processing SLA (elapsed by default,
or explicit business time using a declared calendar),
service mode, lifecycle statuses, an optional processing SLA in elapsed time,
portable constraints, stable conditions, and an optional contract reference.
Because the array is inside `commercial_terms`, the proposal `terms_digest`
binds those rights. When the array is present, an omitted action was not
Expand Down
2 changes: 1 addition & 1 deletion docs/media-buy/product-discovery/proposal-negotiation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ The protocol layer owns schema validation, capability gates, result cardinality,

Fence `ask` from pricing and authorization authority. It is attacker-controlled free text and may influence candidate generation only through bounded application policy. Only the typed `commercial_terms` object becomes contractual.

Post-acceptance change rights belong in `commercial_terms.change_terms[]`, not in `ask`, product prose, or an unbound capability. The seller emits at most one term per canonical action. `service_mode` describes the buyer-visible route; `allowed_statuses` defines the non-terminal lifecycle states in which the contractual right applies; and `processing_sla.response_max` and `completion_max` default to elapsed-time maxima that already account for weekends and non-working periods. An explicit `clock: "business"` uses the immutable calendar declared by `business_calendar_id`; see [Service commitments](/docs/media-buy/service-commitments) for deadline calculation and snapshot rules. `seller_managed` means the seller controls the internal decision process without exposing whether it used automation or people. The legacy `requires_approval` value remains readable for 3.x compatibility.
Post-acceptance change rights belong in `commercial_terms.change_terms[]`, not in `ask`, product prose, or an unbound capability. The seller emits at most one term per canonical action. `service_mode` describes the buyer-visible route; `allowed_statuses` defines the non-terminal lifecycle states in which the contractual right applies; and `processing_sla.response_max` and `completion_max` are elapsed-time maxima that already account for weekends and non-working periods. A processing SLA has no business clock; only a product's creative review SLA can count working time (see [Service commitments](/docs/media-buy/service-commitments)). `seller_managed` means the seller controls the internal decision process without exposing whether it used automation or people. The legacy `requires_approval` value remains readable for 3.x compatibility.

The complete array is covered by `terms_digest`. If `change_terms` is present, an unlisted action is not a negotiated right; if the whole field is absent, consumers retain legacy behavior and consult current `available_actions[]`. Runtime `available_actions[]` may narrow a negotiated right because of current state or a stated condition, but MUST NOT silently broaden the accepted terms or replace the negotiated mode, SLA, constraints, conditions, or contract reference. A broader right requires an accepted amendment proposal.

Expand Down
20 changes: 14 additions & 6 deletions docs/media-buy/service-commitments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ combine business durations from different calendars without computing a common b

## Creative review

`creative_policy.review_sla` uses the existing SLA window with a required
`completion_max`. Completion means an **approved or rejected decision** for a
`creative_policy.review_sla` is a creative review SLA with a required
`completion_max`, in elapsed time by default or in business time on a declared
calendar. Completion means an **approved or rejected decision** for a
creative in an accepted format. Ingestion, `processing`, `pending_review`, and
`suspended` do not complete review. Every applicable approval scope must reach
an approved or rejected decision; a partially approved assignment with pending
Expand Down Expand Up @@ -64,11 +65,18 @@ alone cannot authorize spend.

## Business-time calendars

`SLAWindow.clock` defaults to `elapsed`, including weekends and non-working time.
An explicit `business` clock counts only working time and MUST reference exactly
one `get_adcp_capabilities.business_calendars[]` entry by `business_calendar_id`.
Only the creative review SLA carries a clock. Its `clock` defaults to `elapsed`,
including weekends and non-working time. An explicit `business` clock counts only
working time and MUST reference exactly one
`get_adcp_capabilities.business_calendars[]` entry by `business_calendar_id`.
Missing or unresolved calendars invalidate the commitment; never fall back to elapsed time.

Every other SLA is elapsed time, as in 3.2: an action's `sla`, a change term's
`processing_sla`, and the escalation SLA. They use `core/sla-window.json`, which
stays closed with the commercial terms until 4.0, so they reject `clock` and
`business_calendar_id`, and sellers include weekends and non-working periods in
the maximum.

A calendar declares an IANA time zone, weekly working periods, complete
non-working dates, a coverage interval, and working minutes per business day:

Expand Down Expand Up @@ -127,7 +135,7 @@ Duration filters MUST require presence and account for `clock`: compare UTC
deadlines for the same receipt time across business and elapsed commitments.
Without calendar support, treat business SLAs as unknown for duration matching.

For a peer pinned below 3.3, sellers MUST omit the entire business-clock SLA or
For a peer pinned below 3.3, sellers MUST omit the entire business-clock review SLA or
translate it to a conservative elapsed bound that holds for every request to
which that declaration applies. Dropping only `clock` and
`business_calendar_id` silently changes the promise and is forbidden. Business
Expand Down
1 change: 1 addition & 0 deletions docs/reference/migration/3-2-to-3-3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ What this means for 3.3 adopters:
- **Generated types can change.** A generator that reads `additionalProperties` treats the opened objects as open: json-schema-to-typescript adds an index signature, and Pydantic models generated with `extra="forbid"` stop rejecting extra fields. Code that relied on a type or validation error for an unknown key needs its own check.
- **Send a 3.3 field only to a 3.3 peer.** A strict validator on 3.2.9 or earlier still rejects it ([Send only to 3.3 peers](#send-only-to-3-3-peers)).
- **Features that needed new terms in `commercial_terms` stay withdrawn** ([above](#withdrawn-from-3-3-planned-for-4-0)), and seller-held change terms are planned for 4.0 instead of shipping as `commercial_terms.change_terms[]`.
- **Only the creative review SLA carries a clock** ([adcp#8501](https://github.com/adcontextprotocol/adcp/issues/8501)). `core/sla-window.json` is back to its 3.2.8 shape, and `clock` and `business_calendar_id` now live on `core/creative-review-sla.json`, which `creative_policy.review_sla` uses. Sellers built on 3.3.0-beta.1 through beta.5 stop sending `clock` and `business_calendar_id` on an action's `sla`, a change term's `processing_sla`, and the escalation SLA. Those maxima are elapsed time and include weekends and non-working periods.

### Capabilities are bounded by structure

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/release-notes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Each beta is immutable. The `3.3-beta` documentation selector advances to the la
- **Envelope and negotiation:** `core/protocol-envelope.json` composed on every task response, with `status` required and one definition each for `message` and `task_id` (#8547); a request carrying only `adcp_major_version` is served the oldest supported release in that major, 3.0 where supported (DR-0034); the schemas that were meant to be open opened, starting with the 3.2 proposal flow (#8531), and the rest stay closed where closure enforces something, with `commercial_terms` held closed until 4.0 (DR-0036); the buyer's registration selects the webhook-signing mode (#8475); condition (b) of `request_signature_required` deprecated and JSON-RPC batch bodies rejected (#8064).
- **Capabilities:** every array, string, and keyed map in the `get_adcp_capabilities` response is bounded by structure, and the 64 KiB byte cap is a receiver backstop, not a design target (#8505, #8526).
- **Principals:** a principal is the buyer agent as the seller has bound it, so `principal_kind` and `expected_principal_kind` are deprecated (#8443); one delegation-chain rule for demand-side grants (#8473).
- **Service commitments and records:** product creative-review timing, business-clock SLA windows, and the `report_media_buy_issue` and `get_media_buy_issue` contracts (#8417); package retention on canceled and completed buys (#8467); an optional display `name` on measurement catalog metrics (#8437); an optional `metric_id` on performance standards (#8436); `setup_url` accepts query strings and percent-encoding (#8548).
- **Service commitments and records:** product creative-review timing in elapsed or business time on seller calendars, and the `report_media_buy_issue` and `get_media_buy_issue` contracts (#8417); package retention on canceled and completed buys (#8467); an optional display `name` on measurement catalog metrics (#8437); an optional `metric_id` on performance standards (#8436); `setup_url` accepts query strings and percent-encoding (#8548).
- **Platform identifiers:** `platform_id` on `sync_event_sources` and `sync_audiences` results, with deprecated `seller_id` aliases retained throughout 3.x. Sellers reporting an ID send both with identical values; buyers prefer `platform_id` and fall back to `seller_id` when absent (#8290).
- **Signals:** optional `age_determination_bases` on signal listings, definitions, and enrichment, so a signal can say whether its members' ages are `verified`, `declared`, or `inferred` (#8145). Stable in 3.3; 3.2 integrations carry it as `ext.adcp.age_determination_bases`. Experimental `signal_status` and `source_audience_id` on `get_signals` results (#8036). Experimental signal pricing groups, `signals.pricing_groups` (#8058), and provider-initiated delivery to agent destinations, `signals.delivery_methods` (#8044).
- **Property lists:** enforceable `domain` / `subdomain` identifier formats, `get_property_list` reference resolution with list-scoped bearer credentials, and a registry sandbox (#8281, #8282).
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/whats-new-in-3-3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ These shipped before beta.3 and are summarized here with the rest. Everything ma
- **Undisclosed rosters ([#7948](https://github.com/adcontextprotocol/adcp/pull/7948), beta.1).** A product's `publisher_properties` or `collections` can be `[]`, which means the seller does not disclose the roster. There is no new field and no partial state. Allow lists may be accepted against it and cover only what the seller matches. Exclusion lists are the buyer's choice, because the buyer sees the undisclosed roster at discovery: a seller that accepts one applies it to its full internal roster, and a seller that cannot rejects it with `UNSUPPORTED_FEATURE`. A seller serving a release below 3.3 never returns such a product. Buyers treat the roster as opaque and may refuse the product. See [Roster coverage](/docs/media-buy/product-discovery/media-products#roster-coverage).
- **Canceled and completed buys keep their packages ([#8467](https://github.com/adcontextprotocol/adcp/pull/8467), beta.1).** From 3.3 a seller retains packages on terminal buys, with stable identities, platform mappings, and the latest committed configuration, and discloses irrecoverable history through nonfatal errors instead of returning an incomplete list.
- **Mixed creative approval states at startup ([#8485](https://github.com/adcontextprotocol/adcp/pull/8485), beta.1).** A seller keeps pending-review and rejected assignments and starts the approved ones by default. The optional package `start_requires_all_creatives_approved` gate holds the start until every assignment is approved, and `media_buy.features.creative_start_policy` declares support.
- **Review and escalation commitments ([#8417](https://github.com/adcontextprotocol/adcp/pull/8417), beta.1).** Products can declare creative-review timing and business-clock SLA windows with seller calendars. `report_media_buy_issue` and `get_media_buy_issue` give a human acknowledgement and resolution status for a critical report.
- **Review and escalation commitments ([#8417](https://github.com/adcontextprotocol/adcp/pull/8417), beta.1).** Products can declare creative-review timing in elapsed time or in business time on a seller calendar. Every other SLA stays elapsed time, as in 3.2. `report_media_buy_issue` and `get_media_buy_issue` give a human acknowledgement and resolution status for a critical report.
- **Seller offers (experimental, `media_buy.seller_offers`, [#8102](https://github.com/adcontextprotocol/adcp/pull/8102), beta.2).** A seller can propose a change to an accepted buy: a postponed or canceled installment, a preemption, under-delivery. The offer is a `committed` canonical proposal with `proposal_initiator: "seller"` and an `offer_reason`, read through `list_proposals` and announced by the identifiers-only `proposal.offered` notification. The buyer accepts, declines, or revises it with the existing tasks. At `expires_at` the signed terms stand, and silence is never acceptance. Sellers never send offers to callers below 3.3. See [Seller offers](/docs/media-buy/product-discovery/proposal-negotiation#seller-offers).
- **Price adjustments (experimental, `media_buy.price_adjustments`, [#7955](https://github.com/adcontextprotocol/adcp/pull/7955), beta.1).** A fixed-price pricing option can publish an ordered table of fee and discount rows keyed by format option, placement selection, or always, so a buyer computes the price of what it selects. The table is optional, and its resolution order and rounding are normative. See [Selection-dependent price adjustments](/docs/media-buy/advanced-topics/pricing-models#selection-dependent-price-adjustments).
- **Execution readiness (experimental, `media_buy.execution_readiness`, [#7934](https://github.com/adcontextprotocol/adcp/pull/7934), beta.1).** A `get_products` request with `account` can set `include_execution_readiness: true` to learn, per product and per `execution_requirements` entry, whether the account is `ready`, `setup_required`, `selection_required`, `input_required`, or `unknown`, with candidates whose `resource_id` fits the existing binding field. See [Per-account execution readiness](/docs/media-buy/specification#per-account-execution-readiness-experimental).
Expand Down
16 changes: 1 addition & 15 deletions scripts/lint-additive-minor-baseline.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$comment": "Non-additive schema changes recorded by scripts/lint-additive-minor.cjs, relative to the release named in base. This list only shrinks: CI rejects an entry that the target branch does not have. Remove an entry when its change is reverted or released, and record the decision in reason. A property-added-to-closed entry records an addition to a schema that stays closed in 3.x; all four current entries are in commercial-terms schemas, which are held closed until 4.0 (DR-0036).",
"$comment": "Non-additive schema changes recorded by scripts/lint-additive-minor.cjs, relative to the release named in base. This list only shrinks: CI rejects an entry that the target branch does not have. Remove an entry when its change is reverted or released, and record the decision in reason. A property-added-to-closed entry records an addition to a schema that stays closed in 3.x; the one current entry is in a commercial-terms schema, which is held closed until 4.0 (DR-0036).",
"base": "3.2.9",
"entries": [
{
Expand All @@ -9,20 +9,6 @@
"detail": "property metric_id added",
"reason": "Kept closed: part of the signed commercial terms (DR-0036). metric_id selects which vendor number decides a signed performance standard, so a 3.2 buyer would read the same standard differently. #8501 entry 6 withdraws it from 3.3 and removes this entry."
},
{
"file": "core/sla-window.json",
"pointer": "",
"kind": "property-added-to-closed",
"detail": "property business_calendar_id added",
"reason": "Kept closed: part of the signed commercial terms through change_terms[].processing_sla (DR-0036). #8762 (#8501 entry 12) moves business_calendar_id to core/creative-review-sla.json and removes this entry."
},
{
"file": "core/sla-window.json",
"pointer": "",
"kind": "property-added-to-closed",
"detail": "property clock added",
"reason": "Kept closed: part of the signed commercial terms through change_terms[].processing_sla (DR-0036). A business clock changes what a signed maximum means. #8762 (#8501 entry 11) moves clock to core/creative-review-sla.json and removes this entry."
},
{
"file": "core/x-entity-types.json",
"pointer": "",
Expand Down
Loading
Loading