fix: stop rewriting a required that is not an array of strings - #78
Open
vishkaty wants to merge 1 commit into
Open
fix: stop rewriting a required that is not an array of strings#78vishkaty wants to merge 1 commit into
vishkaty wants to merge 1 commit into
Conversation
`resolve_object` seeded its required list with
map.get("required").and_then(|v| v.as_array()).….unwrap_or_default()
so any `required` that is not an array produced an empty list, and the emit
branch below then wrote `"required": []` back out because the key had been
present. The authored value was discarded silently.
Two consequences.
A schema whose `required` is a string resolved to one whose `required` is
empty, so the constraint disappeared. `validate` then reported an instance
missing that field as valid and exited 0, where the pinned jsonschema crate
would have refused the schema outright. With the authored value preserved the
validator sees it, reports `invalid schema`, and the run exits 2.
OpenAPI and OpenRPC parameter objects carry a boolean `required`. Those
documents are resolved too, and the flag was being rewritten to `[]`, which
means nothing at that position. It now survives. This accounts for the
resolved output changing on six service definitions and on
schemas/shopping/fulfillment.json, whose embedded method parameters carry the
same boolean. Every one of those changes restores the authored value.
An element that is not a string is treated the same way, since a partial
rewrite would drop it just as quietly.
Testing. Three resolver cases and one CLI case, each failing before this change
and passing after. Full suite 358 passed, from 354 before. cargo fmt and clippy
clean. Resolving every schema and service definition in the specification at
release/2026-08-25 in both directions, 246 comparisons, changes 16 outputs and
errors on none. The specification CI gates are unchanged against a control:
`ucp-schema lint source/` 124 files all passed, and validate_examples 343
passed 0 failed 50 skipped, identical with and without this change.
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.
Observed
A schema whose
requiredis a string rather than an array resolves to one whoserequiredis empty, and validation of an instance missing that field thensucceeds:
The pinned
jsonschemacrate would have refused that schema:validator_forreturns
"name" is not of type "array". The constraint is removed before itgets there.
Root cause
resolve_objectseeds its required list withso a
requiredof any other shape yields an empty list, and the emit branchwrites
"required": []back out because the key was present. The authoredvalue is discarded with no diagnostic.
Change
requiredis rewritten only when it is well formed, meaning an array ofstrings. Anything else is passed through untouched, so the validator sees the
authored value and can reject the schema. An element that is not a string is
treated the same way, since a partial rewrite would drop it just as quietly.
A second thing this fixes
OpenAPI and OpenRPC parameter objects carry a boolean
required. Thosedocuments are resolved too, and the flag was being rewritten to
[], whichmeans nothing at that position. For example
schemas/shopping/fulfillment.jsonauthors{ "name": "checkout", "required": true, "schema": { "$ref": "checkout.json" } }and resolution turned that into
"required": []. It now survives.Testing
Three resolver cases and one CLI case, each failing before this change and
passing after. Full suite 358 passed, from 354.
cargo fmt --checkandcargo clippy --all-targetsare clean.After the change,
validateon the schema above reportsinvalid schema: "name" is not of type "array"and exits 2, while a wellformed schema with a genuinely missing field still exits 1. The two failure
modes stay distinguishable.
Blast radius, measured
Resolving every schema and service definition in the specification at
release/2026-08-25, in both directions, is 246 comparisons. 16 outputschange and none errors. Every change restores an authored value: six service
definitions and
schemas/shopping/fulfillment.json, all of them the booleanrequireddescribed above. No schema in that tree has a malformedrequiredkeyword, so nothing else moves.
The specification CI gates are unchanged, checked against a control binary:
ucp-schema lint source/scripts/validate_examples.pyRelated, not addressed here
lintdoes not validate a schema document against the dialect its own$schemadeclares, which is why the malformed schema above passes linting inthe first place. That is a separate change and I have not bundled it. Happy to
follow up if it is wanted.
Four open PRs touch
resolver.rs(#48, #58, #62, #77). None of them touches therequired seeding or emit logic, so there is no overlap in substance, though
#77 and this may need a trivial rebase against each other.