Skip to content

Allow anyOf and an open expansion of schema overlays ontop of 655 - #705

Open
raginpirate wants to merge 4 commits into
feat/request-constraintsfrom
raginpirate/open-655
Open

Allow anyOf and an open expansion of schema overlays ontop of 655#705
raginpirate wants to merge 4 commits into
feat/request-constraintsfrom
raginpirate/open-655

Conversation

@raginpirate

@raginpirate raginpirate commented Aug 10, 2026

Copy link
Copy Markdown
Member

Description

A small tweak over #655 which makes it extensible for payment handlers to drop-in schema expansions of ucp.request_constraints.

With this... I think we have a great final answer:
Shape example with payments: https://gist.github.com/raginpirate/29a5f929d6eb6d5686d6c081560e030b

Net:

│ │ #655 │ synthesis │
│ malformed declared keywords │ catches │ catches │
│ unrecognised keys │ rejects │ accepts — needs a lint
│ discrimination / branching. │ impossible │ native │
│ per-variant narrowing │ impossible │ native
│ handler domain keys │ impossible │ native
│ the #288 use case │ unsatisfiable │ works

igrigorik and others added 4 commits July 31, 2026 11:07
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
   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.
@raginpirate
raginpirate requested a review from igrigorik August 10, 2026 18:28
@raginpirate raginpirate self-assigned this Aug 10, 2026
@damaz91 damaz91 added status:needs-triage Signal that the PR is ready for human triage status:under-review gov:needs-tc-review and removed status:needs-triage Signal that the PR is ready for human triage labels Aug 10, 2026
@igrigorik
igrigorik force-pushed the feat/request-constraints branch from 3105ad5 to c7698c9 Compare August 12, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants