From 3cb7712cd3e4385b7eb7eb93a481fd7c7ded8d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Fas?= Date: Wed, 25 Feb 2026 09:43:59 +0100 Subject: [PATCH 1/4] Agents Manager: Add dedicated CIAB variant support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CIAB environments now always load Agents Manager, regardless of the useUnifiedExperience flag. This replaces the previous behavior where connected CIAB sites either didn't load AM or fell back to wp-admin variant. - New 'ciab' variant for connected CIAB sites (uses dedicated JS entry point that mounts the chat UI without touching the classic admin bar or Site Hub). - Move agents_manager_enqueue_in_block_editor filter to top of get_variant() so it applies universally (including CIAB) rather than being inside is_enabled(). - Skip admin bar manipulation for CIAB — the SPA hides the classic admin bar and uses its own Site Hub, so admin bar nodes are invisible. - Help Center continues to work independently in CIAB — AM does not dequeue Help Center scripts outside of Gutenberg contexts. Co-Authored-By: Claude Opus 4.6 --- .../agents-manager-ciab-variant-support | 4 ++ .../agents-manager/class-agents-manager.php | 21 ++++--- .../agents-manager/Agents_Manager_Test.php | 63 ++++++++++++++----- 3 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/agents-manager-ciab-variant-support diff --git a/projects/packages/jetpack-mu-wpcom/changelog/agents-manager-ciab-variant-support b/projects/packages/jetpack-mu-wpcom/changelog/agents-manager-ciab-variant-support new file mode 100644 index 00000000000..3f7cc29dc09 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/agents-manager-ciab-variant-support @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Agents Manager: Add dedicated CIAB variant. CIAB environments always load Agents Manager regardless of the unified experience setting, using the new 'ciab' variant for connected sites and 'ciab-disconnected' for disconnected sites. diff --git a/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php b/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php index 83a243447c2..c17d9a11582 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php @@ -176,9 +176,11 @@ public function enqueue_scripts() { wp_dequeue_style( 'help-center-style' ); } - // For non-Gutenberg environments, add to admin bar - // Gutenberg doesn't have an admin bar, so JS will handle UI insertion - if ( ! $is_gutenberg ) { + // For non-Gutenberg, non-CIAB environments, add to admin bar. + // Gutenberg doesn't have an admin bar, so JS will handle UI insertion. + // CIAB hides the classic admin bar and uses its own Site Hub — the JS variant handles UI there. + $is_ciab = $this->is_ciab_environment(); + if ( ! $is_gutenberg && ! $is_ciab ) { add_action( 'admin_bar_menu', function ( $wp_admin_bar ) use ( $use_disconnected ) { @@ -271,14 +273,16 @@ function ( $wp_admin_bar ) use ( $use_disconnected ) { * @return string|null The variant name, or null if scripts should not be loaded. */ private function get_variant() { - // CIAB/Next Admin: only load when disconnected (connected CIAB is handled by Help Center). - if ( $this->is_ciab_environment() ) { - if ( self::is_enabled() && $this->is_jetpack_disconnected() ) { - return 'ciab-disconnected'; - } + // Universal: skip block editor if explicitly disabled (e.g. CIAB parent page already runs AM). + if ( $this->is_block_editor() && ! apply_filters( 'agents_manager_enqueue_in_block_editor', true ) ) { return null; } + // CIAB/Next Admin: always load. No is_enabled() gating — CIAB manages its own AI experience. + if ( $this->is_ciab_environment() ) { + return $this->is_jetpack_disconnected() ? 'ciab-disconnected' : 'ciab'; + } + // Frontend: load disconnected variant for eligible logged-in editors. if ( ! is_admin() ) { if ( $this->is_loading_on_frontend() && self::is_enabled() ) { @@ -321,6 +325,7 @@ public static function is_enabled() { return true; } + // Note: CIAB environments bypass is_enabled() entirely — see get_variant(). return false; } diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php index b971d513de0..c990445ce68 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php @@ -1740,7 +1740,7 @@ public function test_enqueue_scripts_includes_section_name_gutenberg_disconnecte /** * Tests that enqueue_scripts includes sectionName as ciab-disconnected in CIAB environment - * when unified experience is enabled but Jetpack is disconnected. + * when Jetpack is disconnected. CIAB always loads AM regardless of unified experience setting. */ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { // Set admin context - scripts only enqueue in admin. @@ -1759,8 +1759,8 @@ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { wp_register_script( 'agents-manager', 'https://example.com/agents-manager.js', array(), '1.0', true ); - // Enable unified experience and simulate a Jetpack site with a disconnected user. - add_filter( 'agents_manager_use_unified_experience', '__return_true', 20 ); + // Simulate a Jetpack site with a disconnected user. + // Note: no agents_manager_use_unified_experience needed — CIAB bypasses is_enabled(). add_filter( 'is_jetpack_site', '__return_true', 20 ); // Do not connect the user - is_user_connected() will return false by default. @@ -1772,7 +1772,6 @@ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { $this->assertStringContainsString( '"sectionName":"ciab-disconnected"', $inline_script ); - remove_filter( 'agents_manager_use_unified_experience', '__return_true', 20 ); remove_filter( 'is_jetpack_site', '__return_true', 20 ); // Restore the original did_action counter to prevent test order dependencies. @@ -1783,6 +1782,46 @@ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { } } + /** + * Tests that enqueue_scripts includes sectionName as ciab in CIAB environment + * when Jetpack is connected. CIAB always loads AM regardless of unified experience setting. + */ + public function test_enqueue_scripts_includes_section_name_ciab_connected() { + // Set admin context - scripts only enqueue in admin. + $this->set_admin_context(); + + // Save the current did_action counter for next_admin_init to restore later. + global $wp_actions; + $original_action_count = $wp_actions['next_admin_init'] ?? 0; + + // Simulate CIAB environment by firing the next_admin_init action. + do_action( 'next_admin_init' ); + + // Reset the script registry. + global $wp_scripts; + $wp_scripts = null; + + wp_register_script( 'agents-manager', 'https://example.com/agents-manager.js', array(), '1.0', true ); + + // Do NOT simulate a Jetpack disconnected site. + // is_jetpack_disconnected() returns false for non-Jetpack sites. + + $this->agents_manager->enqueue_scripts(); + + $this->assertNotNull( $wp_scripts, 'wp_scripts should be initialized after enqueue_scripts' ); + $inline_scripts = $wp_scripts->registered['agents-manager']->extra['before'] ?? array(); + $inline_script = implode( "\n", array_filter( $inline_scripts ) ); + + $this->assertStringContainsString( '"sectionName":"ciab"', $inline_script ); + + // Restore the original did_action counter to prevent test order dependencies. + if ( $original_action_count === 0 ) { + unset( $wp_actions['next_admin_init'] ); + } else { + $wp_actions['next_admin_init'] = $original_action_count; + } + } + /** * Tests that should_enqueue_script returns true when unified experience is enabled but Jetpack is disconnected (in wp-admin). */ @@ -1952,11 +1991,11 @@ public function test_scripts_not_enqueued_on_p2_frontend() { } /** - * Tests that should_enqueue_script returns false in CIAB environment when Jetpack is connected. + * Tests that get_variant returns ciab in CIAB environment when Jetpack is connected. * - * Connected CIAB is handled by Help Center; Agents Manager should not load. + * CIAB always loads Agents Manager regardless of useUnifiedExperience flag. */ - public function test_should_enqueue_script_returns_false_in_ciab_when_connected() { + public function test_get_variant_returns_ciab_in_ciab_when_connected() { $this->set_admin_context(); // Save and simulate CIAB environment. @@ -1964,13 +2003,9 @@ public function test_should_enqueue_script_returns_false_in_ciab_when_connected( $original_action_count = $wp_actions['next_admin_init'] ?? 0; do_action( 'next_admin_init' ); - // Enable unified experience but do NOT simulate a Jetpack disconnected site. + // Do NOT enable unified experience — CIAB bypasses is_enabled() entirely. // is_jetpack_disconnected() returns false for non-Jetpack sites. - add_filter( 'agents_manager_use_unified_experience', '__return_true', 20 ); - - $result = $this->call_should_enqueue_script(); - - remove_filter( 'agents_manager_use_unified_experience', '__return_true', 20 ); + $result = $this->call_get_variant(); // Restore did_action counter. if ( $original_action_count === 0 ) { @@ -1979,7 +2014,7 @@ public function test_should_enqueue_script_returns_false_in_ciab_when_connected( $wp_actions['next_admin_init'] = $original_action_count; } - $this->assertFalse( $result ); + $this->assertSame( 'ciab', $result ); } /** From ec2c3b02471bbaf836596540eb47e7edc2479aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Fas?= Date: Wed, 25 Feb 2026 12:54:03 +0100 Subject: [PATCH 2/4] refactor: route CIAB through is_enabled() with dedicated filter Instead of bypassing is_enabled() entirely for CIAB environments, add a dedicated `agents_manager_enabled_in_ciab` filter that defaults to true. This keeps CIAB loading by default while providing an escape hatch for debugging or gradual rollout. Co-Authored-By: Claude Opus 4.6 --- .../agents-manager/class-agents-manager.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php b/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php index c17d9a11582..288bc038b59 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php @@ -274,12 +274,12 @@ function ( $wp_admin_bar ) use ( $use_disconnected ) { */ private function get_variant() { // Universal: skip block editor if explicitly disabled (e.g. CIAB parent page already runs AM). - if ( $this->is_block_editor() && ! apply_filters( 'agents_manager_enqueue_in_block_editor', true ) ) { + if ( self::is_block_editor() && ! apply_filters( 'agents_manager_enqueue_in_block_editor', true ) ) { return null; } - // CIAB/Next Admin: always load. No is_enabled() gating — CIAB manages its own AI experience. - if ( $this->is_ciab_environment() ) { + // CIAB: Load either the connected or disconnected variants if enabled. + if ( $this->is_ciab_environment() && self::is_enabled() ) { return $this->is_jetpack_disconnected() ? 'ciab-disconnected' : 'ciab'; } @@ -315,6 +315,17 @@ private function get_variant() { * @return bool */ public static function is_enabled() { + // CIAB: Agents Manager is the default AI experience — enabled unless explicitly + // disabled via filter (e.g. for debugging or gradual rollout). + if ( (bool) did_action( 'next_admin_init' ) ) { + /** + * Filter whether Agents Manager is enabled in CIAB (Next Admin) environments. + * + * @param bool $enabled Whether Agents Manager should load. Default true. + */ + return apply_filters( 'agents_manager_enabled_in_ciab', true ); + } + // Full unified experience: Agents Manager with support guides, Help Center takeover, etc. if ( apply_filters( 'agents_manager_use_unified_experience', false ) ) { return true; @@ -325,7 +336,6 @@ public static function is_enabled() { return true; } - // Note: CIAB environments bypass is_enabled() entirely — see get_variant(). return false; } From 7a33c6aa47c1d9198c817cf49ab95916da5ba00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Fas?= Date: Wed, 25 Feb 2026 13:06:16 +0100 Subject: [PATCH 3/4] Remove block editor filter from CIAB variant PR The `agents_manager_enqueue_in_block_editor` filter belongs to a separate PR (#47248) and should not be mixed into the CIAB variant changes. Co-Authored-By: Claude Opus 4.6 --- .../src/features/agents-manager/class-agents-manager.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php b/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php index 288bc038b59..b4d8ec19a65 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/agents-manager/class-agents-manager.php @@ -273,11 +273,6 @@ function ( $wp_admin_bar ) use ( $use_disconnected ) { * @return string|null The variant name, or null if scripts should not be loaded. */ private function get_variant() { - // Universal: skip block editor if explicitly disabled (e.g. CIAB parent page already runs AM). - if ( self::is_block_editor() && ! apply_filters( 'agents_manager_enqueue_in_block_editor', true ) ) { - return null; - } - // CIAB: Load either the connected or disconnected variants if enabled. if ( $this->is_ciab_environment() && self::is_enabled() ) { return $this->is_jetpack_disconnected() ? 'ciab-disconnected' : 'ciab'; From 2828875586b1d846aa64c1deea9de4be7714f0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Fas?= Date: Wed, 25 Feb 2026 13:19:07 +0100 Subject: [PATCH 4/4] test: address Copilot review feedback on CIAB tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use $wp_actions counter instead of do_action('next_admin_init') to avoid triggering enqueue_scripts() as a side effect in tests - Fix docblocks: "when Jetpack is connected" → "when is_jetpack_disconnected() returns false" for accuracy - Remove stale "bypasses is_enabled()" comments - Add test for agents_manager_enabled_in_ciab filter escape hatch Co-Authored-By: Claude Opus 4.6 --- .../agents-manager/Agents_Manager_Test.php | 72 +++++++++++++++---- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php index c990445ce68..e22245aa672 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/agents-manager/Agents_Manager_Test.php @@ -60,6 +60,13 @@ class Agents_Manager_Test extends \WorDBless\BaseTestCase { */ private $original_current_screen; + /** + * Original next_admin_init action count to restore after tests. + * + * @var int + */ + private $original_next_admin_init_count; + /** * Set up test fixtures. */ @@ -77,6 +84,10 @@ public function set_up() { // Save original current_screen global. $this->original_current_screen = $GLOBALS['current_screen'] ?? null; + + // Save original next_admin_init action count (CIAB detection). + global $wp_actions; + $this->original_next_admin_init_count = $wp_actions['next_admin_init'] ?? 0; } /** @@ -122,6 +133,14 @@ public function tear_down() { // Log out any logged-in user. wp_set_current_user( 0 ); + // Restore next_admin_init action count (CIAB detection). + global $wp_actions; + if ( $this->original_next_admin_init_count === 0 ) { + unset( $wp_actions['next_admin_init'] ); + } else { + $wp_actions['next_admin_init'] = $this->original_next_admin_init_count; + } + // Clear the status cache and constants. Cache::clear(); Constants::clear_constants(); @@ -1740,7 +1759,7 @@ public function test_enqueue_scripts_includes_section_name_gutenberg_disconnecte /** * Tests that enqueue_scripts includes sectionName as ciab-disconnected in CIAB environment - * when Jetpack is disconnected. CIAB always loads AM regardless of unified experience setting. + * when Jetpack is disconnected. */ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { // Set admin context - scripts only enqueue in admin. @@ -1750,8 +1769,9 @@ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { global $wp_actions; $original_action_count = $wp_actions['next_admin_init'] ?? 0; - // Simulate CIAB environment by firing the next_admin_init action. - do_action( 'next_admin_init' ); + // Simulate CIAB environment by incrementing the action counter directly + // (avoids side effects from firing the action, which would trigger enqueue_scripts). + $wp_actions['next_admin_init'] = ( $wp_actions['next_admin_init'] ?? 0 ) + 1; // Reset the script registry. global $wp_scripts; @@ -1760,7 +1780,6 @@ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { wp_register_script( 'agents-manager', 'https://example.com/agents-manager.js', array(), '1.0', true ); // Simulate a Jetpack site with a disconnected user. - // Note: no agents_manager_use_unified_experience needed — CIAB bypasses is_enabled(). add_filter( 'is_jetpack_site', '__return_true', 20 ); // Do not connect the user - is_user_connected() will return false by default. @@ -1784,7 +1803,7 @@ public function test_enqueue_scripts_includes_section_name_ciab_disconnected() { /** * Tests that enqueue_scripts includes sectionName as ciab in CIAB environment - * when Jetpack is connected. CIAB always loads AM regardless of unified experience setting. + * when is_jetpack_disconnected() returns false (non-Jetpack site or connected user). */ public function test_enqueue_scripts_includes_section_name_ciab_connected() { // Set admin context - scripts only enqueue in admin. @@ -1794,8 +1813,9 @@ public function test_enqueue_scripts_includes_section_name_ciab_connected() { global $wp_actions; $original_action_count = $wp_actions['next_admin_init'] ?? 0; - // Simulate CIAB environment by firing the next_admin_init action. - do_action( 'next_admin_init' ); + // Simulate CIAB environment by incrementing the action counter directly + // (avoids side effects from firing the action, which would trigger enqueue_scripts). + $wp_actions['next_admin_init'] = ( $wp_actions['next_admin_init'] ?? 0 ) + 1; // Reset the script registry. global $wp_scripts; @@ -1991,19 +2011,17 @@ public function test_scripts_not_enqueued_on_p2_frontend() { } /** - * Tests that get_variant returns ciab in CIAB environment when Jetpack is connected. - * - * CIAB always loads Agents Manager regardless of useUnifiedExperience flag. + * Tests that get_variant returns ciab in CIAB environment + * when is_jetpack_disconnected() returns false (non-Jetpack site or connected user). */ public function test_get_variant_returns_ciab_in_ciab_when_connected() { $this->set_admin_context(); // Save and simulate CIAB environment. global $wp_actions; - $original_action_count = $wp_actions['next_admin_init'] ?? 0; - do_action( 'next_admin_init' ); + $original_action_count = $wp_actions['next_admin_init'] ?? 0; + $wp_actions['next_admin_init'] = ( $wp_actions['next_admin_init'] ?? 0 ) + 1; - // Do NOT enable unified experience — CIAB bypasses is_enabled() entirely. // is_jetpack_disconnected() returns false for non-Jetpack sites. $result = $this->call_get_variant(); @@ -2154,6 +2172,34 @@ public function test_is_enabled_unified_experience_takes_priority_over_block_edi $this->assertTrue( $result ); } + /** + * Tests that get_variant returns null in CIAB when agents_manager_enabled_in_ciab filter returns false. + */ + public function test_get_variant_returns_null_in_ciab_when_disabled_by_filter() { + $this->set_admin_context(); + + // Save and simulate CIAB environment. + global $wp_actions; + $original_action_count = $wp_actions['next_admin_init'] ?? 0; + $wp_actions['next_admin_init'] = ( $wp_actions['next_admin_init'] ?? 0 ) + 1; + + // Disable AM in CIAB via filter. + add_filter( 'agents_manager_enabled_in_ciab', '__return_false', 20 ); + + $result = $this->call_get_variant(); + + remove_filter( 'agents_manager_enabled_in_ciab', '__return_false', 20 ); + + // Restore did_action counter. + if ( $original_action_count === 0 ) { + unset( $wp_actions['next_admin_init'] ); + } else { + $wp_actions['next_admin_init'] = $original_action_count; + } + + $this->assertNull( $result ); + } + /** * Tests that should_enqueue_script returns false on WooCommerce Admin home page. *