Skip to content

Ig/anyof review suggestions - #760

Merged
raginpirate merged 3 commits into
raginpirate/anyof-constraintfrom
ig/anyof-review-suggestions
Aug 21, 2026
Merged

Ig/anyof review suggestions#760
raginpirate merged 3 commits into
raginpirate/anyof-constraintfrom
ig/anyof-review-suggestions

Conversation

@igrigorik

@igrigorik igrigorik commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Nits/suggestions for #757.

docs: complete abridged payment instrument examples — Two complete examples elided id, handler_id, and type behind a // ... more instrument required field comment, and one also omitted credential.type. They only validate because shipped ucp-schema never applies the self-root allOf requirements in selected_payment_instrument, so payment.instruments[] is never checked against payment_instrument's required members. ucp-schema#44 fixes that; built against its head, this branch's base reports 316 passed, 2 failed at exactly these two lines. Completing them costs one line each.

feat: require non-empty anyOf branches in the constraint grammar — An empty Object Constraint is a valid no-op everywhere else, but an empty anyOf branch is satisfied by every object, so it neutralizes the alternation rather than doing nothing. Adds minProperties: 1 as a sibling to the recursive $ref in both grammar schemas (legal 2020-12 sibling composition, applies at every nesting depth) and mirrors the constraint into both grammar tables.

docs: discriminate the anyOf credential example — The example's undiscriminated form does not enforce what the surrounding prose claims: {"card_number_type": "anything", "cvc": "1"} satisfies the cvc branch. Pins card_number_type with const in each branch and names it in that branch's required, matching the idiom #424 already uses in payment/guide.md. Uses the existing closed enum in card_credential.json, so it introduces no dependency on #424's new credential types.

Also documents the consequence that pinning creates — because every branch pins the discriminator, the branch set closes the accepted values, so a dpan credential valid under card_credential.json satisfies neither branch — and replaces the "would restate the path" justification. Request Constraints conjoin, so two separately targeted constraints would require both cvc and cryptogram; the real alternative is discriminating in the path filter, which puts conditional logic in the selector.

Both examples elided id, handler_id, and type behind a "more required
fields" comment, and one also omitted credential.type. They validated
only because shipped ucp-schema never applied the self-root allOf
requirements in selected_payment_instrument, so payment.instruments[]
was never checked against payment_instrument's required members.

Showing the fields costs one line each, keeps both examples validated
once that resolver bug is fixed, and removes a comment that promised
required fields while showing none.
An empty Object Constraint is a valid no-op at every other position, but
an empty anyOf branch is satisfied by every object, so it neutralizes
the alternation rather than doing nothing. The grammar should not admit
a branch that silently disables the alternation it belongs to.

A minProperties sibling on the recursive $ref is legal 2020-12
composition: siblings to $ref apply alongside the referenced schema, so
the branch stays an ordinary Object Constraint while gaining the
non-empty requirement, at every nesting depth.

Mirrors the constraint into both grammar tables, since the schema and
its companion prose are one unit.
The undiscriminated form accepts nonsense: a credential declaring any
card_number_type at all satisfies the cvc branch as long as it carries a
cvc, so the example teaches an idiom that does not enforce what the
surrounding prose claims. Pinning card_number_type with const in each
branch, and naming it in that branch's required, makes a branch match
only the shape it describes.

Documents the consequence that pinning creates: because every branch
pins the discriminator, the branch set also closes the accepted values,
so a dpan credential valid under card_credential.json satisfies neither
branch. That closure is the rule a per-branch conditional cannot
express, and it is load-bearing enough to state.

Replaces the "would restate the path" justification, which understated
the case. Request Constraints conjoin, so two separately targeted
constraints would require both cvc and cryptogram; the real alternative
is discriminating in the path filter, which puts conditional logic in
the selector.

Adds the two grammar mechanics the example relies on next to the
composition rules rather than in the Business guidelines: branches are
alternatives rather than a partition, and properties asserts nothing
about an absent member. Both are consequences of how the grammar
composes, not conformance requirements, so they carry no BCP 14
keywords and belong with the grammar they describe.
@igrigorik igrigorik added this to the 2026-08-24 milestone Aug 21, 2026

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

Good improvements!

@raginpirate
raginpirate merged commit 6a74941 into raginpirate/anyof-constraint Aug 21, 2026
4 of 7 checks passed
@raginpirate
raginpirate deleted the ig/anyof-review-suggestions branch August 21, 2026 16:39
raginpirate pushed a commit that referenced this pull request Aug 21, 2026
* docs: complete abridged payment instrument examples

Both examples elided id, handler_id, and type behind a "more required
fields" comment, and one also omitted credential.type. They validated
only because shipped ucp-schema never applied the self-root allOf
requirements in selected_payment_instrument, so payment.instruments[]
was never checked against payment_instrument's required members.

Showing the fields costs one line each, keeps both examples validated
once that resolver bug is fixed, and removes a comment that promised
required fields while showing none.

* feat: require non-empty anyOf branches in the constraint grammar

An empty Object Constraint is a valid no-op at every other position, but
an empty anyOf branch is satisfied by every object, so it neutralizes
the alternation rather than doing nothing. The grammar should not admit
a branch that silently disables the alternation it belongs to.

A minProperties sibling on the recursive $ref is legal 2020-12
composition: siblings to $ref apply alongside the referenced schema, so
the branch stays an ordinary Object Constraint while gaining the
non-empty requirement, at every nesting depth.

Mirrors the constraint into both grammar tables, since the schema and
its companion prose are one unit.

* docs: discriminate the anyOf credential example

The undiscriminated form accepts nonsense: a credential declaring any
card_number_type at all satisfies the cvc branch as long as it carries a
cvc, so the example teaches an idiom that does not enforce what the
surrounding prose claims. Pinning card_number_type with const in each
branch, and naming it in that branch's required, makes a branch match
only the shape it describes.

Documents the consequence that pinning creates: because every branch
pins the discriminator, the branch set also closes the accepted values,
so a dpan credential valid under card_credential.json satisfies neither
branch. That closure is the rule a per-branch conditional cannot
express, and it is load-bearing enough to state.

Replaces the "would restate the path" justification, which understated
the case. Request Constraints conjoin, so two separately targeted
constraints would require both cvc and cryptogram; the real alternative
is discriminating in the path filter, which puts conditional logic in
the selector.

Adds the two grammar mechanics the example relies on next to the
composition rules rather than in the Business guidelines: branches are
alternatives rather than a partition, and properties asserts nothing
about an absent member. Both are consequences of how the grammar
composes, not conformance requirements, so they carry no BCP 14
keywords and belong with the grammar they describe.
@waynemak waynemak added the area:payments Issues and pull requests related to the Payments vertical label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:payments Issues and pull requests related to the Payments vertical

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants