Commit 6c88ed4ff1d for woocommerce

commit 6c88ed4ff1d638309c2ba747db52987d50d29184
Author: Luis Felipe Zaguini <26530524+zaguiini@users.noreply.github.com>
Date:   Fri Nov 6 13:07:43 2026 -0300

    Save nothing when an ability's change() returns an empty WP_Error (#70752)

    Save nothing when change() returns an empty WP_Error

    execute_change() marked an empty WP_Error from change() as a refusal,
    but decided the save from has_errors(), so the change was saved. Use
    the same check for both, as the change() docblock describes.

    Also name order writes in the changelog entry of #70709.

diff --git a/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message b/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message
index 67738cf2d2f..39345903d4a 100644
--- a/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message
+++ b/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message
@@ -1,4 +1,4 @@
 Significance: patch
 Type: tweak

-Ability errors now list every refused value of a product write in one message, so AI agents can fix them all in one retry.
+Ability errors now list every refused value of a product or order write in one message, so AI agents can fix them all in one retry.
diff --git a/plugins/woocommerce/src/Abilities/ActionableAbility.php b/plugins/woocommerce/src/Abilities/ActionableAbility.php
index 0049fd33518..932dd33c2cc 100644
--- a/plugins/woocommerce/src/Abilities/ActionableAbility.php
+++ b/plugins/woocommerce/src/Abilities/ActionableAbility.php
@@ -280,7 +280,7 @@ abstract class ActionableAbility extends Ability {
 				$refused
 			);
 		}
-		if ( $refused->has_errors() ) {
+		if ( $core_refused || $refused->has_errors() ) {
 			return self::join_refusals( $refused );
 		}

diff --git a/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php b/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php
index d07ae5cc553..85974bbd840 100644
--- a/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php
@@ -3557,6 +3557,45 @@ class ExtensibleAbilitiesTest extends \WC_REST_Unit_Test_Case {
 		$this->assertSame( 'Pen', wc_get_product( $product->get_id() )->get_name() );
 	}

+	/**
+	 * @testdox Should save nothing when change() returns a WP_Error without errors.
+	 */
+	public function test_an_empty_error_from_change_saves_nothing(): void {
+		$product = \WC_Helper_Product::create_simple_product( true, array( 'name' => 'Pen' ) );
+		$ability = new class(
+			'test-ext/product-rename',
+			array(
+				'label'               => 'Rename product',
+				'description'         => 'Renames a product.',
+				'category'            => 'woocommerce',
+				'output_schema'       => array( 'type' => 'object' ),
+				'permission_callback' => '__return_true',
+			)
+		) extends TestProductRenameAbility {
+			/**
+			 * Rename the product, then refuse with an empty error.
+			 *
+			 * @param \WC_Product $subject Product.
+			 * @param array       $input   Ability input.
+			 * @return \WP_Error
+			 */
+			protected function change( $subject, array $input ) {
+				parent::change( $subject, $input );
+				return new \WP_Error();
+			}
+		};
+
+		$result = $ability->execute_change(
+			array(
+				'id'   => $product->get_id(),
+				'name' => 'Pencil',
+			)
+		);
+
+		$this->assertWPError( $result );
+		$this->assertSame( 'Pen', wc_get_product( $product->get_id() )->get_name() );
+	}
+
 	/**
 	 * @testdox Should return a fixed 500 error when the order write fails, though the order's save() catches the exception.
 	 */