Skip to content

fix!: Cherrypick critical schema fixes into 2026-04-08 version#440

Merged
igrigorik merged 2 commits into
Universal-Commerce-Protocol:release/2026-04-08from
jingyli:cp-patches
May 20, 2026
Merged

fix!: Cherrypick critical schema fixes into 2026-04-08 version#440
igrigorik merged 2 commits into
Universal-Commerce-Protocol:release/2026-04-08from
jingyli:cp-patches

Conversation

@jingyli

@jingyli jingyli commented May 15, 2026

Copy link
Copy Markdown
Contributor

Description

This is a cherrypick request for 2 breaking but critical schema fixes:

Category (Required)

  • Core Protocol: Changes to the base communication layer, global context, or breaking refactors. (Requires Technical Council approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)

Checklist

  • I have followed the Contributing Guide (including Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.
  • New and existing unit tests pass locally with my changes.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.

pjordan and others added 2 commits May 15, 2026 15:42
…niversal-Commerce-Protocol#362)

create_cart is the only REST operation (out of 14) missing the required
UCP-Agent header parameter. All other operations — including get_cart,
update_cart, and cancel_cart — include it. The spec states "All requests
MUST include the UCP-Agent header."
…niversal-Commerce-Protocol#429)

* fix(service): drop endpoint requirement from platform schema

  The platform_schema in service.json required `endpoint` for rest/mcp/a2a
  transports. This was structurally wrong: the protocol has no consumer that
  reads service-level endpoint on the platform side.

  - Negotiation algorithm (overview.md §"Capability Negotiation") reads
    version + extends from the platform profile, not endpoint.
  - Key discovery (overview.md §"Key Discovery") is verification-from-headers
    via UCP-Agent profile URL, not callable RPC against the platform.
  - Webhook delivery (business → platform) is modeled at the capability
    layer via `config.webhook_url` (see order.json#/platform_schema and
    order.md §"Order Event Webhook"), not at the service layer.

  The spec's own Platform Profile example (overview.md §"Platform Profile")
  omits endpoint and consequently fails to validate against this branch of
  its own schema. With this fix the canonical example validates.

* fix(discovery): replace top-level oneOf with anyOf in profile schema

  The top-level discriminator in profile_schema.json was `oneOf`, which
  requires data to match exactly one branch. The two branches (platform_profile,
  business_profile) have no required-vs-forbidden split and no type marker,
  so any document fully describing itself (e.g., a business profile that
  includes spec + schema URLs alongside endpoint) satisfies both branches
  and gets rejected with "must match exactly one schema in oneOf."

  Reproduction with ajv + JSON Schema 2020-12:

    Fixture                                | oneOf  | anyOf
    ---------------------------------------|--------|------
    Platform agent (spec+schema)           | PASS   | PASS
    Hosted-platform (spec+schema+endpoint) | FAIL   | PASS
    Business (endpoint only)               | PASS   | PASS
    Well-described business (all fields)   | FAIL   | PASS

  The second and fourth fixtures are the bug — both represent legitimate
  profiles that fail validation.

  Switching to `anyOf` matches what the data model actually expresses
  ("a profile is platform-shaped, business-shaped, or both"). Consumers
  that want self-describing parse can $ref the specific branch they expect
  based on where they dereferenced (UCP-Agent header → platform_profile;
  /.well-known/ucp → business_profile).

* fix(mcp): drop stale signature field from openrpc meta

  The shopping MCP openrpc doc declared a `signature` field in the request
  meta object referencing RFC 7515 detached JWS. This contradicts the
  Message Signatures specification (docs/specification/signatures.md),
  which is unambiguous: UCP wire signing uses RFC 9421 (HTTP Message
  Signatures) + RFC 9530 (Content-Digest), carried in HTTP headers, not
  in the payload.

  The field was a leftover from an earlier signing-scheme exploration.
  RFC 7515 detached JWS still has one legitimate use in UCP — AP2 mandate
  signing — but that's payload-level by design and unrelated to request
  signing. It does not belong on MCP openrpc meta.

* docs(schemas): update prose to match transport contract

  Two prose surfaces still described the pre-fix endpoint contract:

    - source/schemas/service.json: platform_schema.description listed
      `endpoint` as required on REST/MCP/A2A — contradicting the schema
      after the endpoint-requirement fix earlier in this PR.
    - docs/documentation/schema-authoring.md: the Service Schemas section
      listed transport requirements flat (no platform-vs-business split)
      with `endpoint` required on REST/MCP/A2A across the board.

  Both now reflect the actual contract: platform profiles require
  `schema` on REST/MCP/Embedded with A2A base-only; business profiles
  require `endpoint` on REST/MCP/A2A with Embedded base-only.
@jingyli jingyli added this to the 2026-04-08 milestone May 15, 2026
@jingyli jingyli requested review from a team as code owners May 15, 2026 19:52
@jingyli jingyli added the TC review Ready for TC review label May 15, 2026
@igrigorik igrigorik merged commit fcf48d0 into Universal-Commerce-Protocol:release/2026-04-08 May 20, 2026
9 checks passed
jingyli added a commit to jingyli/ucp that referenced this pull request May 21, 2026
…rsal-Commerce-Protocol#440)

* fix: add missing ucp_agent parameter to create_cart REST operation (Universal-Commerce-Protocol#362)

create_cart is the only REST operation (out of 14) missing the required
UCP-Agent header parameter. All other operations — including get_cart,
update_cart, and cancel_cart — include it. The spec states "All requests
MUST include the UCP-Agent header."

* fix!: repair profile schemas to pass validation & endpoint contract (Universal-Commerce-Protocol#429)

* fix(service): drop endpoint requirement from platform schema

  The platform_schema in service.json required `endpoint` for rest/mcp/a2a
  transports. This was structurally wrong: the protocol has no consumer that
  reads service-level endpoint on the platform side.

  - Negotiation algorithm (overview.md §"Capability Negotiation") reads
    version + extends from the platform profile, not endpoint.
  - Key discovery (overview.md §"Key Discovery") is verification-from-headers
    via UCP-Agent profile URL, not callable RPC against the platform.
  - Webhook delivery (business → platform) is modeled at the capability
    layer via `config.webhook_url` (see order.json#/platform_schema and
    order.md §"Order Event Webhook"), not at the service layer.

  The spec's own Platform Profile example (overview.md §"Platform Profile")
  omits endpoint and consequently fails to validate against this branch of
  its own schema. With this fix the canonical example validates.

* fix(discovery): replace top-level oneOf with anyOf in profile schema

  The top-level discriminator in profile_schema.json was `oneOf`, which
  requires data to match exactly one branch. The two branches (platform_profile,
  business_profile) have no required-vs-forbidden split and no type marker,
  so any document fully describing itself (e.g., a business profile that
  includes spec + schema URLs alongside endpoint) satisfies both branches
  and gets rejected with "must match exactly one schema in oneOf."

  Reproduction with ajv + JSON Schema 2020-12:

    Fixture                                | oneOf  | anyOf
    ---------------------------------------|--------|------
    Platform agent (spec+schema)           | PASS   | PASS
    Hosted-platform (spec+schema+endpoint) | FAIL   | PASS
    Business (endpoint only)               | PASS   | PASS
    Well-described business (all fields)   | FAIL   | PASS

  The second and fourth fixtures are the bug — both represent legitimate
  profiles that fail validation.

  Switching to `anyOf` matches what the data model actually expresses
  ("a profile is platform-shaped, business-shaped, or both"). Consumers
  that want self-describing parse can $ref the specific branch they expect
  based on where they dereferenced (UCP-Agent header → platform_profile;
  /.well-known/ucp → business_profile).

* fix(mcp): drop stale signature field from openrpc meta

  The shopping MCP openrpc doc declared a `signature` field in the request
  meta object referencing RFC 7515 detached JWS. This contradicts the
  Message Signatures specification (docs/specification/signatures.md),
  which is unambiguous: UCP wire signing uses RFC 9421 (HTTP Message
  Signatures) + RFC 9530 (Content-Digest), carried in HTTP headers, not
  in the payload.

  The field was a leftover from an earlier signing-scheme exploration.
  RFC 7515 detached JWS still has one legitimate use in UCP — AP2 mandate
  signing — but that's payload-level by design and unrelated to request
  signing. It does not belong on MCP openrpc meta.

* docs(schemas): update prose to match transport contract

  Two prose surfaces still described the pre-fix endpoint contract:

    - source/schemas/service.json: platform_schema.description listed
      `endpoint` as required on REST/MCP/A2A — contradicting the schema
      after the endpoint-requirement fix earlier in this PR.
    - docs/documentation/schema-authoring.md: the Service Schemas section
      listed transport requirements flat (no platform-vs-business split)
      with `endpoint` required on REST/MCP/A2A across the board.

  Both now reflect the actual contract: platform profiles require
  `schema` on REST/MCP/Embedded with A2A base-only; business profiles
  require `endpoint` on REST/MCP/A2A with Embedded base-only.

---------

Co-authored-by: Patrick R. Jordan <patrick.r.jordan@gmail.com>
Co-authored-by: Ilya Grigorik <ilya@grigorik.com>
jingyli added a commit that referenced this pull request May 22, 2026
…changes into 2026-04-08 version (#433)

* update line item ids to differentiate from item ids (#112)

* fix: standardize package registries in lockfile (#368)

Regenerated the lockfile to ensure all dependencies are resolved from the
default public registries configured in pyproject.toml. This removes
environment-specific registry URLs that were inadvertently included.

* Update signature requirements in documentation (format only) (#242)

Split the notes in different lines to fix rendering

Co-authored-by: Guillaume V. <4216770+ptiper@users.noreply.github.com>

* docs: fix inconsistencies in specification examples (#363)

* fix: add missing ucp_agent parameter to create_cart REST operation

create_cart is the only REST operation (out of 14) missing the required
UCP-Agent header parameter. All other operations — including get_cart,
update_cart, and cancel_cart — include it. The spec states "All requests
MUST include the UCP-Agent header."

* docs: fix inconsistencies in specification examples

Fix five documentation bugs where examples diverged from schema
definitions:

- Fix CheckoutCom.svg fallback text showing "Chewy" instead of
  "Checkout.com" (docs/index.md, 2 occurrences)
- Fix invalid checkout status "ready_for_payment" → "ready_for_complete"
  (buyer-consent.md)
- Fix wrong PostalAddress field "address_street" → "street_address"
  (embedded-checkout.md)
- Fix singular/plural JSONPath "method[0]" → "methods[0]"
  (checkout-rest.md)
- Replace deprecated "risk_signal" with "signals" using reverse-domain
  keys (processor-tokenizer-payment-handler.md)

* docs: fix accuracy issues in documentation pages (#365)

- Replace hardcoded version "2026-01-11" with {{ ucp_version }} template
  variable in playground.md (3 UCP version fields; third-party URLs
  left unchanged)
- Add EP (Embedded Protocol) to transport examples list in
  core-concepts.md, matching the four transports defined in the spec
- Update roadmap to reflect that product discovery, cart, and post-order
  management are now part of the specification

* docs: remove entity wrapper from MCP response examples (#360)

MCP response examples incorrectly nested the UCP payload under a
"checkout", "cart", or "order" key inside structuredContent. The
OpenRPC result.name field is descriptive metadata — it does not
create a wrapper key in the JSON-RPC wire format (per the OpenRPC
spec, name only affects params in by-name mode, not results).

This aligns success response examples with error responses, which
already placed the UCP envelope directly in structuredContent
without a wrapper.

Addresses the same issue as the closed #239 — that fix was deferred
to #216, which corrected error responses but left success responses
wrapped.

* fix: render Total and Totals fields on schema reference page (#352)

The Total and Totals sections on the specification reference page
rendered empty tables because `_render_table_from_schema` entered
the `allOf` branch for schemas that have both `properties` and
`allOf` validation constraints (if/then, contains). The allOf items
contained no renderable content, so no table rows were emitted.

Two fixes in `_render_table_from_schema`:

1. Only enter the `allOf` rendering branch when `properties` is
   empty (`and not properties`). This lets Total's three fields
   (type, display_text, amount) render through the normal property
   iteration path.

2. Treat array-typed schemas (like Totals) the same as scalar types
   in the description fallback, since their top-level `allOf` only
   carries `contains` constraints, not composition. Totals now
   renders its description text, consistent with Amount and Signed
   Amount.

Made-with: Cursor

* docs: Correct profile examples in docs Playground widget (#236)

* Correct payment handler profile example in playground

* Remove trailing space from playground.md

---------

Co-authored-by: Guillaume V. <4216770+ptiper@users.noreply.github.com>

* docs: Modernize text diagram in signatures.md (#331)

* fix: Fix inconsistent schema on documentation examples & discounts extension schema (#371)

* Documentation fix to discounts extension to make sure it is compliant with the spec.

* Clean up unsupported field in checkout examples for quantity-related errors.

* Fix discount schema to use UCP specific annotation instead of readOnly.

* docs: add extensibility and forward compatibility guidelines (#290)

- Added "Extensibility and Forward Compatibility" section to the schema
  authoring guide.
- Defined standards for Open vs. Closed Enumerations to prevent breaking
  changes in code-generated clients.
- Added guidance on avoiding 'additionalProperties: false' to ensure
  forward compatibility for objects.
- Clarified the impact of modern code generators (e.g., Quicktype) on
  schema-to-type validation.
- Updated the comprehensive capability schema example to align with
  these new extensibility rules.

Co-authored-by: Guillaume V. <4216770+ptiper@users.noreply.github.com>

* docs: add centralized glossary and acronym standards (#241)

- Organize terms by category (Commerce, Payments, etc.).
- Define financial acronyms previously used but not expanded.
- Establish guidelines for first-use acronyms in Markdown files.

Ref: #214 (comment)

Co-authored-by: Ilya Grigorik <ilya@grigorik.com>

* docs: enrich core-concepts with comprehensive UCP protocol overview (#336)

* feat: attribution field for platform referral context (#391)

* feat: attribution field for platform referral context

  Adds top-level `attribution` carrying platform-emitted referral and
  conversion-event context (campaigns, click IDs, source/medium markers) —
  the agentic counterpart of URL query parameters in browser-based flows.

  - Core field, not an extension. Attribution is informational; its
    presence or absence does not affect protocol behavior, so capability
    negotiation earns nothing. No registry entry, no extension declaration.
  - Open string-keyed map. UCP does not prescribe attribution models,
    vocabularies, or touchpoint logic. Platforms use their existing
    conventions (GA4 campaign parameters, click identifiers like
    gclid / fbclid / ttclid).
  - Lives on cart, checkout, and catalog requests as platform-provided
    input; appears on order as a business-emitted snapshot of the
    originating checkout's attribution.

* drop direct-identifier clause

What counts as "identifying" depends on jurisdiction and on the
agent's and business's data context. Any enumeration is also
incomplete. UCP can't usefully encode this at the schema level.

The privacy paragraph already carries the compliance posture, the
schema description positively scopes the field, and `buyer` is the
canonical home for direct identifiers.

* docs: document min/max property-count default

By default, UCP schemas do not set `minProperties` or `maxProperties`
on object fields. maxProperties caps are deferred to implementers —
the protocol does not define them because any specific limit requires
judgment calls that inevitably hit exceptions; implementers should
impose their own constraints with clear error feedback. minProperties
is omitted because empty objects are well-formed and harmless;
implementers process them as a no-op.

* docs: Fix typos and improve formatting in index and versioning (#416)

* fix(docs): omit deprecated checkout id from playground payload (#332)

Removes checkoutResponse.id from the update payload in playground.md. This field is marked as deprecated_required_to_omit for update requests (as the ID is now passed in the URL path). It was previously only commented as deprecated without actually removing the property from the payload object.

* docs: adds descriptions to links in llms-txt (#419)

* fix!: Cherrypick critical schema fixes into 2026-04-08 version (#440)

* fix: add missing ucp_agent parameter to create_cart REST operation (#362)

create_cart is the only REST operation (out of 14) missing the required
UCP-Agent header parameter. All other operations — including get_cart,
update_cart, and cancel_cart — include it. The spec states "All requests
MUST include the UCP-Agent header."

* fix!: repair profile schemas to pass validation & endpoint contract (#429)

* fix(service): drop endpoint requirement from platform schema

  The platform_schema in service.json required `endpoint` for rest/mcp/a2a
  transports. This was structurally wrong: the protocol has no consumer that
  reads service-level endpoint on the platform side.

  - Negotiation algorithm (overview.md §"Capability Negotiation") reads
    version + extends from the platform profile, not endpoint.
  - Key discovery (overview.md §"Key Discovery") is verification-from-headers
    via UCP-Agent profile URL, not callable RPC against the platform.
  - Webhook delivery (business → platform) is modeled at the capability
    layer via `config.webhook_url` (see order.json#/platform_schema and
    order.md §"Order Event Webhook"), not at the service layer.

  The spec's own Platform Profile example (overview.md §"Platform Profile")
  omits endpoint and consequently fails to validate against this branch of
  its own schema. With this fix the canonical example validates.

* fix(discovery): replace top-level oneOf with anyOf in profile schema

  The top-level discriminator in profile_schema.json was `oneOf`, which
  requires data to match exactly one branch. The two branches (platform_profile,
  business_profile) have no required-vs-forbidden split and no type marker,
  so any document fully describing itself (e.g., a business profile that
  includes spec + schema URLs alongside endpoint) satisfies both branches
  and gets rejected with "must match exactly one schema in oneOf."

  Reproduction with ajv + JSON Schema 2020-12:

    Fixture                                | oneOf  | anyOf
    ---------------------------------------|--------|------
    Platform agent (spec+schema)           | PASS   | PASS
    Hosted-platform (spec+schema+endpoint) | FAIL   | PASS
    Business (endpoint only)               | PASS   | PASS
    Well-described business (all fields)   | FAIL   | PASS

  The second and fourth fixtures are the bug — both represent legitimate
  profiles that fail validation.

  Switching to `anyOf` matches what the data model actually expresses
  ("a profile is platform-shaped, business-shaped, or both"). Consumers
  that want self-describing parse can $ref the specific branch they expect
  based on where they dereferenced (UCP-Agent header → platform_profile;
  /.well-known/ucp → business_profile).

* fix(mcp): drop stale signature field from openrpc meta

  The shopping MCP openrpc doc declared a `signature` field in the request
  meta object referencing RFC 7515 detached JWS. This contradicts the
  Message Signatures specification (docs/specification/signatures.md),
  which is unambiguous: UCP wire signing uses RFC 9421 (HTTP Message
  Signatures) + RFC 9530 (Content-Digest), carried in HTTP headers, not
  in the payload.

  The field was a leftover from an earlier signing-scheme exploration.
  RFC 7515 detached JWS still has one legitimate use in UCP — AP2 mandate
  signing — but that's payload-level by design and unrelated to request
  signing. It does not belong on MCP openrpc meta.

* docs(schemas): update prose to match transport contract

  Two prose surfaces still described the pre-fix endpoint contract:

    - source/schemas/service.json: platform_schema.description listed
      `endpoint` as required on REST/MCP/A2A — contradicting the schema
      after the endpoint-requirement fix earlier in this PR.
    - docs/documentation/schema-authoring.md: the Service Schemas section
      listed transport requirements flat (no platform-vs-business split)
      with `endpoint` required on REST/MCP/A2A across the board.

  Both now reflect the actual contract: platform profiles require
  `schema` on REST/MCP/Embedded with A2A base-only; business profiles
  require `endpoint` on REST/MCP/A2A with Embedded base-only.

---------

Co-authored-by: Patrick R. Jordan <patrick.r.jordan@gmail.com>
Co-authored-by: Ilya Grigorik <ilya@grigorik.com>

* feat!: identity linking OAuth 2.0 foundation with capability-driven scopes (#354) (#431)

* refactor: Update terminology and clarify roles in UCP documentation

- Replace "consumer surfaces/platforms" with "consumer platforms" and "businesses" with "business platforms" for consistency.
- Enhance the definitions of consumer and business platforms, emphasizing their roles in capability consumption and exposure.
- Revise key goals and responsibilities to reflect updated terminology and clarify the interaction dynamics within the UCP framework.
- Introduce a new section on capabilities, detailing their structure and examples to improve understanding of UCP's functionality.

* docs: update core concepts and capabilities in UCP documentation

- Clarified the role of Payment & Credential Providers to emphasize the secure handling of sensitive user data.
- Enhanced the description of Agentic Commerce to include various modalities for AI agents.
- Revised terminology for distinct actors in the UCP framework to improve clarity.
- Updated capability negotiation process to specify version selection and mutual agreement.
- Improved examples and descriptions for capabilities and transport bindings to align with current standards.

* docs: refine terminology and clarify roles in UCP documentation

- Updated terminology to replace "consumer platforms" with "clients" and "business platforms" with "providers" for consistency and clarity.
- Enhanced descriptions of the roles and responsibilities of clients and providers in the UCP framework.
- Revised key goals and capabilities to reflect the updated terminology and improve understanding of UCP's functionality.

* docs: improve formatting and clarity in UCP core concepts documentation

* docs: minor fix to doc and line wrap

* docs: update terminology and clarify roles in UCP documentation

- Replaced "Client" with "Platform" and "Provider" with "Business" for consistency.

* docs: enhance identity linking capability in UCP documentation

- Updated the identity linking specification to clarify the role of platforms and businesses in buyer-authenticated commerce experiences.
- Introduced a new JSON schema for identity linking, detailing the configuration for capabilities that require buyer identity.
- Revised the overview and general guidelines sections to reflect the updated terminology and structure for identity linking capabilities.
- Added new error code for identity requirements in the shopping types schema.

* chore: revert core-concepts.md to upstream version

Restores docs/documentation/core-concepts.md to match
Universal-Commerce-Protocol/ucp upstream main. The local changes
belong to a separate PR and should not be included here.

* docs: update identity linking terminology in specifications

- Renamed the `required` field to `auth_required` in the identity linking specification and JSON schema to enhance clarity regarding buyer identity requirements.

* fix: schema convention, field naming, iss validation

  Four targeted fixes to prepare this PR for backport to 04/08 and
  clean stacking of the delegated IdP follow-up.

  ## 1. Nest $defs under capability name (convention alignment)

  Restructures the schema to match the established pattern required by
  the composition algorithm: `ext_schema["$defs"][root.name]`

  Before:
    $defs:

  After:
    $defs:
      capability_identity_config
      dev.ucp.common.identity_linking:
        platform_schema, business_schema

  Why: capability-scoped schemas live under the capability's reverse-domain
  name so future tooling can resolve them predictably as
  `schema#/$defs/{capability-name}/business_schema`.

  ## 2. Fix `required` → `auth_required` in overview.md

  The business profile example in `overview.md` used `"required": true`
  while the schema and spec text use `"auth_required"`. Anyone copying the
  overview example would hit a validation error.

  ## 3. Remove top-level `version` field from schema

  No other capability schema in the repo carries a top-level `version`
  field — version lives on the capability entry in the UCP profile, not
  on the schema file itself. Removed for consistency with `checkout.json`,
  `fulfillment.json`, `cart.json`, etc.

  ## 4. Tighten `iss` validation language

  Removed the "if present" hedge in two places (For Platforms bullet and
  Account Linking Flow step 3). Since the spec requires businesses to
  MUST return `iss` in every authorization response, the hedge was
  unnecessary and could be read as making `iss` validation conditional.

  ## 5. $comment updated to reflect unified providers model

  The schema-level `$comment` previously described `providers` and
  `mechanisms` as two separate reserved extension points. Updated to
  describe a single `providers` map with a `type` discriminator defaulting
  to `oauth2` — aligning with feedback on #354 that these are the same
  concept (a trust-anchored identity source with a discovery mechanism
  and proof protocol), not separate keys. This is $comment-only — no
  schema behavior change — and gives the follow-up IdP PR a clean model
  to add `providers` onto without rewriting the Future Extensibility
  section.

* docs:Updated the identity linking specification to clarify the support for both delegated identity providers and non-OAuth authentication mechanisms through a unified `config.providers` extension point.

* docs: Update identity linking terminology from "buyer" to "user" across multiple files and update RFC links

- Changed references from "buyer" to "user" in identity-linking documentation to reflect updated terminology.
- Updated the description in the identity linking schema to specify user-scoped features instead of buyer-scoped.
- Adjusted related documentation sections to ensure consistency in terminology and clarify user authentication requirements.

* refactor: flatten scopes to wire-format-keyed map

  Restructures identity linking scope configuration based on TC discussion.

  `config.capabilities` → `config.scopes`. Flat map keyed by wire-format
  scope token (`{capability}:{scope}`); keys are exactly what platforms put
  into OAuth `scope=` parameters.

  ```json
  "config": {
    "scopes": {
      "dev.ucp.shopping.order:read":   {},
      "dev.ucp.shopping.order:manage": {}
    }
  }

  - auth_required removed. Scope presence in the map is the signal:
  listed → user auth required; not listed → public/agent-auth access.
  - Each value is an open scope_policy object for per-scope auth
  constraints (e.g. min_acr, max_token_age) and future metadata. Platforms
  MUST ignore unrecognized fields.
  - New scope_token $def with regex enforcing {reverse-domain}:{name}
  format; removes ambiguity around bare-capability scopes.

* Add scopes section for checkout capability

* docs: Enhance identity linking specification with new error handling and token validation requirements

- Added requirements for handling `insufficient_scope` errors, including the need for businesses to return specific messages identifying missing scopes.
- Updated the `identity_required` section to clarify conditions under which it should be triggered.
- Introduced the `UCP-Identity-Token` header for user identity tokens when using platform credentials.

* docs: Update identity linking specification to include loopback redirect handling

* docs: Add scopes sections for cart, order, and catalog capabilities

* docs: clarify OAuth 2.0 usage and drop UCP-Identity-Token

* support public clients via PKCE

  The MUST on client_secret_basic excluded native, desktop, and on-device
  agent runtimes (RFC 8252 §8.5 — public clients cannot keep a client_secret)
  and also blocked stronger asymmetric methods (private_key_jwt RFC 7523,
  tls_client_auth RFC 8705). The IdP support we want to land next requires
  asymmetric crypto for JWT bearer assertions.

  Replaces the single-method MUST with RFC-8414-driven negotiation:
    - Confidential clients SHOULD prefer asymmetric methods; MAY use
      client_secret_basic.
    - Public clients (RFC 8252 §8.5) MUST use 'none' and rely on PKCE
      with S256 as proof-of-possession; MUST NOT embed a client_secret.
    - Businesses declare methods in token_endpoint_auth_methods_supported;
      SHOULD support an asymmetric method; MAY support 'none' for public
      clients. PKCE S256 required when 'none' is advertised.
    - Distinct error codes: invalid_client for auth-method failures,
      invalid_grant for PKCE failures.

* docs: normalize capability scope sections

  Per TC discussion default access is a merchant policy decision, not a
  spec mandate. UCP defines well-known scopes; merchants decide what auth
  is required for non-scoped operations.

    - Remove default-access framing from capability specs. Each section
      now states only the well-known scopes the capability defines.
    - Tighten scope descriptions to a consistent shape:
      "<operations gated> — <data or behavior unlocked>".
    - Hoist protocol-level rules (declaration, derivation, well-known vs
      custom extension) into identity-linking.md, where they live once.
      Capability specs link to that section instead of duplicating.
    - Switch the identity-linking.md B2B walkthrough from
      dev.ucp.shopping.checkout:create to :manage. The well-known scope
      fits the "no guest checkout" narrative more cleanly (gates all
      checkout ops, not just the entry point).

* Add an optional description field to the scope_policy object in the identity_linking.json schema.

* document identity_optional and relation to scope description

  * `scope_policy.description`: $ref shared `description.json` type for
    multi-format text (plain/markdown/html). Cross-domain ref to
    shopping/types is the minimal change; promoting the type to
    common/types is a follow-up.

  * New `## Optional Authentication` section + `identity_optional`
    info-severity code. Decoupled from per-scope `description` by
    design: identity_optional is a runtime per-request notice;
    description is static per-scope context for OAuth consent.

  * `insufficient_scope` example fixed: response lists the FULL required
    scope set, not the delta (per Amit's TC restatement). Platform
    computes the diff and uses incremental authorization to avoid
    redundant consent prompts. continue_url wording aligned.

  * Schema descriptions: drop "public or agent-authenticated access"
    framing. TC consensus is that UCP does not prescribe a default;
    merchants decide access policy for non-scoped operations.

  * `message_info.json` `code`: register known info codes via JSON
    Schema `examples` array AND inline names in the description.
    Tooling (autocomplete, codegen) and humans both served.

* docs: extract and document well-known info+warning mesages codes

From the boyscout 'leave it better than you found it' rulebook...

 - Copy _error pattern as standalone file for _info and _warning
 - Include info and warning codes in reference docs

* adopt WWW-Authenticate Bearer challenges

Replace the pre-baked OAuth `continue_url` pattern with RFC 6750 §3
WWW-Authenticate challenges, plus RFC 9728 Protected Resource Metadata
pointers. Resolves David's TC flag.

Why: pre-baking an authorization URL forces the merchant to own
parameters it can't sensibly own — PKCE code_challenge, state,
redirect_uri, client_id are all client-side concerns. Native/agent
clients per RFC 8252 construct their own authorization request anyway,
so the pre-baked URL was either ignored or rewritten. Standard OAuth
client libraries parse WWW-Authenticate Bearer challenges automatically;
custom continue_url parsing was UCP-specific dead weight.

* For Platforms: MUST process WWW-Authenticate Bearer challenges per
  RFC 6750 §3 on 401/403; extract scope parameter; SHOULD follow
  resource_metadata pointer per RFC 9728. Bumped Bearer Authorization
  bullet with RFC 6750 §2.1 reference.
* For Businesses: MUST emit Bearer challenge on identity_required
  (401) and insufficient_scope (403). RFC 9728 SHOULD bullet upgraded
  to reference /.well-known/oauth-protected-resource and integration
  with WWW-Authenticate.
* identity_required: full normative restructure (status code, header,
  body). realm MUST be issuer URI; error="invalid_token" when token
  present-but-bad; error SHOULD be omitted when no token (RFC 6750
  §3.1). resource_metadata SHOULD. continue_url retained for non-OAuth
  onboarding flows ONLY; explicit MUST NOT for pre-baked OAuth URLs.
* insufficient_scope: full normative restructure. realm + error +
  scope (full required set, not delta) MUST. resource_metadata SHOULD.
  Pre-baked OAuth continue_url removed entirely.
* Security Considerations: new "Authentication challenges" bullet.
  Platforms MUST drive flow from structured scope/error params;
  error_description is hint-only and MUST NOT control flow. realm MUST
  match issuer URI for cross-protection-space correlation.

* docs: add UCP and OAuth architecture explainer

  Document the architectural split between UCP and OAuth (RFC 8414)
  responsibilities that the rest of the spec relies on but never
  articulates. Anchors the four moving parts:

  * UCP config.scopes — hard gates (required auth)
  * OAuth scopes_supported — accepted scope vocabulary
  * Diff (scopes_supported ∖ config.scopes) — optional layer
  * UCP messages[] — runtime contextual hints (e.g., identity_optional)

* Three corrections to identity-linking.md following review of the WWW-Authenticate Bearer changes:

1. Add no-token identity_required example

The existing example only showed the token-present-but-expired case (error="invalid_token"). RFC 6750 §3.1 says error SHOULD be omitted when no token was presented — the more common case for a first request to a gated operation. Added a second labeled example for the no-token case so implementers aren't led to emit error="invalid_token" unconditionally.

2. Fix identity_optional section direction

The section intro described identity_optional as "a mechanism for the platform to inform the buyer." The direction was reversed — the business emits this code in its response; the platform receives it and may present it to the user. Corrected to accurately describe the emitter and receiver.

3. Simplify identity_optional to remove misleading description coupling

The section stated that per-scope description fields convey context for optional authentication, and that businesses SHOULD populate them when emitting identity_optional. This is incorrect: description is a field on scope_policy objects in config.scopes, which is the hard-gate (required) layer. Scopes relevant to identity_optional are by definition in the optional layer (scopes_supported ∖ config.scopes) and have no corresponding UCP schema field for descriptions. Removed the two-mechanisms paragraph and the SHOULD guidance on populating descriptions, as both described a mechanism that doesn't exist for optional-layer scopes. Upgraded identity_optional emission from MAY to SHOULD, since content on the message is the only available value prompt mechanism.

---------

Co-authored-by: Amit Handa <amithanda@google.com>
Co-authored-by: Ilya Grigorik <ilya@grigorik.com>

---------

Co-authored-by: James Thompson <thompson.tomo@outlook.com>
Co-authored-by: Guillaume V. <4216770+ptiper@users.noreply.github.com>
Co-authored-by: Dario Guzik <dario@guzik.com.ar>
Co-authored-by: Patrick R. Jordan <patrick.r.jordan@gmail.com>
Co-authored-by: James Andersen <james.j.andersen@gmail.com>
Co-authored-by: Nachiket Torwekar <nachiket.torwekar@gmail.com>
Co-authored-by: Dylan Koch <dkoch74@gmail.com>
Co-authored-by: Gauresh G Pai <107191770+gaureshpai@users.noreply.github.com>
Co-authored-by: Greg Smith <smithgrg@amazon.com>
Co-authored-by: Ilya Grigorik <ilya@grigorik.com>
Co-authored-by: Amit Handa <amithanda@google.com>
Co-authored-by: pemamian <pemamian@google.com>
Co-authored-by: Nicholas James Hall <55357993+nicholasjameshall@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

TC review Ready for TC review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants