Skip to content

fix(schemas): open the capability blocks and containers 3.3 extends, against DR-0019 (#8501) - #8772

Open
KonstantinMirin wants to merge 6 commits into
adcontextprotocol:mainfrom
KonstantinMirin:schemas/8501-open-against-dr-0019
Open

KonstantinMirin wants to merge 6 commits into
adcontextprotocol:mainfrom
KonstantinMirin:schemas/8501-open-against-dr-0019

Conversation

@KonstantinMirin

@KonstantinMirin KonstantinMirin commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Depends on #8777, which states DR-0036 as a decision; this PR adds only register entries under it.

Summary

This PR opens the 3.2 capability blocks and containers that 3.3 adds fields to. It adds them to scripts/lint-additive-minor-corrections.json as register entries for openings (correction kind 1 in DR-0036: opening a schema that the published text keeps open). It also adds register entries for one named exception: the closed assign operation on sync_creatives takes trackers and landing_page_url, and nothing else. It makes no edits to DR-0036.

It covers entries 7 to 10 and 13 to 19 of the disposition table on #8501 (issuecomment-6601730525).

Part of #8501.

Why

DR-0019 says:

Response roots, capability blocks, and container objects stay open

and:

A Class B schema without an escape hatch is a defect.

3.2 published these schemas closed anyway, and no record explains why. It's the same defect that #8531 fixed for the proposal flow, so I record it the same way. Each register entry cites the DR-0019 sentence that requires the location to be open.

What opens

additionalProperties goes from false to true at exactly these locations, and nothing else in the schema changes:

Schema Pointer Kind Entries
core/daypart-target.json root row with no ext 7, 8
core/targeting-overlay-requirements.json root container 15, 16
core/targeting-overlay-requirements.json /definitions/daypartRequirement/anyOf/1 container branch 9
core/targeting-overlay-requirements.json /properties/demographics/anyOf/1 container branch 17
core/targeting-overlay-support.json /definitions/daypartSupport/anyOf/1 capability block 10
core/spot-reporting-capability.json root capability block 13, 14
core/canonical-reporting-capabilities.json root capability block none yet

What closure did, and what does it now:

  • Daypart windows. The root oneOf still rejects a window that mixes start_hour/end_hour with start_time/end_time.
  • Requirements. An unknown requirement now validates, but it still doesn't match any product. The schema already says "Missing or unknown required fields do not match." The other requirement branches stay closed, so seller limits such as max_values_per_package are still rejected there.

canonical-reporting-capabilities gets no new field here. It opens so that available_installment_statuses can move into it, as agreed on #8501. That move is in a separate PR, and this PR doesn't touch the field.

Each opening's register reason begins "Opens a schema closed in 3.2 against DR-0019.", followed by the kind of schema and what still rejects.

The named exception

The assign branch of assignment_operations[] is a discriminated oneOf branch, and DR-0019 lists those under class A. So it stays closed and takes only trackers and landing_page_url. Any other property is still rejected.

DR-0036's Named exceptions section, as #8777 states it, covers this case: the schema validates a request, so the receiver is the validator; each name requires a capability the receiver declares; and a sender MUST NOT send the name to a receiver that doesn't declare it. Both fields qualify. They require creative.supports_assignment_trackers and creative.supports_assignment_landing_page, and no 3.2 release defines either one. A conforming buyer never sends them to a seller whose validator rejects them.

The register lists both as named-property entries, which the check already supports. Each reason begins "Named exception:". The source quote for each one is the field's own "buyers MUST NOT send this field otherwise" sentence.

Entry 17: the boolean form of demographics is deprecated

In 3.2, age was the only demographic dimension, and demographics: true was the shorthand for { "age": true }. Every 3.2 release, 3.2.0-beta.0 through 3.2.8, also accepts the object form { "age": true } on both schemas. 3.3 deprecates the boolean form on targeting-overlay-support.json and targeting-overlay-requirements.json, and 4.0 removes it (#8778). Senders MUST send the object form. Receivers MUST accept true and read it as { "age": true }.

  • The boolean branch of demographics on both schemas carries deprecated: true, x-deprecated-in: "3.3.0", and x-removed-in: "4.0.0", with the description "Deprecated in 3.3. The 3.2 shorthand for { age: true }. Senders MUST send the object; receivers MUST accept true."
  • targeting-overlay-requirements.json, root: "For demographics, true on either side is the deprecated shorthand for { age: true } and matches as that object." Support satisfies a requirement for a dimension only if it supports that dimension, and the shorthand counts as age.
  • The gender requirement: "Experimental in AdCP 3.3 (media_buy.gender_targeting). Only product support with gender: true satisfies it." The gender support property drops "AdCP 3.2 core remains age-only."
  • get_products.mdx states the same matching rule. The 3.2 to 3.3 migration guide lists the deprecation under Deprecations.
  • static/compliance/source/universal/v4-compatibility.yaml lists both branches in schema_removals_not_checked under issue 4.0: remove the boolean form of demographics in overlay support and requirements #8778. Product support is seller-emitted and observable on get_products, but isn't graded yet; requirements are buyer-sent. The 4.0 compatibility guide's count of marked fields goes from 46 to 48.

Baseline

scripts/lint-additive-minor-baseline.json drops these 11 rows, from 38 to 27:

  • core/daypart-target.json: start_time, end_time (entries 7, 8)
  • core/targeting-overlay-requirements.json /definitions/daypartRequirement/anyOf/1: time_granularity (9)
  • core/targeting-overlay-support.json /definitions/daypartSupport/anyOf/1: time_granularity (10)
  • core/spot-reporting-capability.json: available_statuses, supports_break_position (13, 14)
  • core/targeting-overlay-requirements.json: collection_lists, collection_lists_exclude (15, 16)
  • core/targeting-overlay-requirements.json /properties/demographics/anyOf/1: gender (17)
  • creative/sync-creatives-request.json /properties/assignment_operations/items/oneOf/0: landing_page_url, trackers (18, 19)

No 3.2.x patch

The proposal-flow reopening shipped in 3.3.0 and in a 3.2.x patch (#8532). These openings ship in 3.3.0 only.

In 3.2, closing these schemas rejects nothing a conforming 3.2 peer sends. Every field the opening lets in is a 3.3 field, and 3.3 fields go only to 3.3 peers. The proposal-flow reopening had a 3.2 reason that these schemas lack: the closed proposal-flow responses rejected the adcp_version echo that the 3.2 versioning page requires (#8518). The named exception can't go to 3.2.x at all, because its fields and capabilities are 3.3.

Other changes

  • Tests. Four tests asserted closure on the opened schemas: daypart-time-of-day, targeting-aware-discovery, spot-delivery-reporting-contract, and mcp-schema-analysis. They now expect the schemas to be open. The requirements test also checks that the description keeps the rule that unknown requirements don't match. A new targeting-aware-discovery test checks that both schemas accept demographics: true and the object forms, and that the boolean branch is marked deprecated and removed in 4.0 in the source and the built schema.
  • Docs. The 3.2 to 3.3 migration guide's list of 3.3 corrections, which docs(governance): state DR-0036 as a decision; corrections live in the register #8777 rewrote, now includes these openings (3.3 only, no 3.2.x patch) and the named exception. A correction note in the migration guide and on the versioning page, as fix(schemas): reopen the 3.2 proposal-flow schemas closed against DR-0019 #8531 added for the proposal flow. The guide's Deprecations section lists the boolean form of demographics, and the 4.0 compatibility guide lists it among the seller-emitted removals that aren't graded yet.
  • Changeset. A minor changeset for adcontextprotocol, because the deprecation adds a sender requirement.
  • Size. The comply-test-controller request bundles these schemas, and it sits just under the MCP parity compile limit: 1,375,995 of 1,376,000 bytes. I kept the new descriptions short so it stays within it; this PR doesn't raise the limit.

Compatibility impact

  • Nothing that was valid becomes invalid.
  • A strict validator on 3.2 still rejects the 3.3 fields on these schemas. So a seller sends them only when the served release is 3.3 or later (DR-0024), and a buyer sends them only when the negotiated release is 3.3 or later.
  • demographics: true stays valid on both schemas and reads as { "age": true }. So it never satisfies a gender requirement, and a demographics: true requirement isn't satisfied by gender-only support.

Expected conflicts

Test plan

  • ADCP_ADDITIVE_MINOR_BASELINE_REF=origin/main npm run test:additive-minor: 89 tests pass. The lint reports 27 recorded changes and 0 new, with 7 notices, each marked as a correction listed in the register.
  • The check without the two named-exception entries fails on exactly trackers and landing_page_url. With a made-up rotation_mode on assign, it fails on that name only.
  • npm run test:v4-compatibility-registry (the source tree passes the registry lint, with both demographics sites listed) and tests/schema-deprecation-metadata.test.cjs: pass.
  • test:schemas, test:json-schema, test:examples, test:composed, test:extension-schemas, test:sdk-response-conformance, test:targeting-aware-discovery, test:mcp-schema-projection: pass.
  • npm run build:compliance -- --check, test:docs-nav, check:owned-links, lint:schema-links: pass.
  • tests/changeset-protocol-scope.test.cjs, tests/capabilities-structural-bounds.test.cjs, and node scripts/check-changeset-protocol-scope.cjs origin/main: pass.
  • The pre-commit hook, including the full server unit suite, passes on both commits.
  • Ajv against the source schemas: each opened location accepts an unknown property, and on main each one rejects it. The daypart window still rejects start_hour with end_time, start_time with end_hour, and both forms together. assign accepts trackers and landing_page_url and still rejects any other property. The metro requirement, which stays closed, still rejects one.

@mintlify

mintlify Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit b3e9767.

Approve preview

…e register

DR-0036 states the rule and its departures: a closed schema stays closed
for the major, a minor changes a schema only by addition, and the only
departures are corrections and named exceptions. A correction either
opens a schema that the published text keeps open or applies a bound or
required name that the published text already sets. The record no longer
lists corrections location by location.

scripts/lint-additive-minor-corrections.json is the register. The
additive-minor check no longer parses correction tables from the
decision record; it still requires the register to name an existing
decision record, verifies each quoted source, and accepts exactly the
listed changes. Each reason names its kind of correction in place of a
correction letter.

DR-0019's amendment note, the 3.3 release docs, the migration guides and
the versioning page describe corrections by kind.
Comment on lines +2011 to +2016
// The requirements container is open (a correction under DR-0036). An unknown
// hard requirement still isn't ignored: matching fails closed on it.
assert.equal(
validate({ future_unknown_dimension: true }),
false,
true,
errors(validate)

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.

Medium: Opening targeting-overlay-requirements.json drops the schema-level rejection of unknown hard requirements, and this test swaps the functional guarantee for a prose check. Before, validate({ future_unknown_dimension: true }) returned false — the schema itself fail-closed. Now it returns true and the only remaining assertion is assert.match(...description..., /Missing or unknown required fields do not match\./). That tests the wording, not the matcher. The fail-closed guarantee now rests entirely on the reference matcher, and no functional test in this PR exercises an unrecognized requirement dimension actually failing to match. If the matcher ever fails open on an unknown key, a buyer's hard requirement is silently ignored and non-conforming inventory matches — the exact failure the "do not match" rule exists to prevent. Add a matcher-level case that an unknown requirement dimension matches no product.

@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 five files under static/schemas/source/**, which is a hard, deterministic approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so the gate is not satisfied (decision table row 2). Human/CODEOWNERS review is required before this can merge.

Gated files requiring approval:

  • static/schemas/source/core/canonical-reporting-capabilities.json
  • static/schemas/source/core/daypart-target.json
  • static/schemas/source/core/spot-reporting-capability.json
  • static/schemas/source/core/targeting-overlay-requirements.json
  • static/schemas/source/core/targeting-overlay-support.json

Medium findings:

  • tests/targeting-aware-discovery.test.cjs:2016 — Opened requirements container shifts fail-closed behavior to a matcher with no functional test covering the now-reachable fail-open path.

The reviewer confirmed schema↔docs coherence holds, changeset types are correct (minor for additive openings, no breaking wire change, no dist/ edits, no oneOf regression), and the lint governance redesign is deliberate and documented. The sole substantive concern is the Medium test-coverage gap above — not blocking on its own, but it lands on a modified high-risk schema path and warrants a human eye alongside the gate review.

Medium findings

  • tests/targeting-aware-discovery.test.cjs:2016 — Opened requirements container shifts fail-closed to matcher with no functional test

Why human review

  • Gated paths touched (static/schemas/source/**) with review_decision=REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required on: canonical-reporting-capabilities.json, daypart-target.json, spot-reporting-capability.json, targeting-overlay-requirements.json, targeting-overlay-support.json
  • Medium finding on a modified high-risk schema path: opened requirements container shifts fail-closed to matcher with no functional test (tests/targeting-aware-discovery.test.cjs:2016)
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/canonical-reporting-capabilities.json (modified) matches static/schemas/source/**; static/schemas/source/core/daypart-target.json (modified) matches static/schemas/source/**; static/schemas/source/core/spot-reporting-capability.json (modified) matches static/schemas/source/**; static/schemas/source/core/targeting-overlay-requirements.json (modified) matches static/schemas/source/**; static/schemas/source/core/targeting-overlay-support.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.

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Nov 7, 2026
@aao-secretariat
aao-secretariat Bot requested a review from bokelley November 7, 2026 16:28
@KonstantinMirin

Copy link
Copy Markdown
Collaborator Author

Thanks, that's fair: the assertion here only checked the wording. a7f7f7f adds a functional case in server/tests/unit/training-agent.test.ts. It shows that { geo_regions: {...}, future_unknown_dimension: true } validates against targeting-overlay-requirements.json, but list_products and get_products return no product for it, while the same request without the unknown key returns the product. That is what the schema requires: "Missing or unknown required fields do not match." Opening the container makes the unknown key valid on the wire. It doesn't make it a requirement the seller may ignore.

…against DR-0019 (adcontextprotocol#8501)

3.2 published these schemas closed. DR-0019 keeps capability blocks and
container objects open, and calls a class B row without an escape hatch
a defect. No record explains the closure. Open them as corrections under
DR-0036, which allows opening a schema that the published text keeps
open:

- core/daypart-target.json, a row with no ext. The root oneOf still
  rejects a window that mixes start_hour/end_hour with
  start_time/end_time.
- core/targeting-overlay-requirements.json, with its structured daypart
  and demographics requirement branches. An unknown requirement still
  matches no product.
- the structured daypart support branch of
  core/targeting-overlay-support.json.
- core/spot-reporting-capability.json and
  core/canonical-reporting-capabilities.json.

Each change sets additionalProperties to true at the listed pointer and
nothing else. No 3.2.x patch opens them: their 3.2 closure rejects
nothing a conforming 3.2 peer sends.

Register the sync_creatives assign branch as a named exception under
DR-0036. It is a discriminated oneOf branch (DR-0019 class A), so it
stays closed and takes only trackers and landing_page_url. Each requires
a creative capability the seller declares, and buyers must not send it
otherwise.

Add the nine entries to scripts/lint-additive-minor-corrections.json and
drop the 11 baseline rows they cover (38 to 27). Update the four tests
that asserted closure on the opened schemas, extend the list of 3.3
corrections in the migration guide, add correction notes to the
migration guide and versioning page, and add a patch changeset.
… { age: true }

The boolean branch of demographics on targeting-overlay-support and
targeting-overlay-requirements is deprecated in 3.3 and removed in 4.0
(adcontextprotocol#8778). It is defined as the 3.2 shorthand for { age: true }. Senders
MUST send the object form, which every 3.2 release accepts; receivers
MUST accept true and read it as { age: true }, so it satisfies an age
requirement and never a gender requirement.

Mark both boolean branches x-removed-in 4.0.0 and list them in the 4.0
compatibility registry under schema_removals_not_checked. Product
support is seller-emitted and observable on get_products but isn't
graded yet; requirements are buyer-sent.

Document the deprecation in the get_products matching rules, the 3.2 to
3.3 migration guide, and the 4.0 compatibility guide. The changeset
becomes minor because the deprecation adds a sender requirement.
… no product

The requirements container is open, so an unknown required_overlay_support
key passes schema validation. Assert that list_products and get_products
still return no product for it, against a control request that differs
only by that key: missing or unknown required fields do not match.
@KonstantinMirin
KonstantinMirin force-pushed the schemas/8501-open-against-dr-0019 branch from a7f7f7f to b3e9767 Compare November 7, 2026 22:48

Every payload that was valid stays valid. The 3.3 fields on these schemas go only to 3.3 peers, because a strict 3.2 validator still rejects them. No 3.2.x patch opens them.

The boolean form of `demographics` is deprecated on both `core/targeting-overlay-support.json` and `core/targeting-overlay-requirements.json`, and removed in 4.0 (#8778). `demographics: true` is the 3.2 shorthand for `{ "age": true }`, and every 3.2 release accepts the object form. Senders MUST send the object form. Receivers MUST accept `true` and read it as `{ "age": true }`, so it satisfies an age requirement and never a `gender` requirement.

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.

Medium: This states boolean demographics "satisfies an age requirement and never a gender requirement." The reference matcher doesn't implement that. overlaySupportContains (server/src/training-agent/task-handlers.ts:8054) treats boolean true as a wildcard — its own doc comment says "true means unrestricted protocol-valid support." So product support demographics: true + requirement { gender: true } matches at L8059-8068, and a demographics: true requirement is satisfied by gender-only support at L8070. Neither reads true as { age: true }. No functional test covers the boolean→age matching claim — the new targeting-aware-discovery.test.cjs case only asserts schema validation. Same shape as the prior container finding: a semantic matcher claim shipped without a functional test.

@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 opens 3.2 capability blocks/containers to additionalProperties: true (DR-0036 corrections) and deprecates boolean demographics, touching protocol source of truth under static/schemas/source/**. Per the decision table, row 2 fires: gated_paths: true and review_decision is not APPROVED (unknown/none). Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

Iteration credit: the prior Medium finding (missing functional test for the opened requirements container) has been resolved by the new fail-closed functional test in server/tests/unit/training-agent.test.ts:409-445. Schema↔docs↔changeset are coherent, the minor changeset is correct (additive opens + still-functional deprecation), the daypart root oneOf still rejects mixed forms, and lint baseline→corrections moves are consistent.

Medium findings

  • .changeset/open-capability-blocks-and-containers.md:16 — Boolean demographics age-only claim ("{age:true} never satisfies a gender requirement") not honored by the reference matcher (overlaySupportContains treats boolean true as a wildcard both directions); no functional test for the age-only semantics.

Escalation reasons

  • Gated paths touched (static/schemas/source/**) with review_decision not APPROVED — human/CODEOWNERS approval required on: canonical-reporting-capabilities.json, daypart-target.json, spot-reporting-capability.json, targeting-overlay-requirements.json, targeting-overlay-support.json.
  • Fresh medium finding still present on this run, sustaining the prior escalation.

Medium findings

  • .changeset/open-capability-blocks-and-containers.md:16 — Boolean demographics age-only claim not honored by reference matcher; no functional test

Why human review

  • Gated paths touched (static/schemas/source/**) with review_decision not APPROVED — human/CODEOWNERS approval required on: canonical-reporting-capabilities.json, daypart-target.json, spot-reporting-capability.json, targeting-overlay-requirements.json, targeting-overlay-support.json
  • Fresh medium finding present on this run (.changeset/open-capability-blocks-and-containers.md:16 — boolean demographics age-only claim not honored by reference matcher; no functional test), sustaining prior escalation
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/canonical-reporting-capabilities.json (modified) matches static/schemas/source/**; static/schemas/source/core/daypart-target.json (modified) matches static/schemas/source/**; static/schemas/source/core/spot-reporting-capability.json (modified) matches static/schemas/source/**; static/schemas/source/core/targeting-overlay-requirements.json (modified) matches static/schemas/source/**; static/schemas/source/core/targeting-overlay-support.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'unknown', 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 commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

@KonstantinMirin, thank you for this. It's superseded by #8866, which folds it in with credit, so I'm leaving this PR for you to close.

On Nov 9 the WG decided that 3.x opens the schemas that were meant to be open, as DR-0036 is being rewritten in #8858. #8866 makes that change for 3.3:

  • Kept from this PR, unchanged: the schema, test and doc changes. That covers the daypart window, the overlay requirement and support containers, spot and canonical reporting capabilities, the boolean demographics deprecation with its v4-compatibility.yaml entry, the training-agent test and the get_products matching text.
  • Not carried over: the register entries and the named exception for assign. Under the decision, opening needs no corrections entry. The assign branch also opens: its operation const keeps the oneOf disjoint, so trackers and landing_page_url need no exception.
  • Changed: the other requirement branches open too. Instead of keeping them closed, they now reject the seller maxima (max_values_per_package, max_packages) by name, in the not/required form you used for canonical-product in fix(schemas): reopen the 3.2 proposal-flow schemas closed against DR-0019 #8531.

The rest of #8866 opens 744 of the 859 closed locations. 115 stay closed for a structural reason that Brian approved, and the additive-minor lint changes to match.

🤖 Generated with Claude Code

bokelley added a commit that referenced this pull request Nov 9, 2026
Brings over the schema, test and doc changes of #8772 by Konstantin
Mirin: the daypart window, the overlay requirement and support
containers, spot reporting and canonical reporting capabilities open;
boolean demographics becomes the deprecated 3.2 shorthand for
{ age: true }, so only gender support satisfies a gender requirement.

The DR-0036 register entries and the named exception from #8772 are
left out: the next commits open these schemas under the WG decision of
2026-11-09, which needs neither.

Co-authored-by: Konstantin Mirin <konstantin.mirin@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KonstantinMirin

Copy link
Copy Markdown
Collaborator Author

I'd recommend closing this as superseded by #8866, which carries every schema change, test and the registry entry from here. Thanks for the credit, Brian. Two doc hunks didn't come across, the demographics deprecation in the migration guide and the count in the 4.0 compatibility guide, and I've noted both in my review on #8866.

bokelley added a commit that referenced this pull request Nov 10, 2026
Brings over the schema, test and doc changes of #8772 by Konstantin
Mirin: the daypart window, the overlay requirement and support
containers, spot reporting and canonical reporting capabilities open;
boolean demographics becomes the deprecated 3.2 shorthand for
{ age: true }, so only gender support satisfies a gender requirement.

The DR-0036 register entries and the named exception from #8772 are
left out: the next commits open these schemas under the WG decision of
2026-11-09, which needs neither.

Co-authored-by: Konstantin Mirin <konstantin.mirin@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bokelley added a commit that referenced this pull request Nov 10, 2026
…unks

From Konstantin Mirin's review of #8866:

- The commercial terms and proposal-budget-constraint are held closed
  until 4.0, not closed for a structural reason. terms_digest covers
  every member at every depth, open ones included, so the docs no longer
  claim that closure stops a 3.2 buyer accepting a term it ignored.
- The boolean demographics deprecation reaches the 3.2 to 3.3 summary
  and Deprecations list, and the 4.0 guide's removal count and
  seller-emitted list, as #8772 had them.
- target_audiences sits beside audience_evidence_requirements because
  one names which audience and the other the evidence quality, not
  because the requirements object was closed.
- 3.2.10 opens all but one of 3.2's locations: it keeps the
  lookup_unavailable branch closed. The get_principal current and
  sync_principal applied and validated results open, so 747 locations
  open and 112 stay closed.
- The lint hint points to the corrections register again and says a
  correction sets a bound or a required name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bokelley added a commit that referenced this pull request Nov 12, 2026
* fix(schemas): fold in #8772's capability-block and container openings

Brings over the schema, test and doc changes of #8772 by Konstantin
Mirin: the daypart window, the overlay requirement and support
containers, spot reporting and canonical reporting capabilities open;
boolean demographics becomes the deprecated 3.2 shorthand for
{ age: true }, so only gender support satisfies a gender requirement.

The DR-0036 register entries and the named exception from #8772 are
left out: the next commits open these schemas under the WG decision of
2026-11-09, which needs neither.

Co-authored-by: Konstantin Mirin <konstantin.mirin@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): open the 3.3 schemas that were meant to be open

The WG decided on 2026-11-09 that 3.x opens the schemas that were meant
to be open: closing a schema only breaks hard-coded validators, so
opening one is not a protocol break. additionalProperties goes from
false to true at 784 of the 859 closed locations in the source tree.
Nothing else in those schemas changes.

75 locations stay closed for a structural reason: oneOf branches told
apart only by key presence (a2ui bound-value, brand.json), the signed
commercial terms that terms_digest binds, metric qualifiers and
forecast dimensions (join keys), the canonical installment identity,
the JWS response envelope, the TMP match requests and the not-found
error (privacy boundaries), the reporting-destination setup action,
and patternProperties maps keyed by IDs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): keep the privacy clamps closed; drop closure claims from opened schemas

The per-agent PERMISSION_DENIED details and the failed and unconfigured
get_principal results stay closed: their descriptions make the closed
shape the clamp against an existence or commercial-state oracle.

Descriptions of schemas that are now open no longer say they are
closed or reject unknown members. The metric qualifiers stay closed, so
their descriptions now say new keys ship in a major release, not in a
minor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(lint): flag additions only to schemas that stay closed

Under the WG decision of 2026-11-09 and the rewritten DR-0036, schemas
are open by default and a schema stays closed only for a structural
reason. The additive-minor lint now matches:

- property-added-to-closed fires only where the schema is closed in both
  the base and the candidate. Opening a schema, and adding to one the
  candidate opens, passes.
- The closed-anywhere-in-the-major rule goes: closedInEarlierReleases,
  the "(closed in X)" findings and the released exemption.
- Corrections only tighten a bound or require a name the published text
  already set. The 24 whole-schema reopen entries go; an entry that lets
  in a property is an error.
- Openings print one notice line with the count.
- Removals, renames, retypes, enum removals, bound tightening, new
  required names and closing an open schema still fail.

The baseline drops the 34 entries this resolves and keeps 4, each with
its resolution: metric_id and the two sla-window fields sit on schemas
that stay closed (signed commercial terms), and evaluator is restored.

Also ports the DR-0036 cross-check removal from #8858 (from Konstantin
Mirin's #8777): the corrections list names its decision record, which
no longer lists entries.

Co-authored-by: Konstantin Mirin <konstantin.mirin@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(lint): cover the open-by-default additive-minor rule

Drops the tests of the earlier-release closure, the released exemption
and the reopen and property corrections, moves the correction checks
onto bound fixtures, and adds cases for additions to schemas closed on
both sides, opened, or open, for closing an open schema, and for a
correction that lets in a property.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): keep the reporting-status and billing-permission clamps closed

The 3.2.10 backport found two more privacy clamps:

- get_reporting_status's Unavailable lookup failure is closed in every
  3.2 release; #8547 opened it during the 3.3 betas. Its caller
  isolation text requires unknown and unauthorized identifiers to use
  one identical lookup_unavailable shape, so it closes again with the
  envelope fields redeclared as 3.2.8 does. The Operational failure
  branch stays open; its failure_kind const keeps the two apart.
- BILLING_NOT_PERMITTED_FOR_AGENT details stay closed, as
  PERMISSION_DENIED's per-agent details do: their description calls
  extra keys commercial-state oracles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: say schemas are open by default; restate the closed ones' reasons

The 3.2 to 3.3 migration guide's "Closed schemas stay closed within a
major" section becomes "Schemas are open by default": what opened, the
structural reasons the rest stay closed, and what it means for
validators, generated types and 3.2 peers. The 3.3 beta page, What's
new, the release notes and the 4.0 compatibility guide follow.

Pending changesets that cited "a minor never changes a closed schema"
or a lettered DR-0036 correction now cite the structural reason
(commercial_terms is signed) or DR-0024. Adds the changeset for the
opening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): keep trust.json and placement-presentation closed

Both are security-sensitive documents whose closure keeps out an
open-ended class of content that no list of names could cover:

- trust.json sits on the signature-verification fetch path, and signing
  keys are published only in the key directory, so its records,
  agents, grants and acknowledgements carry no key material (jwks_uri,
  key pins, signing profiles) and no undeclared authority.
- placement-presentation is a declarative, non-executable frame that
  consumers render, so it carries no markup or script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: count the 100 locations that stay closed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): keep principal results, reporting destinations and hard budget bounds closed

- get_principal and sync_principal results keep their closed branches:
  no branch may become an oracle for another principal's existence or
  state, and a dry run issues no durable identifier.
- Reporting destinations are credential-free by contract, and closure
  rejects credentials under any name.
- proposal-budget-constraint is the strict peer of budget-range: a
  seller that ignored an unknown member of a hard constraint would
  violate a constraint it never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: count the 116 locations that stay closed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): keep the reporting-status lookup_unavailable branch open

Every response root and status branch accepts every envelope field
(#8547), so the Unavailable lookup branch can't close without
redeclaring the envelope, which the composition rule forbids. main's
shape already carries the caller-isolation clamp: the error objects
stay closed, the message is a const, and the ledger and account fields
are rejected by name. The branch goes back to that shape; only its
nested error objects stay closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(security): count the capability blocks 3.3 opens as open positions

The worst-case capabilities document is unchanged at 99,100,981 bytes;
its open positions go from 108 to 190.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): name the fields closure used to reject in opened schemas

Where a closed schema also rejected a specific field the protocol
defines elsewhere, or one its text forbids there, the opened schema now
rejects it by name in a not/anyOf/required clause, as #8531 did for
canonical-product. Examples: trackers, landing_page_url and weight stay
off unassign, replace and merge; hold entries carry no expires_at; a
reporting revision carries no destination_ref; delivery-ready webhooks
carry no access_token; SDK metadata carries no self-asserted approval;
an attestation presentation carries no evaluator decision; requirement
branches reject seller maxima.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: expect opened schemas to accept unknown keys

Tests that asserted generic closure (an undeclared key is rejected, or
additionalProperties is false) now assert the opened behaviour. Tests
that reject a named field are unchanged and pass through the schema's
named rejection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): keep key-presence anyOf unions apart by name

Opening the branches of an anyOf that was told apart only by which keys
are present lets one payload carry both forms. Each branch now rejects
the other's keys by name:

- canonical-account-ref: the account_id form rejects brand and
  operator, and the natural-key form rejects account_id;
- geo-region requirement and support: all_values and values exclude
  each other per country;
- the deprecated fused-system postal area rejects country;
- a sync_reporting_receipts result carries a receipt or an adjustment
  receipt, not both.

Also names step on frequency-cap impression constraints and mode on
list application receipts, matching the 3.2.10 backport.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: finish the open-schema test fallout

- mcp-schema-projection: PARITY_COMPILE_LIMIT 1,384,000 -> 1,385,000,
  measured (the named rejections add 395 bytes to the
  comply-test-controller closure); the list_products legacy-graph check
  strips the new format_ids and v1_format_ref prohibitions as it strips
  canonical-product's.
- Unknown-key cases that are now accepted: entries_total on list
  receipts, an unknown geo_countries support key, response_due_at and
  completion_due_at on issue receipts, the bucket_completness typo, and
  an unexpected publisher targeting key-value config key.
- New canonical-account-ref test: account_id and the natural key
  exclude each other, and an unknown key is accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(training-agent): keep tools/list within budget and ignore unknown declarations

- tools/list discovery schemas omit `additionalProperties: true` inside
  bundled definitions. It is JSON Schema's default and carries no
  information, and dropping it keeps the four-tool surface under its
  162 KiB budget once the named rejections land. The root keeps the
  keyword, which the parity test compares with the source.
- The parity test compares openness instead of the literal keyword.
- sync_principal keeps only the declaration axes the seller reads, so an
  unknown declarations member is ignored rather than stored and read
  back. An unknown section is still refused (UNSUPPORTED_FEATURE).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): address review on named rejections and key-presence guards

From Konstantin Mirin's review of #8866:

- Key-presence guards on two more unions: account-ref's account_id and
  natural-key forms exclude each other (as canonical-account-ref does),
  and image bleed's uniform and per-side forms exclude each other.
  canonical-account-ref's account_id form also rejects operator_unit,
  currency, timezone and sandbox, and a failed receipt result carries
  no receipt. A rescan of every oneOf and anyOf finds no other
  key-presence union.
- Named rejections now cover only names the protocol defines elsewhere
  or forbids there. Dropped: step, covered (two nodes), gpc_honored,
  political, threshold_rate, per_ad_spend, verified (two nodes) and
  mode on list receipts. access_token on the delivery-ready webhook
  stays as a credential clamp.
- Every node or union with a named rejection says in its description
  what it rejects and why, since generated types can't express not.
- The get_principal current branch and the sync_principal applied and
  validated branches open: they return the caller's own configuration
  and are dispatched by kind. The validated branch rejects
  destination_ref and configuration_version, because a dry run issues
  no durable identifier. recognized, unconfigured and failed stay closed.
- The reporting-status lookup_unavailable branch states its clamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: call the commercial terms a hold until 4.0; carry #8772's doc hunks

From Konstantin Mirin's review of #8866:

- The commercial terms and proposal-budget-constraint are held closed
  until 4.0, not closed for a structural reason. terms_digest covers
  every member at every depth, open ones included, so the docs no longer
  claim that closure stops a 3.2 buyer accepting a term it ignored.
- The boolean demographics deprecation reaches the 3.2 to 3.3 summary
  and Deprecations list, and the 4.0 guide's removal count and
  seller-emitted list, as #8772 had them.
- target_audiences sits beside audience_evidence_requirements because
  one names which audience and the other the evidence quality, not
  because the requirements object was closed.
- 3.2.10 opens all but one of 3.2's locations: it keeps the
  lookup_unavailable branch closed. The get_principal current and
  sync_principal applied and validated results open, so 747 locations
  open and 112 stay closed.
- The lint hint points to the corrections register again and says a
  correction sets a bound or a required name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: flip probes for dropped rejections; add account-ref and bleed guards

From Konstantin Mirin's review of #8866:

- Probes for names the protocol never defines (step, mode on receipts,
  covered, gpc_honored, political, threshold_rate, per_ad_spend,
  verified) now expect the opened schema to accept them.
- New tests: account-ref and image bleed key-presence guards, and the
  extended canonical-account-ref guard.
- The list_products legacy-graph check skips not subtrees and
  descriptions structurally, instead of matching serialized strings.
- The model-context projection omits named rejections beside declared
  properties, as it omits bounds: they are validation, and the
  descriptions carry them. A bare not branch of a union stays. The
  media-buy budget is unchanged; PARITY_COMPILE_LIMIT is 1,386,000,
  measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(lint): prune the additive-minor baseline to base 3.2.9

The 3.2.9 forward-merge makes 3.2.9 the default base. The 4 recorded
entries are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: point the backport to the next 3.2.x patch; drop stale closure claims

3.2.10 shipped without the open-schemas backport, so the changeset and
the migration guide now say the next 3.2.x patch (#8864) opens 3.2's
locations, all but one.

Docs that still called opened objects closed now match the schemas:
the refine scope branches (a stray id fails on the missing product_id),
policy entries, sync_plans plan items, and the account-ref variants.
Where the reason was the commercial terms, the docs say they are held
closed until 4.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: regenerate compliance symbol links on get_products

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: describe the account-ref guard errors, not additionalProperties

Merged variants now fail on the named rejection (not) or a missing
required name, and a budget object fails on type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(schemas): reserve the plan_hash bookkeeping names on sync_plans plans

The opened sync_plans plan item accepted the governance agent's
bookkeeping names: version, status, syncedAt, revisionHistory,
committedBudget and committedByType. The plan_hash profile strips
exactly those names from its preimage, so a buyer value under one of
them would sit outside the hash and could collide with state the agent
manages. The plan item now rejects them by name, and its description
says they are reserved and a buyer MUST NOT send them. The list matches
the profile's closed "Excluded fields" list. No storyboard, fixture or
request example sends them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(3.3): say the commercial terms are held closed until 4.0

whats-new and the release notes no longer give the signed commercial_terms
as the example of a structural reason: the rest stay closed where closure
enforces something, and commercial_terms is held closed until 4.0.
experimental-status no longer calls the buy_products purchase input and
the list_products offer_filters closed; both are open in 3.3 and gain
target_audiences in 4.0, with the accepted product-purchase snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(lint): describe the commercial terms as held closed until 4.0

The withdrawn-to-4-0 test comment, the additive-minor baseline $comment
and the lint header drop the structural-reason framing for the commercial
terms. All four baseline entries sit in commercial-terms schemas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(refinement): match the troubleshooting heading to the error it shows

The heading quoted "must NOT have additional properties" while the body
shows the missing product_id error. The body no longer says every scope
branch requires its own identifier: the request scope requires ask.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Konstantin Mirin <konstantin.mirin@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants