Skip to content

docs: clarify UCP core release versioning contract - #614

Open
igrigorik wants to merge 12 commits into
mainfrom
docs/versioning-contract
Open

docs: clarify UCP core release versioning contract#614
igrigorik wants to merge 12 commits 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 Outdated
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md Outdated
   - Breaking backports: next release by default, but the Governance
     Council MAY approve an exceptional backport to a supported release,
     following the breaking-change notice process and re-certification.

   - Consolidate backport policy into Component Versioning and Release
     Snapshots; Backwards Compatibility returns to pure classification
     and versioning.md keeps workflow mechanics with a pointer. Also
     removes the "never as a backport" sentence the escape hatch would
     have contradicted.

   - Mixed-version profiles: a profile for ucp.version D advertises
     exactly D for dev.ucp.* entries; older versions live only in
     supported_versions leaf profiles. The registry mechanism supports
     multi-version arrays, but the core contract exercises them only for
     third-party extensions. ucp-schema lint queued to enforce this.

   - Cross-profile negotiation: Platforms and Businesses MUST negotiate
     using exactly one selected profile and MUST NOT combine capabilities
     across profiles.

   - Third-party section reworded to versioning-only claims; whether
     vendors may define root capabilities is deferred to its own RFC.
@igrigorik

Copy link
Copy Markdown
Contributor Author

@jingyli great questions & flags, clarified in 7d8b56b — ptal.

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?

Both paths are possible under our contract. If a net-new service is a backwards-compatible addition, then a newly approved vertical either:

  1. ships in the next release D2 and declares D2 (default), or
  2. if approved for backport, lands on the maintained release/D branch, declares D, and the snapshot is re-certified.

Practically, I would push us to synchronize releases and snap to common dates rather than cut per-vertical releases — the shared date is what guarantees a common core and cross-service interop. Today that cadence is quarterly (targeting ~12 weeks), so in your example food ordering slots into the next release train rather than waiting indefinitely. The cadence itself is release-process policy, not codified in the spec -- the GC can choose to adjust the schedule.

@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 @igrigorik for the updates in 7d8b56b!

We added one proposal (https://github.com/Universal-Commerce-Protocol/ucp/pull/614/changes#r3694051647) to provide platforms, who may support multiple versions, flexibility to select which business profile they would like to negotiate against and not always be forced into choosing the "current" version under the current wording - @sinhanurag @gsmith85 FYI

Also ack on #614 (comment), personally I prefer the backporting route to minimize having to wait for D2, especially in the rare instances where a vertical may have just missed the release train.

Comment thread docs/specification/overview.md Outdated
   Step 2 now states that the Platform selects one mutually supported
   version from the Business's version and supported_versions keys, with a
   SHOULD-prefer-most-recent to keep the ecosystem moving forward —
   mirroring how capability intersection selects the latest shared date.

   Steps 3 and 4 follow from the selected version instead of "the
   Platform's protocol version", which is ambiguous once platforms support
   several. Selection remains exact and single-profile: this changes which
   profile may be chosen, not how many.
@igrigorik

Copy link
Copy Markdown
Contributor Author

@jingyli landed, ready for final pass!

   A selected profile guarantees compatibility among its declared
   capabilities at its declared version; resource representations may
   differ across versions. Platforms MAY run separate negotiations with
   the same Business at different supported versions, each bound to a
   single profile.

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

LGTM! Gave it one last read through, it looks great. Thanks @igrigorik for formalizing the core release snapshot contract. This provides clear alignment between ucp.version, profile discovery, and component artifacts.

@raginpirate raginpirate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost all nits, one concern about our backporting stance for breaking changes. (#614 (comment))

Comment thread docs/documentation/core-concepts.md Outdated
Comment thread docs/documentation/core-concepts.md Outdated
Comment thread docs/documentation/core-concepts.md Outdated
Comment thread docs/documentation/core-concepts.md Outdated
Comment thread docs/documentation/schema-authoring.md
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md Outdated
Comment thread docs/specification/overview.md Outdated
   Address review nits: reword core-concepts so dev.ucp.* components are
   versioned in lockstep with the specification — each declares the date D
   of the release it ships in — tighten the service-version sentence, and
   drop prose that duplicated the normative overview.

   Scope the Governance Council breaking-backport exception to defects
   that compromise the security, correctness, or interoperability of the
   release as published: releases are stable unless broken as shipped,
   rather than open to indefinite amendment. State the payment-handler
   clause as ownership-based — UCP defines the declaration structure but
   currently no handler of its own, so no handler version is constrained
   to D.
@igrigorik

Copy link
Copy Markdown
Contributor Author

@raginpirate good nits! Updated: dac71fb.

@raginpirate raginpirate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

Comment thread docs/documentation/core-concepts.md Outdated
Comment thread docs/specification/overview.md
@igrigorik

Copy link
Copy Markdown
Contributor Author

@amithanda good catch on both comments — working through them surfaced a few related gotchas (notice-process ordering for security fixes, hosting policy not covering schema artifacts), landed updated wording in 1e1dea06.

   Amend the release contract per review of the backport clause: a dated
   release works like a long-term-support channel whose snapshot is
   amended in place, so document the resulting cache skew and why it is
   safe by construction — backports default to the backwards-compatible
   class, leaving earlier copies behind but never wrong. Runtime fetchers
   follow standard HTTP caching semantics, so refresh frequency stays
   publisher-controlled; build-time consumers converge on their own
   cadence, with exceptional breaking backports announced and enforced at
   request time.

   Unify hosting policy: HTTPS and the Cache-Control floor now cover
   schemas and transport descriptions alongside profiles, with validators
   recommended for efficient revalidation. Add the consumer-side pairing
   for the publisher snapshot rule: a dev.ucp.* entry whose version
   differs from the profile's ucp.version is rejected as not present. Add
   the security exception to the breaking-change notice process: the
   Governance Council sets disclosure timing, so fixes are not announced
   before they are safe to disclose.

Co-authored-by: Amit Handa <amithanda@google.com>
@igrigorik
igrigorik force-pushed the docs/versioning-contract branch from 1e1dea0 to f5581c8 Compare August 7, 2026 16:02

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

f5581c8 covers both of my comments well. The skew paragraph states the part I
was after, that D is amended in place and earlier copies stay valid, and
grounding refresh behavior in standard HTTP caching plus the new
ETag/Last-Modified validator is a better answer than the one I proposed, it
makes skew self-correcting for runtime fetchers rather than merely documented.
Extending Hosting to cover schema and transport-description artifacts closes the
gap that made skew undetectable in the first place.

The consumer-side rejection rule reads right, and keeping it entity-scoped means
one malformed entry does not invalidate the full profile.

Approving!

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

7 participants