Skip to content

docs: clarify UCP core release versioning contract#614

Draft
igrigorik wants to merge 1 commit into
mainfrom
docs/versioning-contract
Draft

docs: clarify UCP core release versioning contract#614
igrigorik wants to merge 1 commit into
mainfrom
docs/versioning-contract

Conversation

@igrigorik

Copy link
Copy Markdown
Contributor

UCP intentionally gave services, capabilities, and extensions their own version fields so they could evolve independently from ucp.version. The specification described that flexibility as part of the protocol model.

In practice, UCP's publishing and certification process settled on an atomic release contract: each dated core release ships its services and transport bindings, capabilities, extensions, and shared schemas as one tested snapshot. This PR codifies that current contract rather than continuing to imply independent release schedules that UCP does not currently exercise.

The release contract

For a selected release D:

  1. The Platform resolves one exact Business profile for D, either the current profile or a supported_versions leaf whose ucp.version matches its map key.
  2. ucp.version = D selects the UCP core snapshot published under D.
  3. Every UCP-authored service, capability, and extension declaration in that profile carries D.
  4. Exact capability intersection selects the active capabilities and extensions from the chosen snapshot.
  5. requires.protocol and requires.capabilities verify the selected versions; they do not select replacements.
Business profiles
  ├── current profile
  └── supported_versions[D]
                │
                ▼
       exact profile for D
                │
                ▼
       UCP core snapshot D
                │
                ▼
   exact capability intersection
                │
                ▼
       verify requires ranges

Transport descriptions remain versioned release artifacts. OpenAPI and OpenRPC info.version identifies the artifact published under D; it is not another profile negotiation axis. This does not prevent transport specifications from evolving—it defines how UCP currently bundles them into a core release.

The individual version fields remain in place. They preserve the existing wire shape and leave room for UCP to define independently versioned core components in a future contract without requiring a profile migration.

Independent contracts

Third-party extensions and payment handlers retain their author-controlled versions and release cadence. The build hook now stamps the release version only onto UCP-authored dev.ucp.* schemas, making that ownership boundary explicit without changing current generated output. The PR also clarifies that dev.ucp.* capabilities are governed by the UCP Tech Council responsible for their domain.

Documentation (re)structure

To avoid repeating subtly different rules across the documentation:

  • Protocol Version owns exact profile discovery and supported_versions.
  • Capability Versions owns exact capability intersection.
  • Component Versioning and Release Snapshots owns release-D alignment and version ownership.
  • Versioning owns the release-branch and backport workflow.
  • Core Concepts and Schema Authoring summarize the relevant rules and link to their normative definitions.

Compatibility

This is a contract clarification, not a wire migration.

  • No profile shape changes.
  • No fields added or removed.
  • No required-field changes.
  • No payload-validation changes.
  • No change to current generated artifacts.
  • Schema changes are descriptions only.
  • Existing illustrative names, URLs, versions, and placeholders are unchanged.

Related Issues

Closes #499. Supersedes #418.


Checklist

  • Documentation: Updates to README, or documentations regarding schema or capabilities.
  • Infrastructure: CI/CD, Linters, or build scripts.
  • I have followed the Contributing Guide
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.

UCP intentionally gave services, capabilities, and extensions their own
version fields so they could evolve independently from ucp.version. The
specification described that flexibility as part of the protocol model.

In practice, UCP's publishing and certification process settled on an
atomic release contract: each dated core release ships its services and
transport bindings, capabilities, extensions, and shared schemas as one
tested snapshot. This commit codifies that current contract rather than
continuing to imply independent release schedules that UCP does not
currently exercise.

For a selected release D:

1. The Platform resolves one exact Business profile for D, either the
   current profile or a supported_versions leaf whose ucp.version matches
   its map key.
2. ucp.version D selects the UCP core snapshot published under D.
3. Every UCP-authored service, capability, and extension declaration in
   that profile carries D. Because services[name][] pairs one service with
   one transport binding, each binding entry repeats the service version.
4. Exact capability intersection selects the active capabilities and
   extensions from the chosen snapshot.
5. requires.protocol and requires.capabilities verify the selected
   versions; they do not select replacements.

Transport descriptions remain versioned release artifacts. OpenAPI and
OpenRPC info.version identifies the artifact published under D, while the
version repeated in a service entry identifies the service. Under the
current atomic contract, a transport binding does not introduce a separate
negotiated version.

Retain the individual version fields. They preserve the existing wire
shape and leave room for UCP to define independently versioned core
components in a future contract without requiring a profile migration.
Today, those fields explicitly record the version of the selected
snapshot rather than creating independent release axes.

Keep independently authored contracts independent. Third-party extensions
and payment handlers retain their author-controlled versions and release
cadence. Scope build-time version injection to dev.ucp.* schemas so this
ownership boundary is explicit without changing current generated output.

Give each part of the model one authoritative home: Protocol Version owns
exact profile discovery, Capability Versions owns exact intersection,
Component Versioning and Release Snapshots owns D alignment, and the
versioning guide owns release maintenance. Approved backwards-compatible
changes may be backported and the snapshot re-certified; breaking changes
enter a new release.

This is a contract clarification, not a wire migration. It changes no
profile shape, required field, payload validation, or current generated
artifact; the schema edits are descriptions only. It also clarifies that
dev.ucp.* capabilities are governed by the UCP Tech Council responsible
for their domain.
@igrigorik igrigorik added this to the Working Draft milestone Jul 20, 2026
@igrigorik igrigorik self-assigned this Jul 20, 2026
@igrigorik igrigorik added the TC review Ready for TC review label Jul 20, 2026

@jingyli jingyli 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.

Thanks for proposing the clarification! Given this PR, it would also significantly simplify version's correlation to UCP's releases (related RFC - #499), especially if the releases are going to be done at a fixed/regular cadence.

But curious if we have some early thoughts around: How will the first release for new services look like? Let's hypothetically say a new service (i.e. food ordering) has an approved spec tomorrow, will they need to wait until the next official release of UCP to stamp a version D on their capabilities? Or will these be considered as backwards-compatible changes (since they are net new capabilities) and be backported into a previous stable release?

Comment thread docs/versioning.md
Comment on lines +15 to +16
it, and UCP re-certifies the snapshot before publishing the updated artifacts.
Breaking changes are never backported; they enter the next release.

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.

Trying to pressure test this statement here: I do agree with it for almost all of the cases we will face but do we want to give us some break-glass mechanism to override this (subject to Governance Council's discretion on a case-by-case evaluation) when there is a sensitive breaking fix that needs to be backported into a previous release?

Example from the v2026-04-08 release: #441

capabilities and extensions are still selected by exact-version intersection (see
[Capability Versions](#capability-versions)).

#### Third-party extensions (`com.{vendor}.*`, `org.{org}.*`)

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.

With this new language, we are now limiting the scope of third-party/vendor's participation to just extensions (vs. before it was generalized to capabilities). Is this reduction in vendor scope intentional?

In my mind, the main pros and cons will be:

  • (+): Tightening the usage so that we ensure UCP prescribes the main building blocks (base capabilities) and anything vendors introduce is only supplemental to these main building blocks
  • (-): Removing the flexibility for implementors to "mix-and-match" - i.e. businesses & platforms cannot mutually agree to negotiate for a vendor catalog capability and UCP's checkout capability

My gut feeling is that the con above will eliminate some interesting use cases - especially for base capabilities that UCP has not yet defined (i.e. if a vendor wants to temporarily lean on their own identity management capability before some flavour of #337 lands in UCP).

intersection algorithm considers only capability versions supported
by both parties.
Capability compatibility is established only by exact version equality, not by
inferring compatibility from date order. Each capability in the profile is an

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.

Trying to visualize how this works in practice, especially cross-referencing against our new guidance on UCP-authored releases (please correct me if anything deviates from how you are thinking about it):

Scenario 1 - valid ✅

{
  "ucp": {
    "version": "2026-04-08",
    "supported_versions": {
      "2026-01-23": "https://business.example.com/.well-known/ucp/2026-01-23"
    }
    "services": {...},
    "capabilities": {
      "dev.ucp.shopping.checkout": [
        {
          "version": "2026-04-08",
          "spec": "https://ucp.dev/2026-04-08/specification/checkout",
          "schema": "https://ucp.dev/2026-04-08/schemas/shopping/checkout.json"
        }
      ],
    },
   ....
}

Scenario 2 - valid ✅

{
  "ucp": {
    "version": "2026-04-08",
    "supported_versions": {
      "2026-01-23": "https://business.example.com/.well-known/ucp/2026-01-23"
    }
    "services": {...},
    "capabilities": {
      "dev.ucp.shopping.checkout": [
        {
          "version": "2026-04-08",
          "spec": "https://ucp.dev/2026-04-08/specification/checkout",
          "schema": "https://ucp.dev/2026-04-08/schemas/shopping/checkout.json"
        },
        {
          "version": "2026-01-23",
          "spec": "https://ucp.dev/2026-01-23/specification/checkout",
          "schema": "https://ucp.dev/2026-01-23/schemas/shopping/checkout.json"
        }
      ],
    },
   ....
}

Scenario 3 - invalid ❌

{
  "ucp": {
    "version": "2026-04-08",
    "supported_versions": {
      "2026-01-23": "https://business.example.com/.well-known/ucp/2026-01-23"
    }
    "services": {...},
    "capabilities": {
      "dev.ucp.shopping.cart": [
        {
          "version": "2026-04-08",
          "spec": "https://ucp.dev/2026-04-08/specification/cart",
          "schema": "https://ucp.dev/2026-04-08/schemas/shopping/cart.json"
        }
      ],
      "dev.ucp.shopping.checkout": [
        {
          "version": "2026-01-23",
          "spec": "https://ucp.dev/2026-01-23/specification/checkout",
          "schema": "https://ucp.dev/2026-01-23/schemas/shopping/checkout.json"
        }
      ],
    },
   ....
}

Or is it the case that Scenario 3 would also be valid - i.e. the sentence UCP-authored dev.ucp.* entries declare version D in release D only applies to the release, and not on how the profile can be built?

4. Otherwise: the business does not support the platform's protocol
2. If the Platform's protocol version matches `version`, it uses this
profile directly and proceeds to capability negotiation.
3. If the Platform's protocol version is a key in

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.

Trying to poke this sentence a bit more, especially approaching it from the angle where platforms may also have multiple supported protocol versions: what this is indicating is that negotiation's scope is limited to that mapped URI being fetched (and there should ever be only one profile being "selected" for negotiation) and there should be no cross-profile "merging" happening. <- is this correct?

This would mean let's say if a business advertises cart capability only in their v2026-04-08 profile (which has supported_versions containing v2026-01-23) and checkout capability in their v2026-01-23 profile, then from platform's perspective (if both versions are supported), only cart or checkout capability can be negotiated (and not both via running intersection algorithms against both profiles)?

The only way to achieve negotiating both capabilities would be to have both capabilities (cart on 2026-04-08 version and checkout on 2026-01-23 version) advertised in the same v2026-04-08 profile?

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.

[RFC]: Refactor of codebase to support capability versioning independent of protocol version

2 participants