feat: add response-carried request constraints - #655
Open
igrigorik wants to merge 3 commits into
Open
Conversation
UCP registries are reverse-DNS keyed JSON maps, and JSON object member order is not a protocol contract: RFC 8785 (JCS) canonicalization, used by UCP signing, sorts object member names. Businesses have no reliable way to declare preferred traversal order for registry keys, e.g. payment handler presentation order (#170, design discussion in #525). The model: the member name `ucp` is reserved at every object scope as the protocol namespace. The top-level envelope is the root manifestation of that reservation, not a special wrapper. The namespace is ambient: any object scope MAY carry a `ucp` member, domain schemas never declare it (it is document grammar, like the reservation itself), and its contents are defined exclusively by the vocabulary registered in ucp.json#/$defs/members. Consumers process the members they recognize and ignore the rest; a member is admitted to the vocabulary only if it is safe to ignore, so no member can be load-bearing for correctness. One exception: an object closed with additionalProperties:false must declare the optional `ucp` property explicitly. Future members register once and work at every scope immediately -- request constraints (#655) can rebase onto this vocabulary. The first member, map_order, declares key-traversal order for sibling map-valued fields, carried in an array because JCS preserves array element order. Partial lists are valid, unlisted keys remain valid and follow, and the list is not an allowlist. At the root envelope it orders the registries beside it: "ucp": { "payment_handlers": { "com.google.pay": [ ... ], "dev.shopify.shop_pay": [ ... ] }, "map_order": { "payment_handlers": ["dev.shopify.shop_pay", "com.google.pay"] } } At any deeper scope the same member rides the ambient `ucp` member, with no schema change to the host object -- e.g. ordering an identity provider registry inside a capability config: "config": { "providers": { "app.example.login": [ ... ], "com.google": [ ... ] }, "ucp": { "map_order": { "providers": ["app.example.login", "com.google"] } } } Refs #525, #170
5 tasks
Static request schemas describe the inputs a Business accepts generally, but cannot express narrower requirements for specific transaction. Platforms therefore cannot know before submission that a negotiated quantity is fixed or that an otherwise optional field is required. Add response-only `$requestConstraints` as a bounded Draft 2020-12 fragment that a Business emits and enforces against the corresponding later request representation. A Platform may use the fragment when forming or validating input, or ignore it and rely on the Business's existing validation errors. Keep the structural member ambient rather than adding an ordinary property to every carrier schema. Each adopting contract defines correspondence and lifecycle, while the shared type closes the vocabulary to `required`, `properties`, `enum`, and `const`, with optional display text and `$comment`. Examples demonstrate a Cart Line Item fixed at quantity 100, preservation of the company-scoped `ACME-X7Q9-L2M4` discount code, and a submitted card instrument that requires `billing_address`.
Static request schemas cannot express transaction-specific narrowing, forcing Platforms to discover negotiated requirements only after submission fails. Register response-only `ucp.request_constraints` in the central protocol vocabulary and define a closed Draft 2020-12 fragment for object presence and value constraints. Attach constraints through the containing `ucp` scope so host schemas do not need carrier-specific declarations. Keep Platform processing optional and Business validation authoritative, preserving the namespace requirement that registered members remain safe to ignore.
igrigorik
force-pushed
the
feat/request-constraints
branch
from
July 31, 2026 18:56
7521e4d to
3105ad5
Compare
igrigorik
marked this pull request as ready for review
July 31, 2026 18:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #657.
Static request schemas describe the inputs a Business generally accepts, but not narrower requirements for a particular transaction. Without response-time constraints, a Platform learns that a negotiated quantity is fixed or an otherwise optional field is required only after submission fails—or through domain-specific constraint formats.
PR #657 establishes the ambient
ucpprotocol namespace and central member registry. This PR addsrequest_constraintsto that vocabulary instead of inventing a parallel structural namespace.ucp.request_constraintsinucp.json#/$defs/members.required,properties,enum, andconst.allOfwithout schema merging.ucpscope to identify the constrained parent object; adopting contracts define correspondence and lifecycle when existing operation semantics do not.Examples
A Cart Line Item fixed at quantity
100:{ "id": "line_123", "quantity": 100, "ucp": { "request_constraints": { "properties": { "quantity": {"const": 100} } } } }A submitted card instrument that requires a billing address for this transaction:
{ "type": "card", "ucp": { "request_constraints": { "required": ["billing_address"] } } }Boundaries
This PR does not define payment availability, custom assertion keywords, schema merging, or new outcomes and error codes. Payment-specific correspondence and availability remain downstream.