Commit 59a8672900 for wordpress.org

commit 59a8672900c1df7174e56310d312d5d1064b6cc6
Author: gziolo <gziolo@git.wordpress.org>
Date:   Fri Oct 23 07:24:54 2026 +0000

    Abilities API: Improve inline documentation

    Clarify required capabilities and input-dependent permission checks,
    schema handling, callbacks, and execution hooks. Correct registration
    examples and naming constraints, and document discovery filtering
    and preservation of ability keys.

    Developed in https://github.com/WordPress/wordpress-develop/pull/14550.

    Props gziolo, jorgefilipecosta.
    See #66325, #65569.



    Built from https://develop.svn.wordpress.org/trunk@64685


    git-svn-id: http://core.svn.wordpress.org/trunk@63835 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/abilities-api.php b/wp-includes/abilities-api.php
index 36c1af8a07..764261d331 100644
--- a/wp-includes/abilities-api.php
+++ b/wp-includes/abilities-api.php
@@ -22,7 +22,7 @@
  * Abilities must be registered on the `wp_abilities_api_init` action hook.
  * Attempting to register an ability outside of this hook will fail and
  * trigger a `_doing_it_wrong()` notice.
-
+ *
  * Example:
  *
  *     function my_plugin_register_abilities(): void {
@@ -38,12 +38,11 @@
  *                     'type'        => 'string',
  *                     'enum'        => array( 'subscriber', 'contributor', 'author', 'editor', 'administrator' ),
  *                     'description' => __( 'Limits the export to users with this role.', 'my-plugin' ),
- *                     'required'    => false,
+ *                     'default'     => 'subscriber',
  *                 ),
  *                 'output_schema'         => array(
  *                     'type'        => 'string',
  *                     'description' => __( 'User data in CSV format.', 'my-plugin' ),
- *                     'required'    => true,
  *                 ),
  *                 'meta'                  => array(
  *                     'public' => true,
@@ -53,6 +52,11 @@
  *     }
  *     add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
  *
+ * Use a category to group related abilities and improve discoverability. The
+ * `data-export` category must be registered first with `wp_register_ability_category()`
+ * on `wp_abilities_api_categories_init`, or the ability registration fails.
+ * Abilities that omit `category` fall back to the built-in `uncategorized` category.
+ *
  * Once registered, abilities can be checked, retrieved, and managed:
  *
  *     // Checks if an ability is registered, and prints its label.
@@ -72,7 +76,8 @@
  *
  *  - Always register abilities on the `wp_abilities_api_init` hook.
  *  - Use namespaced ability names to prevent conflicts.
- *  - Implement robust permission checks in permission callbacks.
+ *  - Declare capabilities required for every execution in `required_capabilities`.
+ *  - Check input-dependent permissions in `permission_callback`.
  *  - Provide an `input_schema` to ensure data integrity and document expected inputs.
  *  - Define an `output_schema` to describe return values and validate responses.
  *  - Return `WP_Error` objects for failures rather than throwing exceptions.
@@ -90,7 +95,8 @@ declare( strict_types = 1 );
  *
  *  1. Hook into the `wp_abilities_api_init` action.
  *  2. Call `wp_register_ability()` with a namespaced name and configuration.
- *  3. Provide execute and permission callbacks.
+ *  3. Provide an `execute_callback`. For permission checks, set `required_capabilities`,
+ *     `permission_callback`, or both.
  *
  * Example:
  *
@@ -98,24 +104,23 @@ declare( strict_types = 1 );
  *         wp_register_ability(
  *             'my-plugin/analyze-text',
  *             array(
- *                 'label'               => __( 'Analyze Text', 'my-plugin' ),
- *                 'description'         => __( 'Performs sentiment analysis on provided text.', 'my-plugin' ),
- *                 'category'            => 'text-processing',
- *                 'input_schema'        => array(
+ *                 'label'                 => __( 'Analyze Text', 'my-plugin' ),
+ *                 'description'           => __( 'Performs sentiment analysis on provided text.', 'my-plugin' ),
+ *                 'category'              => 'text-processing',
+ *                 'input_schema'          => array(
  *                     'type'        => 'string',
  *                     'description' => __( 'The text to be analyzed.', 'my-plugin' ),
  *                     'minLength'   => 10,
- *                     'required'    => true,
  *                 ),
- *                 'output_schema'       => array(
+ *                 'output_schema'         => array(
  *                     'type'        => 'string',
  *                     'enum'        => array( 'positive', 'negative', 'neutral' ),
  *                     'description' => __( 'The sentiment result: positive, negative, or neutral.', 'my-plugin' ),
- *                     'required'    => true,
  *                 ),
- *                 'execute_callback'    => 'my_plugin_analyze_text',
- *                 'permission_callback' => 'my_plugin_can_analyze_text',
- *                 'meta'                => array(
+ *                 'execute_callback'      => 'my_plugin_analyze_text',
+ *                 'required_capabilities' => array( 'edit_posts' ),
+ *                 'permission_callback'   => 'my_plugin_can_analyze_text',
+ *                 'meta'                  => array(
  *                     'annotations' => array(
  *                         'readonly' => true,
  *                     ),
@@ -126,6 +131,9 @@ declare( strict_types = 1 );
  *     }
  *     add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
  *
+ * The `text-processing` category must be registered first with
+ * `wp_register_ability_category()`, or the registration fails.
+ *
  * On failure, this function returns `null` and calls `_doing_it_wrong()` with the reason.
  * By default, the resulting notice is displayed when `WP_DEBUG` is enabled.
  *
@@ -133,7 +141,8 @@ declare( strict_types = 1 );
  *
  * Ability names must follow these rules:
  *
- *  - Include a namespace prefix (e.g., `my-plugin/my-ability`).
+ *  - Include exactly one namespace and one ability name, separated by a single slash
+ *    (e.g., `my-plugin/my-ability`). Nested namespaces are not supported.
  *  - Use only lowercase alphanumeric characters, dashes, and forward slashes.
  *  - Use descriptive, action-oriented names (e.g., `process-payment`, `generate-report`).
  *
@@ -167,21 +176,22 @@ declare( strict_types = 1 );
  * related WordPress REST API Schema documentation:
  * https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics
  *
- * Defining schemas is mandatory when there is a value to pass or return.
- * They ensure data integrity, improve developer experience, and enable
- * better documentation:
+ * An input schema is required when passing non-null input. Without one, only `null`
+ * input is accepted and callbacks receive no arguments. When input is `null`, a
+ * top-level `default` in the input schema is applied before validation.
  *
- *     'input_schema' => array(
+ * An output schema is optional. When omitted, output schema validation is skipped.
+ * Define one to document and validate the result returned by the ability:
+ *
+ *     'input_schema'  => array(
  *         'type'        => 'string',
  *         'description' => __( 'The text to be analyzed.', 'my-plugin' ),
  *         'minLength'   => 10,
- *         'required'    => true,
  *     ),
- *     'output_schema'       => array(
+ *     'output_schema' => array(
  *         'type'        => 'string',
  *         'enum'        => array( 'positive', 'negative', 'neutral' ),
  *         'description' => __( 'The sentiment result: positive, negative, or neutral.', 'my-plugin' ),
- *         'required'    => true,
  *     ),
  *
  * ### Callbacks
@@ -189,7 +199,9 @@ declare( strict_types = 1 );
  * #### Execute Callback
  *
  * The execute callback performs the ability's core functionality. It receives
- * optional input data and returns either a result or `WP_Error` on failure.
+ * the normalized, validated input when an input schema is defined, and no arguments
+ * otherwise. It returns either a result or `WP_Error` on failure. Exceptions thrown
+ * by execute or permission callbacks are caught and converted to a `WP_Error`.
  *
  *     function my_plugin_analyze_text( string $input ): string|WP_Error {
  *         $score = My_Plugin::perform_sentiment_analysis( $input );
@@ -199,19 +211,55 @@ declare( strict_types = 1 );
  *         return My_Plugin::interpret_sentiment_score( $score );
  *     }
  *
- * #### Permission Callback
+ * #### Required Capabilities and Permission Callback
+ *
+ * Declare only the minimum capabilities required for every execution in
+ * `required_capabilities`. Check additional capabilities that depend on the input
+ * in `permission_callback`. Declaring capabilities needed only for particular inputs
+ * can cause discovery to hide an ability the current user is allowed to execute
+ * with other inputs.
+ *
+ * Each required capability is checked with `current_user_can()` before the
+ * permission callback runs. The callback is optional when the list is non-empty.
+ * For an ability that everyone may execute, use `__return_true` as the callback
+ * with no required capabilities.
  *
  * The permission callback determines whether the ability can be executed.
  * It receives the same input as the execute callback and must return a
  * boolean or `WP_Error`. Common use cases include checking user capabilities,
- * validating API keys, or verifying system state:
+ * validating API keys, or verifying system state. In the example above, every
+ * execution requires `edit_posts`, and the callback only adds the check that
+ * depends on the input:
  *
  *     function my_plugin_can_analyze_text( string $input ): bool|WP_Error {
- *         return current_user_can( 'edit_posts' );
+ *         // Only administrators can analyze long texts.
+ *         if ( mb_strlen( $input ) > 5000 ) {
+ *             return current_user_can( 'manage_options' );
+ *         }
+ *         return true;
  *     }
  *
+ * ### Execution Lifecycle
+ *
+ * Call `WP_Ability::execute()` to normalize and validate input, check permissions,
+ * invoke the execute callback, and validate output. It returns the result or a
+ * `WP_Error` on failure. Custom ability classes can override this behavior.
+ *
+ * Hooks run in this order, and processing stops at the first failure:
+ *
+ *  1. `wp_ability_invoked` action, with the raw input.
+ *  2. `wp_pre_execute_ability` filter, which can short-circuit the rest of execution.
+ *  3. `wp_ability_normalize_input`, `wp_ability_validate_input`, and
+ *     `wp_ability_permission_result` filters. The input validation filter runs only
+ *     when a non-empty input schema is defined.
+ *  4. `wp_before_execute_ability` action.
+ *  5. `wp_ability_execute_result` and `wp_ability_validate_output` filters.
+ *  6. `wp_after_execute_ability` action.
+ *
  * ### Client Exposure
  *
+ * Exposure flags do not grant execution permission.
+ *
  * Set the high-level `public` flag to make an ability available to clients
  * such as the REST API, MCP, or AI agents:
  *
@@ -235,12 +283,14 @@ declare( strict_types = 1 );
  * @since 7.2.0 Added the `required_capabilities` argument.
  *
  * @see WP_Abilities_Registry::register()
+ * @see WP_Ability::execute()
  * @see wp_register_ability_category()
  * @see wp_unregister_ability()
  *
  * @param string               $name The name of the ability. Must be a namespaced string containing
  *                                   a prefix, e.g., `my-plugin/my-ability`. Can only contain lowercase
- *                                   alphanumeric characters, dashes, and forward slashes.
+ *                                   alphanumeric characters and dashes, with exactly one slash
+ *                                   separating the namespace from the ability name.
  * @param array<string, mixed> $args {
  *     An associative array of arguments for configuring the ability.
  *
@@ -258,12 +308,14 @@ declare( strict_types = 1 );
  *                                                       Receives optional mixed input data (same as `execute_callback`) and
  *                                                       must return `true`/`false` for simple checks, or `WP_Error` for
  *                                                       detailed error responses. To let everyone run the ability,
- *                                                       use `__return_true`. The `public` meta flag doesn't affect
- *                                                       this. It only controls where the ability is shown.
- *     @type list<string>         $required_capabilities Optional. Capabilities the current user must have to run the
- *                                                       ability. Each one is checked with `current_user_can()` before
- *                                                       `permission_callback` runs. Use it for checks that don't depend
- *                                                       on the input. Default empty array.
+ *                                                       use `__return_true` with no required capabilities. The `public`
+ *                                                       meta flag doesn't affect this. It only controls where the
+ *                                                       ability is shown.
+ *     @type list<string>         $required_capabilities Optional. Minimum capabilities required for every execution of the
+ *                                                       ability, regardless of input. Each one is checked with
+ *                                                       `current_user_can()` before `permission_callback` runs.
+ *                                                       Must be a list of non-empty strings. Check input-dependent
+ *                                                       requirements in `permission_callback`. Default empty array.
  *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for validating the ability's input.
  *                                                       Must be a valid JSON Schema object defining the structure and
  *                                                       constraints for input data. Used for automatic validation and
@@ -276,7 +328,8 @@ declare( strict_types = 1 );
  *
  *         @type array<string, bool|null> $annotations  {
  *             Optional. Semantic annotations describing the ability's behavioral characteristics.
- *             These annotations are hints for tooling and documentation.
+ *             These annotations are hints for tooling and documentation, not enforced behavior.
+ *             Each annotation defaults to null, indicating that its behavior is unspecified.
  *
  *             @type bool|null $readonly    Optional. If true, the ability does not modify its environment.
  *             @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment.
@@ -295,7 +348,9 @@ declare( strict_types = 1 );
  *     @type string               $ability_class       Optional. Fully-qualified custom class name to instantiate
  *                                                     instead of the default `WP_Ability` class. The custom class
  *                                                     must extend `WP_Ability`. Useful for advanced customization
- *                                                     of ability behavior.
+ *                                                     of ability behavior, including callback handling. The default
+ *                                                     execute and permission callback registration requirements
+ *                                                     are not enforced for custom classes.
  * }
  * @return WP_Ability|null The registered ability instance on success, `null` on failure.
  *
@@ -426,14 +481,15 @@ function wp_get_ability( string $name ): ?WP_Ability {
  *
  * When called without arguments, returns all registered abilities. When called
  * with an $args array, returns only abilities that match every specified condition.
+ * In both cases, the callbacks and filters below can change the result.
  *
  * Filtering pipeline (executed in order):
  *
- * 1. Declarative filters (`category`, `namespace`, `meta`) — per-item, AND logic between
- *    arg types.
+ * 1. Declarative filters (`category`, `namespace`, `meta`, `has_required_capabilities`) —
+ *    per-item, AND logic between arg types.
  * 2. `item_include_callback` — per-item, caller-scoped. Return true to include, false to exclude.
  * 3. `wp_get_abilities_item_include` filter — per-item, ecosystem-scoped. Plugins can enforce
- *    universal inclusion rules regardless of what the caller passed.
+ *    inclusion rules for abilities that passed the declarative filters.
  * 4. `result_callback` — on the full matched array, caller-scoped. Sort, slice, or reshape.
  * 5. `wp_get_abilities_result` filter — on the full array, ecosystem-scoped.
  *
@@ -441,7 +497,7 @@ function wp_get_ability( string $name ): ?WP_Ability {
  *
  * Examples:
  *
- *     // All abilities (unchanged behaviour).
+ *     // All abilities.
  *     $abilities = wp_get_abilities();
  *
  *     // Filter by category.
@@ -453,6 +509,10 @@ function wp_get_ability( string $name ): ?WP_Ability {
  *     // Filter by meta.
  *     $abilities = wp_get_abilities( array( 'meta' => array( 'show_in_rest' => true ) ) );
  *
+ *     // Abilities whose declared minimum capabilities the current user has.
+ *     // Input-dependent permission checks still run when executing an ability.
+ *     $abilities = wp_get_abilities( array( 'has_required_capabilities' => true ) );
+ *
  *     // Combine filters (AND logic between arg types).
  *     $abilities = wp_get_abilities( array(
  *         'category'  => 'content',
@@ -470,14 +530,14 @@ function wp_get_ability( string $name ): ?WP_Ability {
  *     // Caller-scoped result callback (sort + paginate).
  *     $abilities = wp_get_abilities( array(
  *         'result_callback' => function ( array $abilities ) {
- *             usort( $abilities, fn( $a, $b ) => strcasecmp( $a->get_label(), $b->get_label() ) );
- *             return array_slice( $abilities, 0, 10 );
+ *             uasort( $abilities, fn( $a, $b ) => strcasecmp( $a->get_label(), $b->get_label() ) );
+ *             return array_slice( $abilities, 0, 10, true );
  *         },
  *     ) );
  *
  * The pipeline always runs, even when called with no arguments. This ensures that the
- * `wp_get_abilities_item_include` and `wp_get_abilities_result` filters always fire,
- * giving plugins a reliable place to enforce universal inclusion or shaping rules.
+ * `wp_get_abilities_result` filter always fires, giving plugins a reliable place to
+ * enforce universal shaping rules.
  * For raw, unfiltered registry data that bypasses the filter pipeline entirely, use
  * {@see WP_Abilities_Registry::get_all_registered()} directly.
  *
@@ -488,21 +548,23 @@ function wp_get_ability( string $name ): ?WP_Ability {
  * @see WP_Abilities_Registry::get_all_registered()
  *
  * @param array $args {
- *     Optional. Arguments to filter the returned abilities. Default empty array (returns all).
+ *     Optional. Arguments to filter the returned abilities. Default empty array.
+ *     Omitted filters do not restrict the results.
  *
- *     @type string          $category                  Filter by category slug. Only abilities whose category
+ *     @type string          $category                  Optional. Filter by category slug. Only abilities whose category
  *                                                      exactly matches the given slug are included.
- *     @type string          $namespace                 Filter by ability namespace prefix. Pass the namespace
+ *     @type string          $namespace                 Optional. Filter by ability namespace prefix. Pass the namespace
  *                                                      without a trailing slash, e.g. `'woocommerce'` matches
  *                                                      `'woocommerce/create-order'`.
- *     @type array           $meta                      Filter by meta key/value pairs. All conditions must
- *                                                      match (AND logic). Supports nested arrays for structured
- *                                                      meta, e.g. `array( 'mcp' => array( 'public' => true ) )`.
- *     @type bool            $has_required_capabilities Whether to leave out abilities whose required capabilities
+ *     @type array           $meta                      Optional. Filter by meta key/value pairs. All keys must exist
+ *                                                      and non-array values must match strictly, including their types.
+ *                                                      Nested arrays are matched recursively as subsets, e.g.
+ *                                                      `array( 'mcp' => array( 'public' => true ) )`.
+ *     @type bool            $has_required_capabilities Optional. Whether to leave out abilities whose required capabilities
  *                                                      the current user doesn't have. Abilities without required
- *                                                      capabilities are always included. Permission callbacks
- *                                                      and filters don't run, so an included ability can still
- *                                                      deny execution. Default false.
+ *                                                      capabilities pass this check but remain subject to other filters.
+ *                                                      Permission callbacks do not run, so inclusion does not guarantee
+ *                                                      execution permission.
  *     @type callable        $item_include_callback     Optional. A callback invoked per ability after declarative
  *                                                      filters. Receives a WP_Ability instance, returns bool.
  *                                                      Return true to include, false to exclude.
@@ -510,10 +572,9 @@ function wp_get_ability( string $name ): ?WP_Ability {
  *                                                      array. Receives WP_Ability[], must return WP_Ability[].
  *                                                      Use for sorting, slicing, or reshaping the result.
  * }
- * @return WP_Ability[] An array of registered WP_Ability instances matching the given args,
- *                      keyed by ability name. Returns an empty array if no abilities are
- *                      registered, the registry is unavailable, or no abilities match the
- *                      given args.
+ * @return WP_Ability[] An array of registered WP_Ability instances, keyed by ability name
+ *                      unless reshaped by a callback or filter. Returns an empty array if
+ *                      the registry is unavailable.
  */
 function wp_get_abilities( array $args = array() ): array {
 	$registry = WP_Abilities_Registry::get_instance();
@@ -567,12 +628,12 @@ function wp_get_abilities( array $args = array() ): array {
 		 * Filters whether an individual ability should be included in the result set.
 		 *
 		 * Fires after the declarative filters and the caller-scoped item_include_callback.
-		 * Plugins can use this to enforce universal inclusion rules regardless of
-		 * what the caller passed in $args.
+		 * Only abilities that pass the declarative filters reach this hook. It can override
+		 * the caller-scoped callback's result.
 		 *
 		 * @since 7.1.0
 		 *
-		 * @param bool       $include Whether to include the ability. Default true (after declarative filters pass).
+		 * @param bool       $include The caller-scoped callback's result, or true when no callback is provided.
 		 * @param WP_Ability $ability The ability instance being evaluated.
 		 * @param array      $args    The full $args array passed to wp_get_abilities().
 		 */
@@ -606,7 +667,9 @@ function wp_get_abilities( array $args = array() ): array {
  * Checks whether an ability's meta array matches a set of required key/value conditions.
  *
  * All conditions must match (AND logic). Supports nested arrays for structured meta,
- * e.g. `array( 'mcp' => array( 'public' => true ) )`.
+ * e.g. `array( 'mcp' => array( 'public' => true ) )`. Every condition key must exist,
+ * and non-array values are compared strictly, including their types. Extra meta keys
+ * are allowed at any level.
  *
  * @since 7.1.0
  * @access private
@@ -666,8 +729,9 @@ function _wp_get_abilities_match_meta( array $meta, array $conditions ): bool {
  * @see wp_register_ability()
  * @see wp_unregister_ability_category()
  *
- * @param string               $slug The unique slug for the ability category. Must contain only lowercase
- *                                   alphanumeric characters and dashes (e.g., 'data-export').
+ * @param string               $slug The unique slug for the ability category. Must contain lowercase
+ *                                   alphanumeric segments separated by single dashes (e.g., 'data-export'),
+ *                                   with no leading or trailing dash.
  * @param array<string, mixed> $args {
  *     An associative array of arguments for the ability category.
  *
@@ -816,8 +880,9 @@ function wp_get_ability_category( string $slug ): ?WP_Ability_Category {
  * @see WP_Ability_Categories_Registry::get_all_registered()
  * @see wp_get_ability_category()
  *
- * @return WP_Ability_Category[] An array of registered ability category instances. Returns an empty array
- *                               if no ability categories are registered or if the registry is unavailable.
+ * @return WP_Ability_Category[] An array of registered ability category instances,
+ *                               keyed by category slug. Returns an empty array if no ability categories
+ *                               are registered or if the registry is unavailable.
  */
 function wp_get_ability_categories(): array {
 	$registry = WP_Ability_Categories_Registry::get_instance();
diff --git a/wp-includes/abilities-api/class-wp-abilities-registry.php b/wp-includes/abilities-api/class-wp-abilities-registry.php
index 6790ed8535..fe41f8e0f6 100644
--- a/wp-includes/abilities-api/class-wp-abilities-registry.php
+++ b/wp-includes/abilities-api/class-wp-abilities-registry.php
@@ -61,8 +61,9 @@ final class WP_Abilities_Registry {
 	 *     @type callable             $permission_callback   A callback function to check permissions before execution.
 	 *                                                       Receives optional mixed input and returns bool or WP_Error.
 	 *                                                       Required unless `$required_capabilities` lists at least one capability.
-	 *     @type list<string>         $required_capabilities Optional. Capabilities the current user must have to run the
-	 *                                                       ability. They are checked before `$permission_callback`.
+	 *     @type list<string>         $required_capabilities Optional. Minimum capabilities required for every execution of the
+	 *                                                       ability, regardless of input. They are checked before
+	 *                                                       `$permission_callback`, which handles input-dependent requirements.
 	 *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input.
 	 *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
 	 *     @type array<string, mixed> $meta                  {
@@ -133,8 +134,9 @@ final class WP_Abilities_Registry {
 		 *     @type callable             $permission_callback   A callback function to check permissions before execution.
 		 *                                                       Receives optional mixed input and returns bool or WP_Error.
 		 *                                                       Required unless `$required_capabilities` lists at least one capability.
-		 *     @type list<string>         $required_capabilities Optional. Capabilities the current user must have to run the
-		 *                                                       ability. They are checked before `$permission_callback`.
+		 *     @type list<string>         $required_capabilities Optional. Minimum capabilities required for every execution of the
+		 *                                                       ability, regardless of input. They are checked before
+		 *                                                       `$permission_callback`, which handles input-dependent requirements.
 		 *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input.
 		 *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
 		 *     @type array<string, mixed> $meta                  {
diff --git a/wp-includes/abilities-api/class-wp-ability.php b/wp-includes/abilities-api/class-wp-ability.php
index c7e32ad8ef..d800e8c1b2 100644
--- a/wp-includes/abilities-api/class-wp-ability.php
+++ b/wp-includes/abilities-api/class-wp-ability.php
@@ -164,8 +164,9 @@ class WP_Ability {
 	 *     @type callable             $permission_callback   A callback function to check permissions before execution.
 	 *                                                       Receives optional mixed input and returns bool or WP_Error.
 	 *                                                       Required unless `$required_capabilities` lists at least one capability.
-	 *     @type list<string>         $required_capabilities Optional. Capabilities the current user must have to run the
-	 *                                                       ability. They are checked before `$permission_callback`.
+	 *     @type list<string>         $required_capabilities Optional. Minimum capabilities required for every execution of the
+	 *                                                       ability, regardless of input. They are checked before
+	 *                                                       `$permission_callback`, which handles input-dependent requirements.
 	 *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input.
 	 *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
 	 *     @type array<string, mixed> $meta                  {
@@ -238,8 +239,9 @@ class WP_Ability {
 	 *     @type callable             $permission_callback   A callback function to check permissions before execution.
 	 *                                                       Receives optional mixed input and returns bool or WP_Error.
 	 *                                                       Required unless `$required_capabilities` lists at least one capability.
-	 *     @type list<string>         $required_capabilities Optional. Capabilities the current user must have to run the
-	 *                                                       ability. They are checked before `$permission_callback`.
+	 *     @type list<string>         $required_capabilities Optional. Minimum capabilities required for every execution of the
+	 *                                                       ability, regardless of input. They are checked before
+	 *                                                       `$permission_callback`, which handles input-dependent requirements.
 	 *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input. Required if ability accepts an input.
 	 *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
 	 *     @type array<string, mixed> $meta                  {
@@ -274,8 +276,9 @@ class WP_Ability {
 	 *     @type callable             $permission_callback   A callback function to check permissions before execution.
 	 *                                                       Receives optional mixed input and returns bool or WP_Error.
 	 *                                                       Required unless `$required_capabilities` lists at least one capability.
-	 *     @type list<string>         $required_capabilities Optional. Capabilities the current user must have to run the
-	 *                                                       ability. They are checked before `$permission_callback`.
+	 *     @type list<string>         $required_capabilities Optional. Minimum capabilities required for every execution of the
+	 *                                                       ability, regardless of input. They are checked before
+	 *                                                       `$permission_callback`, which handles input-dependent requirements.
 	 *     @type array<string, mixed> $input_schema          Optional. JSON Schema definition for the ability's input.
 	 *     @type array<string, mixed> $output_schema         Optional. JSON Schema definition for the ability's output.
 	 *     @type array<string, mixed> $meta                  {
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 8516225007..9b005558e0 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta1-64684';
+$wp_version = '7.2-beta1-64685';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.