Skip to content

Abilities API: Normalize required schema shape for REST responses#12013

Closed
gziolo wants to merge 5 commits into
WordPress:trunkfrom
gziolo:64955-abilities-rest-required-normalization
Closed

Abilities API: Normalize required schema shape for REST responses#12013
gziolo wants to merge 5 commits into
WordPress:trunkfrom
gziolo:64955-abilities-rest-required-normalization

Conversation

@gziolo
Copy link
Copy Markdown
Member

@gziolo gziolo commented May 29, 2026

Summary

Normalizes the required keyword in an ability's input and output schemas to its JSON Schema draft-04 form when those schemas are exposed through the REST API (wp-abilities/v1).

Background

Ability input and output schemas are a public contract. Clients, including the @wordpress/abilities JavaScript client, consume these schemas as standard JSON Schema and validate ability input and output against them. They therefore need a draft-04-conformant shape that standard validators understand.

On the server, WordPress accepts both forms supported by rest_validate_value_from_schema():

  • A draft-04 required array of property names on the object schema.
  • A draft-03 per-property required boolean.

Both forms validate server-side, but only the array form is valid JSON Schema draft-04. Ability schemas registered with per-property booleans, including examples shown for wp_register_ability(), were therefore exposed to clients in a shape that standard draft-04 validators do not recognize.

Changes

In WP_REST_Abilities_V1_List_Controller::prepare_schema_for_response():

  • Collects per-property required: true booleans and emits them as a parent required: [ ... ] array of property names.
  • Applies the conversion recursively, so nested object schemas each get their own required array.
  • Converts object schemas inside array items as well.
  • Strips required: false from property schemas without emitting an empty parent array.
  • Strips boolean required values that have no draft-04 equivalent, such as on scalar root schemas.
  • Mirrors rest_validate_object_value_from_schema() precedence: when an object already has a draft-04 required array, that array wins; per-property booleans on the same object are ignored for collection but still removed from the REST response.

The transformation only rewrites the REST response copy. Stored ability schemas and server-side validation behavior are unchanged.

Tests

Adds coverage for:

  • Top-level per-property boolean conversion.
  • Nested object conversion.
  • Object schemas nested inside array items.
  • required: false stripping.
  • Scalar-root boolean required stripping.
  • Mixed draft-04 array plus draft-03 booleans, confirming the draft-04 array takes precedence.
  • The underlying rest_validate_value_from_schema() precedence contract.

Trac ticket: https://core.trac.wordpress.org/ticket/64955

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 29, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props gziolo, westonruter, jorgefilipecosta.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@gziolo gziolo force-pushed the 64955-abilities-rest-required-normalization branch 2 times, most recently from 03946ca to 508a98f Compare May 29, 2026 19:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Normalizes Abilities API REST-exposed JSON Schemas so the required keyword is always represented in JSON Schema draft-04 form (parent required: [] array), even when abilities were registered using WordPress’s draft-03-style per-property required: true/false booleans. This keeps the REST schema contract compatible with standard JSON Schema validators consumed by clients.

Changes:

  • Add server-side schema-response normalization to convert per-property required booleans into a parent required array (and drop boolean-only cases with no draft-04 equivalent).
  • Preserve/merge existing draft-04 required arrays while de-duplicating entries.
  • Add PHPUnit coverage for top-level, nested object, array items object, mixed required forms, required: false, and scalar-root cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php Adds required normalization logic to prepare_schema_for_response() so REST responses conform to JSON Schema draft-04.
tests/phpunit/tests/rest-api/wpRestAbilitiesV1ListController.php Adds tests asserting correct conversion/stripping/merging behavior for required across multiple schema shapes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php Outdated
Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php Outdated
@gziolo gziolo self-assigned this Jun 1, 2026

// Collect draft-03 per-property `required: true` flags into a draft-04
// `required` array of property names on the parent object schema.
if ( isset( $schema['properties'] ) && is_array( $schema['properties'] ) ) {
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.

I wonder if REST is the right place for this transformation or if it is something that should be considered at the ability registration level. The advantage of being on the ability level, is that for things like wp_ai_client use abilities and MCP adapter the abilities would also have the draft-04 shape.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an open question to me as well. I shared more thoughts in https://core.trac.wordpress.org/ticket/64955 on the topic. I decided to start with an actual use case, which solves a real problem that developers might encounter when using the ability registered on the server through the newly introduced @wordpress/abilities package.

If you look at steps 5 and 6 outlined in https://core.trac.wordpress.org/ticket/64955#comment:1, I covered two possible pathways:

  • Step 5: Emit authoring guidance via _doing_it_wrong()
  • Step 6: Introduce schema_version for opt-in strict authoring

The original idea I had was that we eventually tap into the ability registration phase and first warn about schema incompatibilities for existing abilities while auto-correcting them (like in REST API now), and offer a way to enable strict validation that would prevent registration when the schema doesn't follow the spec.

If we are on board with that plan, then we can follow up with these strategies in separate PRs.

Copy link
Copy Markdown
Member

@jorgefilipecosta jorgefilipecosta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a comment for consideration but other than that it looks good.

Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php Outdated
Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php Outdated
Comment thread src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php Outdated
gziolo and others added 4 commits June 2, 2026 13:46
When exposing an ability's input and output schemas through the REST API, convert any draft-03 per-property `required` boolean into a draft-04 `required` array of property names on the parent object schema, and drop boolean `required` flags that have no draft-04 equivalent (such as on a scalar root schema).

WordPress accepts both forms internally, but only the array form is valid JSON Schema draft-04, so clients consuming `wp-abilities/v1` now receive a conformant schema. The transformation only affects the REST response copy; server-side validation continues to use the stored schema via validate_input().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
When a draft-04 `required` array is already present on an object schema,
`rest_validate_object_value_from_schema()` validates only that array and
ignores any draft-03 per-property `required` booleans. Align
`prepare_schema_for_response()` with that precedence so the published
schema describes exactly what gets enforced instead of merging the two
forms (which over-stated requirements for mixed schemas).

Also clear the dangling `foreach` reference after collecting flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin the contract that `rest_validate_object_value_from_schema()` validates
only the draft-04 `required` array when one is present on an object node,
ignoring per-property `required` booleans at the same level. Existing
coverage exercised each form independently and mixed them across nesting
levels, but never on a single node — the case the Abilities REST schema
normalization relies on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gziolo gziolo force-pushed the 64955-abilities-rest-required-normalization branch from 9387993 to bd946f7 Compare June 2, 2026 11:48
Drop the now-unnecessary deduplication and reindexing when building the
draft-04 `required` array. Property keys are unique, so the collected
list needs no `array_unique()`/`array_values()`, and when a draft-04
`required` array is already present it is left untouched rather than
rebuilt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gziolo
Copy link
Copy Markdown
Member Author

gziolo commented Jun 2, 2026

A commit was made that references the Trac ticket included in the description of this pull request.

SVN changeset: 62449
GitHub commit: 63bf1d9

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@gziolo gziolo closed this Jun 2, 2026
@gziolo gziolo deleted the 64955-abilities-rest-required-normalization branch June 2, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants