Repository navigation
docs(governance): ratify DR-0034 (major-only requests serve the oldest release) and record DR-0035 #8410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
docs(governance): ratify DR-0034 (major-only requests serve the oldest release) and record DR-0035 #8410
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
6752ad2
docs(governance): propose version-translation decisions and a downgra…
bokelley 74b772d
docs(governance): extend the downgrade-path rule to every major's min…
bokelley 741346d
docs(reference): restate that adcp_major_version is removed in 4.0
bokelley 2866521
docs(governance): ratify DR-0034; split DR-0032/0033 out to #8519
bokelley File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "adcontextprotocol": patch | ||
| --- | ||
|
|
||
| Docs: restate in the 3.3 release notes and the 3.2 → 3.3 migration guide that `adcp_major_version` and `adcp.major_versions`, deprecated since 3.1, are removed in 4.0 (DR-0035). Buyers send `adcp_version` on every request and keep the integer mirror through 3.x; in-house clients that send only the integer should switch now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
governance/decisions/DR-0032-sdks-translate-between-releases.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| --- | ||
| id: DR-0032 | ||
| title: SDKs translate between minor releases of a major; mutations refuse lossy conversion; reduced account keys stay one-to-one | ||
| class: normative | ||
| status: proposed | ||
| date: 2026-10-22 | ||
| refs: ["#8373", "adcontextprotocol/adcp-client#3370", "adcontextprotocol/adcp-client#3372", "DR-0024"] | ||
| dissent: none recorded | ||
| --- | ||
|
|
||
| ## Decision | ||
|
|
||
| **Application code speaks one release.** An AdCP SDK lets an application build requests and read responses in the newest release the SDK supports. The SDK negotiates the highest release both sides support, per counterpart and per call, and converts between that release and the application's, one consecutive minor at a time within the major (3.2 ↔ 3.1 ↔ 3.0, chained; likewise 4.1 ↔ 4.0). This holds for client SDKs (convert requests down, responses up) and server frameworks (convert requests up, responses down, as [DR-0024] requires of responses). | ||
|
|
||
| **Every loss is reported.** A conversion that removes or rewrites a field reports it where the application can see it, with the field's JSON path and the two releases. | ||
|
|
||
| **Reads drop; mutations refuse.** | ||
| - On a read, a field the older release cannot express is removed and reported. | ||
| - On a mutation, the SDK removes only fields that do not change what the counterpart commits to: labels and hints (a buy's `name`, `pacing`, creative rotation hints). If any other field would be lost (a cap, a targeting exclusion, a bidding mode, any field of an update), the SDK refuses the call before sending, with a typed `UNSUPPORTED_FEATURE`-class error. | ||
| - A field with an exact older equivalent is moved, not dropped. Moves are defined by the shared conformance cases (see below), never by one SDK. | ||
|
|
||
| **A task the older release does not define** is refused before sending with a typed error, unless the counterpart lists the tool. | ||
|
|
||
| **Account keys.** When the older release's account natural key cannot express fields of the buyer's key (3.2's `operator_unit`, `currency`, `timezone` and `brand.countries`), the SDK may send the older, reduced key only when the reduction is one-to-one: | ||
| - It records which reduced key each buyer key reached the seller with. | ||
| - It refuses a second buyer key that would reach the same seller account, and refuses a request that sends that reduced key unchanged on behalf of another buyer key. | ||
| - It never merges two buyer accounts into one seller account. | ||
|
|
||
| **When the seller upgrades**, a reduced account keeps being addressed by its reduced key (or by `account_id`) until the buyer migrates it explicitly: | ||
| - An `operator_unit`-only reduction is rekeyed in place with a `sync_accounts` settings update (`account`, `operator_identity`, `revision`). | ||
| - An account reduced by `currency`, `timezone` or `brand.countries` cannot change those on an existing account, so the buyer opens a new account under the full key and decides which key keeps the history. | ||
| - `account_id` addressing is the recommended steady state for accounts first opened across a version gap. | ||
|
|
||
| **Shared conformance cases.** Every consecutive minor pair has cross-version cases in the compliance bundle. They cover each direction for both client and server: input, expected output, expected drops, or expected refusal. Every SDK runs them. | ||
|
|
||
| ## Rationale | ||
|
|
||
| Without this, each buyer and seller rebuilds the same translation, inconsistently: pinning outbound clients to old releases, reducing account keys by hand, and branching handlers on the served release. Dropping fields from a mutation is the dangerous case. A buy sent without its budget cap or exclusion still succeeds, and spends differently from what was asked. Refusing is the only conversion that cannot misspend. | ||
|
|
||
| Sharing a reduced account key looks harmless but merges billing terms, spend, reporting and read access across buyer accounts, for example two buying desks. Encoding the dropped fields into `brand_id` invents brand identities the house never published. A one-to-one reduction keeps account counts honest without either cost, and fails closed exactly where the older release cannot tell keys apart. | ||
|
|
||
| ## Implications | ||
|
|
||
| - Updates the "SDK consequences" section of `specs/version-negotiation.md`: auto-downshift is the default, and it is reported rather than silent. | ||
| - Supersedes nothing. [DR-0024]'s rule that responses conform to the served release is how the server side of this decision is graded. Its unpinned-request clause is addressed separately in DR-0034. | ||
| - The conformance cases are a follow-up deliverable. Until they exist, SDKs document their moves and refusals. | ||
| - Not settled here: a wire signal telling the counterpart that a conversion withheld something. Logs and callbacks on the converting side suffice for now. | ||
|
|
||
| [DR-0024]: ./DR-0024-responses-conform-to-served-release.md |
37 changes: 37 additions & 0 deletions
37
governance/decisions/DR-0033-every-minor-change-has-a-downgrade-path.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| --- | ||
| id: DR-0033 | ||
| title: Every minor-release change states its pathway to the previous minor of the same major | ||
| class: normative | ||
| status: proposed | ||
| date: 2026-10-22 | ||
| refs: ["#8373", "DR-0032", "DR-0024"] | ||
| dissent: none recorded | ||
| --- | ||
|
|
||
| ## Decision | ||
|
|
||
| Every Normative or Breaking change targeting a minor release states, in the PR that introduces it, how it maps to the previous minor release of the same major: 3.3 to 3.2, and later 4.1 to 4.0. A minor release is non-breaking *and* backward compatible: an SDK on the new minor must still be able to reach a counterpart on any earlier minor of its major. That covers each new or changed field, enum value, task, union variant, requiredness and semantic rule. The statement takes exactly one of these forms: | ||
|
|
||
| 1. **Droppable.** Older counterparts never see it, and nothing else changes. For a request field on a mutating task, say also whether dropping it changes what the seller commits to; if it does, the downgrade is a refusal (form 3). | ||
| 2. **Moves.** It has an exact equivalent in the previous release (a renamed field, a restructured block, a narrower enum). Give the mapping, both directions where both exist. | ||
| 3. **Refused.** The previous release cannot express it, and an SDK must refuse rather than send a different meaning. Name the error. | ||
| 4. **Identity-bearing.** It changes account identity, resource identity or idempotency keys. Treat it as Breaking, and state the reduction and migration rule (see [DR-0032]). | ||
|
|
||
| New tasks keep using the manifest's `added_in` and `legacy_fallback`. The pathway also covers the reverse direction: how a server reads the previous release's payload in the new shape. | ||
|
|
||
| Each stated pathway ships with its cross-version conformance case, or with a tracked follow-up when the conformance harness for that pair is not ready. | ||
|
|
||
| Reviewers (Ladon and the expert seats) treat a minor-release schema or normative-doc change with no stated pathway as a `high` finding. | ||
|
|
||
| ## Rationale | ||
|
|
||
| SDKs can only translate what the spec has defined how to translate. When a change ships without a pathway, every SDK invents one, and they disagree on exactly the calls that move money. Asking at design time costs one paragraph; reverse-engineering it after release cost the 3.2 cycle an account-identity break ([DR-0032]) and a set of response shapes older validators reject ([DR-0024]). | ||
|
|
||
| ## Implications | ||
|
|
||
| - Applies to every change targeting a minor release from adoption onward, on 3.x now and on every later major's own minor cycle (4.1 maps to 4.0, 4.2 to 4.1). Existing 3.2 and 3.3 changes are backfilled through the conformance cases in [DR-0032]. | ||
| - A change whose only honest pathway is "refused" is still allowed. The record makes the cost visible, so reviewers can weigh the additive alternative the constitution prefers. | ||
| - A major release (4.0) is the one place breaking changes land, and it is out of scope: cross-major bridging stays `VERSION_UNSUPPORTED` by design. Its own minors are in scope from 4.1 onward. | ||
|
|
||
| [DR-0024]: ./DR-0024-responses-conform-to-served-release.md | ||
| [DR-0032]: ./DR-0032-sdks-translate-between-releases.md |
31 changes: 31 additions & 0 deletions
31
governance/decisions/DR-0034-major-only-requests-are-served-the-oldest-release.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| --- | ||
| id: DR-0034 | ||
| title: A request carrying only adcp_major_version is served the oldest supported release of that major | ||
| class: breaking | ||
| status: proposed | ||
| date: 2026-10-22 | ||
| refs: ["#8373", "DR-0024", "DR-0032"] | ||
| supersedes: DR-0024 | ||
| dissent: open, see Implications | ||
| --- | ||
|
|
||
| ## Decision | ||
|
|
||
| A request that carries `adcp_major_version` and no `adcp_version` is served the **oldest** release the server supports in that major, and the response echoes it. A buyer that wants a newer release's behavior sends `adcp_version`. | ||
|
|
||
| This replaces only the "Unpinned requests" paragraph of [DR-0024]. The rest of DR-0024 stands. A request carrying neither field is still served the server's default release. | ||
|
|
||
| ## Rationale | ||
|
|
||
| Every 3.1+ SDK sends `adcp_version`, so in practice a major-only request comes from a 3.0 buyer, and its payload is 3.0-shaped. Serving it the oldest release reads the request in the shape it was written in and answers in the shape the buyer can validate. That is what [DR-0024] demands of every pinned response. Serving the highest release instead answers a 3.0 buyer with a 3.2 response that a strict 3.0 validator rejects (closed shared types, the removed media-buy `status` alias). With [DR-0032], servers convert between releases, so serving the oldest release costs the handler nothing. | ||
|
|
||
| ## Implications | ||
|
|
||
| - **Changes a default.** Today's text (`specs/version-negotiation.md` §4, `docs/reference/versioning.mdx` "Omitted" and the major-only example) says a major-only request is served the highest supported release. This record classes the change as Breaking, so it needs WG or Board ratification. | ||
| - **Open concern.** The versioning docs explicitly allow in-house clients to stay on `adcp_major_version` through all of 3.x. Such a client written against 3.2 would now be served 3.0 and lose newer fields until it sends `adcp_version`. Ratification should decide whether that cost is acceptable. Possible mitigations: deprecation guidance, a grader warning, or keeping "highest" only for sellers that do not translate. | ||
| - **3.x only.** `adcp_major_version` is deprecated and removed in 4.0 ([DR-0035]), so this rule ends with the 3.x line. In 4.x a request without `adcp_version` is served the server's default release. | ||
| - The TypeScript server framework already serves the oldest release for major-only requests (`@adcp/sdk` `selectServedAdcpRelease`), so it is off-spec until this record or a contrary one is ratified. | ||
|
|
||
| [DR-0024]: ./DR-0024-responses-conform-to-served-release.md | ||
| [DR-0032]: ./DR-0032-sdks-translate-between-releases.md | ||
| [DR-0035]: ./DR-0035-adcp-major-version-removed-in-4-0.md | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium:
supersedes: DR-0024here, and the Decision says this "replaces only the 'Unpinned requests' paragraph of DR-0024" — but DR-0024 is not touched by this PR. Its## Decisionstill reads: "A request with noadcp_version, or with onlyadcp_major_version, is served at the default or highest release" (DR-0024:25). Two ratified records now give opposite answers for the major-only case (highest vs oldest), and DR-0024 carries no marker that it was superseded. Per the records README, supersession keeps the file but addssuperseded_byand the superseding record replaces the stale text. Update DR-0024:25 to point at DR-0034 (or restate the oldest rule) and addsuperseded_by: DR-0034so a reviewer citing DR-0024 §Unpinned requests doesn't get the superseded answer.