docs: clarify UCP core release versioning contract - #614
Conversation
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.
jingyli
left a comment
There was a problem hiding this comment.
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?
- 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.
|
@jingyli great questions & flags, clarified in 7d8b56b — ptal.
Both paths are possible under our contract. If a net-new service is a backwards-compatible addition, then a newly approved vertical either:
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
left a comment
There was a problem hiding this comment.
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.
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.
|
@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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Almost all nits, one concern about our backporting stance for breaking changes. (#614 (comment))
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.
|
@raginpirate good nits! Updated: dac71fb. |
|
@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>
1e1dea0 to
f5581c8
Compare
amithanda
left a comment
There was a problem hiding this comment.
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!
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:D, either the current profile or asupported_versionsleaf whoseucp.versionmatches its map key.ucp.version = Dselects the UCP core snapshot published underD.D.requires.protocolandrequires.capabilitiesverify the selected versions; they do not select replacements.Transport descriptions remain versioned release artifacts. OpenAPI and OpenRPC
info.versionidentifies the artifact published underD; 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 thatdev.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:
supported_versions.Dalignment and version ownership.Compatibility
This is a contract clarification, not a wire migration.
Related Issues
Closes #499. Supersedes #418.
Checklist