Skip to content

Abilities Explorer: move to DataViews and REST (from #1074) - #1181

Merged
dkotter merged 11 commits into
developfrom
claude/abilities-explorer-dataviews-develop-2as8vr
Oct 15, 2026
Merged

dkotter merged 11 commits into
developfrom
claude/abilities-explorer-dataviews-develop-2as8vr

Conversation

@jeffpaul

@jeffpaul jeffpaul commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

What?

Closes #1016
Closes #203

Brings @whyisjake's Abilities Explorer rewrite from #1074 (DataViews and REST) onto develop. The work is his. #1074 was squash-merged into feat/ai-workspace as 0c96aabb, stacked on the AI Workspace branch (#1004), which is not on develop and is in Future Release. This PR cherry-picks that commit with -x, so Jake stays its author. A follow-up commit removes the AI Workspace dependency, so the Explorer rewrite can ship in 1.5.0 without the Workspace.

Props @whyisjake

Why?

The Explorer was the plugin's last admin screen on WP_List_Table, vanilla JS and admin-ajax. Its hand-written search, sort and filters kept needing repair (#883, #641, #648, #344, #588). #1016 is in the 1.5.0 milestone, but #1074 landed on a branch that is not.

#203 asked for a PHP hook to swap the Ability_Table class. That table is removed here, so #203 is resolved by the ai.abilitiesExplorer.fields JavaScript filter instead: a plugin that depends on wp-hooks can add read-only DataViews columns. Each row carries the ability's meta, so the MCP column from #203 could look like this:

wp.hooks.addFilter( 'ai.abilitiesExplorer.fields', 'my-plugin/mcp', ( fields ) => [
	...fields,
	{
		id: 'my-plugin/mcp',
		label: 'MCP',
		type: 'boolean',
		enableSorting: true,
		getValue: ( { item } ) =>
			true === ( item.meta?.mcp?.public ?? item.meta?.public ),
	},
] );

Enqueueing and the full rules are in docs/experiments/abilities-explorer.md under "Adding fields to the table". @raftaar1191, could you confirm this covers your use case?

How?

Three commits change code:

  1. Abilities Explorer: move to DataViews and REST (#1074): the cherry-pick, authored by Jake Spurlock. Conflict resolution only:
  2. Abilities Explorer: remove the AI Workspace dependency (mine). Details below.
  3. Abilities Explorer: adapt the tests to develop (mine). Details below.

Two more commits add the screenshots below under pr-screenshots/ and remove them again, so the description can link them by commit. They leave no files behind.

Removed for develop (the AI Workspace dependency)

WordPress\AI\Experiments\AI_Workspace\Tool_Policy does not exist on develop, and the picked code would fatal on it. Removed:

  • PHP:
    • the Tool_Policy import and calls in REST/Abilities_Controller.php and Ability_Handler.php
    • the ai/v1/abilities/surface route and its SURFACE_ROUTE and SURFACE_CHANGES constants
    • change_surface(), policy_state(), and the policy and sequence fields of the list response. The sequence numbers existed only so a late list refresh could not undo a surface change. Refresh is disabled while a load is in flight, so list requests can't overlap.
    • get_surface_reason_labels(), get_surface_reason_label() and the surfaceReasonLabels localized setting
    • the list item fields conversational_surface, surface_reason, surface_reason_label and owner_excluded, plus show_in_rest and show_in_mcp. The last two only fed the "Exposed in" column's REST and MCP badges. Each row still carries meta.
  • React:
    • the "Exposed in" field and filter (components/list/ExposedIn.tsx), SurfaceActions.tsx and PolicyToggle.tsx
    • their uses in fields.tsx, NameCell.tsx, ListView.tsx, App.tsx, api.ts (changeSurface() and the response sequencer) and types.ts
    • the column's minimum-width layout style, the focus-recovery logic for surface changes, and the related SCSS
  • Tests:
    • tests/e2e/specs/experiments/abilities-explorer-surface.spec.js
    • the surface constants and payloadRow in tests/e2e/utils/abilities-explorer.js
    • the assistant fixtures, admission and withheld filters, and surface-option reset in tests/e2e-testing/e2e-testing.php
    • the list spec's "Exposed in" test
    • the PHPUnit cases for:
      • the surface route
      • assistant state and reasons
      • model declarations
      • the description shared with Streaming_Turn_Driver
      • REST and MCP exposure
      • the reason labels
  • Docs: the Workspace text and the "Exposed in" and policy steps in docs/experiments/abilities-explorer.md.

The code has no class_exists() guards or dead branches. The ai.abilitiesExplorer.fields filter stays, so the Workspace can add its column back from its own code.

Adapted for develop

Fixes on develop since the branch point

git log d0b54f07..origin/develop -- includes/Experiments/Abilities_Explorer src/experiments/abilities-explorer tests lists 33 commits. Five touch files #1074 changes:

The other 28 commits touch none of the files #1074 changes.

REST routes

All three are registered only while the experiment is on, and none is registered as an ability:

Route Method Purpose
ai/v1/abilities GET every registered ability, including ones without show_in_rest
ai/v1/abilities/item?name= GET one ability with schemas, raw data and example input
ai/v1/abilities/invoke POST runs the ability through WP_Ability::execute(), so its own permission check still applies

They share one permission check:

  • the user has manage_options
  • cookie authentication
  • a wp_rest nonce, checked in the callback itself
  • no application passwords

Abilities_ControllerTest still covers each refusal on every route. Abilities_ExplorerTest covers registration with the experiment on and off, and the navigation e2e spec covers the screen being unreachable with the experiment off and with AI globally off.

Behavior changes worth knowing

  • Invoke now runs under REST, where is_admin() is false. The route loads wp-admin/includes/admin.php first.
  • The list is built from a REST request too, so an ability a plugin registers only when is_admin() is true no longer appears.
  • Errors show the ability's code, message and data instead of the always-null trace.
  • The admin-ajax action ai_ability_explorer_invoke and the Ability_Table class are removed.
  • Old list bookmarks with s, orderby or filter args land on the unfiltered list.
  • Back to List keeps the view's layout, columns, sort and page size. Search and filters reset.
  • Row actions (View and Test) sit under each ability's name and show on hover or keyboard focus, as in WP_List_Table. They are always shown below 782px.

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: The cherry-pick's conflict resolution, removing the AI Workspace dependency, adapting the tests to develop, running the checks, and drafting this description. #1074's own code was also written with Claude Code, per its disclosure.

Testing Instructions

  1. Enable the Abilities Explorer experiment, then open Tools > Abilities Explorer.
  2. Filter by Provider and Category, search, sort and page. The statistics should not change with search or filters.
  3. Open an ability's detail view, copy a schema, then open Test Ability. With Content Classification enabled, for ai/content-classification, max_suggestions 11 should report "must be at most 10", and 5 should validate.
  4. Use browser Back and reload on each view.
  5. Turn the experiment off. The menu item should be gone, and tools.php?page=ai-abilities-explorer should report that you are not allowed to access the page.

Checks run

wp-env could not start in the environment I worked in: its network policy blocks wordpress.org and the Alpine package mirror the wp-env images build from. I ran the same tools directly instead, so CI on this PR is the authoritative run.

  • npm run build, npm run typecheck: pass.
  • npm run lint:js: 0 errors. The one new warning (jsdoc/reject-function-type) is on restRoute() in tests/e2e/utils/abilities-explorer.js, from Abilities Explorer: move to DataViews and REST #1074.
  • lint:php (composer run-script lint) and lint:php:stan (composer run-script phpstan), run on local PHP 8.3 rather than in the wp-env container: pass, PHPStan with no errors.
  • test:php: run against the WordPress 7.1.3 PHPUnit suite on the SQLite database drop-in, not MySQL.
    • The Explorer tests in tests/Integration/Includes/Experiments/Abilities_Explorer/: 76 tests, all pass.
    • The full suite: 1952 tests, with 45 errors and failures in other features. All 45 also fail on develop in the same setup. They come from SQLite (table schema checks) and plugin path detection. No test fails only on this branch.
  • test:e2e: run against WordPress 7.1.3 on PHP's built-in server with SQLite, Chromium, with the OpenAI and Google provider plugins taken from their GitHub repos.
    • The three Explorer specs (list, runner, navigation): 17 tests, all pass.
    • The full suite: 215 passed, 1 failed. The failure is connector-approval.spec.js:23 ("Can use the Connector Approval Experiment", its .notice-warning never appears), which fails the same way on develop here.
  • Manual smoke test, scripted with Playwright, following the testing steps above:
    • The list shows the statistics and the Name, Slug and Provider columns.
    • Neither the Provider (Core) filter, the Category filter nor search changes the statistics.
    • Sort descending works. Paging was not exercised: the site had 14 abilities, under one page.
    • The detail view's Copy works.
    • max_suggestions 11 reports Field "max_suggestions" must be at most 10, and 5 reports "Input is valid according to the schema".
    • Back and reload keep the view, with one h1.

Screenshots or screencast

Same site and abilities, WordPress 7.1.3. Before is develop, after is this branch.

Before After
List Before: List After: List
Detail view Before: Detail view After: Detail view
Test runner, max_suggestions 11 Before: Test runner, max_suggestions 11 After: Test runner, max_suggestions 11

Changelog Entry

Changed - Rebuilt the Abilities Explorer on DataViews and three ai/v1 REST routes, with an ai.abilitiesExplorer.fields JavaScript filter for adding read-only table columns. The ai_ability_explorer_invoke admin-ajax action and the Ability_Table class are removed, abilities registered only when is_admin() is true no longer appear, and old bookmarked filter URLs open the unfiltered list.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DM4Q4KoGRf8wxhcCQy1hfA

Open WordPress Playground Preview

whyisjake and others added 3 commits October 14, 2026 13:31
Closes #1016. See #203.

Moves the whole Abilities Explorer screen (list, statistics, the
"Exposed in" column and its actions, detail view, test runner) off
`WP_List_Table`, vanilla JS and admin-ajax onto a `@wordpress/dataviews`
screen backed by four new `ai/v1` routes. Stacked on `feat/ai-workspace`
(#1004), because the "Exposed in" column and its actions exist only
there.

The Explorer was the plugin's last admin screen on the old stack, and
its hand-written search, sort and filters kept needing repair (#883,
owner most needs answered, which abilities the assistant can reach, but
it could not be filtered without more hand-written filtering. #203 asked
for a custom-column hook, which would have shipped an API against a
table about to be replaced.

**REST routes**
(`includes/Experiments/Abilities_Explorer/REST/Abilities_Controller.php`),
registered only while the experiment is on and never as abilities:

| Route | Method | Purpose |
|---|---|---|
| `ai/v1/abilities` | GET | every registered ability, including ones
without `show_in_rest`, plus the assistant policy state |
| `ai/v1/abilities/item?name=` | GET | one ability with schemas, raw
data and example input |
| `ai/v1/abilities/invoke` | POST | runs the ability through
`WP_Ability::execute()`, so its own permission check still applies |
| `ai/v1/abilities/surface` | POST | remove, restore, disable_policy or
enable_policy |

All four share one permission check: `manage_options`, cookie
authentication, a valid `wp_rest` nonce, and no application password.
The nonce is checked in the callback itself because core skips its own
check when an earlier authentication filter has already answered.
Ability names travel in the query string or body, never the path, so an
encoded `/` cannot 404 on Apache. Each list item is encode-checked on
its own, so one ability with unencodable data cannot blank the list.

**Screen** (`src/experiments/abilities-explorer/`): a React app mounted
like the AI Request Logs page. It routes between list, detail, runner
and not-found on the existing `action` and `ability` query args, with
browser Back and Forward. The provider filter keeps the #883 rule
(origin for Core, Plugin and Theme, exact label otherwise). "Exposed in"
is now filterable by assistant state. Surface changes wait for the
server, and responses carry a sequence number, tracked per row, so a
late refresh cannot undo a newer change.

**#203:** plugins that depend on `wp-hooks` can add read-only fields
with the `ai.abilitiesExplorer.fields` filter. Built-in field IDs win on
a collision, a filter that throws or returns a non-array leaves the
built-ins, and a saved view keeps a third-party column's ID while its
plugin is inactive. Documented in
`docs/experiments/abilities-explorer.md`.

**Behavior changes worth knowing:**
- Invoke now runs under REST, where `is_admin()` is false. The route
loads `wp-admin/includes/admin.php` first.
- The list is built from a REST request too, so an ability a plugin
registers only when `is_admin()` is true no longer appears.
- Errors show the ability's code, message and data instead of the
always-null `trace`.
- The admin-ajax action `ai_ability_explorer_invoke` and the
`Ability_Table` class are removed.
- Old list bookmarks with `s`, `orderby` or filter args land on the
unfiltered list.
- Back to List keeps the view's layout, columns, sort and page size.
Search and filters reset.
- Row actions (View, Test, and Remove from or Return to assistant) sit
under each ability's name and show on hover or keyboard focus, as in
`WP_List_Table`. They are always shown below 782px.

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Planning, implementation of the REST controller, React screen,
tests and docs, and a multi-reviewer code review whose confirmed
findings were fixed in this branch.

<!-- JAKE: add your review attestation here in your own words before
opening. -->

1. Enable Experiments and the Abilities Explorer experiment, then open
**Tools > Abilities Explorer**.
2. Filter by Provider, Category and "Exposed in", search, sort and page.
The statistics should not change with search or filters.
3. With the AI Workspace experiment on, remove an ability from the
assistant, return it, and turn the admission policy off and on. Each
change should show a notice.
4. Open an ability's detail view, copy a schema, then open **Test
Ability**. For `ai/content-classification`, `max_suggestions` 11 should
report "must be at most 10", and 5 should validate.
5. Use browser Back and reload on each view.

- `npm run build`, `npm run typecheck`, `npm run lint:js`, `npm run
lint:php`, `npm run lint:php:stan`: all pass.
- `npm run test:php`: 1988 tests, 40 skipped, 0 failures.
- Explorer e2e, 24 tests across four files, all passing:
  - `tests/e2e/specs/experiments/abilities-explorer-list.spec.js`:
    - shows the statistics and the table under a single heading
    - narrows the rows with the "Category" filter
    - matches "Plugin" by origin and "Acme" by its exact label
    - keeps the statistics unaffected by search
    - filters "Exposed in" down to what the assistant can reach
- restores layout, fields and sort from a saved view, but not search or
filters
    - loads a saved view with an unknown field ID and keeps the ID
  - `tests/e2e/specs/experiments/abilities-explorer-surface.spec.js`:
    - removes an ability and returns it, with a notice for each
- returning a withheld ability shows the withheld reason, not the
assistant badge
    - turns the admission policy off and on, updating every row
    - disables a pending change, so a second click sends nothing
    - does not let a refresh answered after a remove undo it
    - moves focus to the list when a change filters its row out
  - `tests/e2e/specs/experiments/abilities-explorer-runner.spec.js`:
    - shows each detail section and copies the JSON
    - validates deep-linked input against the schema
    - shows the success and error panels, and Clear hides them
    - never shows one runner the result of another ability in flight
  - `tests/e2e/specs/experiments/abilities-explorer-navigation.spec.js`:
    - keeps one heading per view, and Back and reload keep the view
- fetches an ability again when it is opened after returning to the list
- shows an extension column on first load and keeps it through
deactivation
    - keeps the built-in columns when the filter returns a non-array
    - does not let an extension replace a built-in field
    - is unreachable when the experiment is off
    - is unreachable when AI is globally off
- Full `npm run test:e2e`: running at the time this PR was opened; the
result will be added below.

<details><summary>PHPUnit tests added or moved in this branch
(67)</summary>

`Abilities_ExplorerTest` (2):
- `test_rest_routes_are_registered_when_enabled`
- `test_rest_routes_are_absent_when_disabled`

`Ability_HandlerTest` (5):
- `test_validate_input_accepts_a_type_list`
- `test_generate_example_input_returns_empty_for_empty_schema`
- `test_generate_example_input_uses_default_values`
- `test_generate_example_input_uses_example_values`
- `test_generate_example_input_generates_type_defaults`

`Admin_PageTest` (12):
- `test_load_hook_registers_help_tabs_and_assets`
- `test_assets_are_hooked_only_when_the_screen_loads`
- `test_enqueue_assets_enqueues_bundle_and_dataviews_fallback`
- `test_enqueue_assets_skips_dataviews_fallback_when_core_registers_it`
- `test_enqueue_assets_does_nothing_for_an_editor`
- `test_localized_settings_route_map_matches_controller_constants`
- `test_localized_settings_carry_reason_and_provider_labels`
- `test_render_page_works_for_admin`
- `test_render_page_ignores_action_for_server_render`
- `test_render_page_outputs_nothing_for_editor`
- `test_render_page_outputs_nothing_when_logged_out`
- `test_help_tabs_register_on_screen`

`Abilities_ControllerTest` (48):
- `test_list_includes_an_ability_not_shown_in_rest`
-
`test_list_keeps_origin_and_provider_apart_and_sends_the_category_label_unescaped`
- `test_list_decodes_a_category_label_escaped_by_a_filter`
- `test_list_survives_an_ability_with_unencodable_meta`
- `test_item_returns_schemas_and_example_input`
- `test_item_returns_404_for_an_unregistered_name`
- `test_item_returns_400_for_a_missing_name`
- `test_invoke_with_valid_input_returns_the_data`
- `test_invoke_with_input_failing_explorer_validation_returns_400`
-
`test_invoke_with_input_failing_core_validation_returns_the_ability_error`
- `test_invoke_accepts_a_property_typed_with_a_type_list`
- `test_invoke_without_an_input_schema_accepts_empty_and_null`
- `test_invoke_with_input_omitted_invokes_with_no_input`
- `test_invoke_decodes_scalar_input_on_the_server`
- `test_invoke_with_malformed_json_returns_400`
- `test_invoke_refuses_non_string_input`
- `test_invoke_returns_404_for_an_unregistered_ability`
- `test_invoke_reports_the_ability_permission_denial_as_its_outcome`
- `test_invoke_writes_no_request_log_row`
- `test_surface_remove_stores_the_exclusion_and_a_repeat_is_no_change`
- `test_surface_restore_clears_the_exclusion`
-
`test_surface_restore_of_a_withheld_ability_keeps_it_off_the_assistant`
- `test_surface_policy_switch_disables_and_enables_and_returns_the_list`
- `test_surface_refuses_an_unknown_change`
- `test_surface_remove_refuses_an_unregistered_or_missing_name`
- `test_surface_get_changes_nothing`
- `test_list_reports_only_known_origins`
- `test_list_carries_a_custom_provider_beside_the_known_origins`
- `test_list_lets_a_custom_provider_row_match_its_origin_and_its_label`
- `test_list_marks_a_declared_admitted_ability_as_on_the_assistant`
- `test_list_carries_the_reason_an_ability_is_off_the_assistant`
- `test_list_and_function_declaration_share_one_description_source`
-
`test_list_does_not_report_a_removed_ability_as_held_without_the_workspace_bootstrap`
- `test_list_reports_the_general_public_flag_on_both_channels`
- `test_list_reports_rest_and_mcp_exposure_independently`
- `test_surface_remove_takes_the_ability_off_the_model_declarations`
- `test_surface_restore_returns_the_ability_to_the_model_declarations`
- `test_surface_policy_switch_withdraws_and_returns_admitted_abilities`
- `test_editor_is_refused_on_every_route`
- `test_application_password_is_refused_on_every_route`
- `test_application_password_is_refused_even_alongside_the_cookie_flag`
-
`test_determine_current_user_without_a_cookie_is_refused_on_every_route`
- `test_cookie_administrator_without_a_nonce_is_refused_on_every_route`
-
`test_cookie_administrator_with_an_invalid_nonce_is_refused_on_every_route`
-
`test_cookie_administrator_with_a_nonce_for_another_action_is_refused_on_every_route`
- `test_cookie_administrator_with_the_nonce_as_a_parameter_is_accepted`
- `test_cookie_administrator_reaches_every_route`
- `test_no_route_is_registered_as_an_ability`
</details>

From the branch's code review, not blocking:
- [ ] P3 — `src/experiments/abilities-explorer/api.ts` — The response
sequencer and `validate.ts` have no JS unit tests, because the repo has
no JS unit test setup. They are covered only through e2e.
- [ ] P3 —
`includes/Experiments/Abilities_Explorer/REST/Abilities_Controller.php`
— The invoke success payload is not passed through the strict encode
check the list and item use, so an ability returning NaN or INF produces
a generic client error.
- [ ] P3 — `src/experiments/abilities-explorer/fields.tsx` —
`BUILT_IN_FIELD_IDS` is a hand-kept list. Deriving it from
`getBuiltInFields()` would stop a new built-in field from being
overridable.
- [ ] P3 — Response ordering uses one server's `microtime()`. On
multi-node hosting with clock skew, a stale list could briefly show a
row's old state until the next refresh.

> Changed - Rebuilt the Abilities Explorer on DataViews with REST
routes, a filterable "Exposed in" column, and a JavaScript filter for
adding read-only table fields.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- wp-playground-preview:start -->
<a
href="https://playground.wordpress.net?blueprint-url=data:application/json,%7B%22preferredVersions%22%3A%7B%22wp%22%3A%22latest%22%7D%2C%22login%22%3Atrue%2C%22landingPage%22%3A%22%2Fwp-admin%2Fadmin.php%3Fpage%3Dai-wp-admin%22%2C%22steps%22%3A%5B%7B%22step%22%3A%22installPlugin%22%2C%22pluginData%22%3A%7B%22resource%22%3A%22url%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2FWordPress%2Fai%2Freleases%2Fdownload%2Fci-artifacts%2Fpr-1074-88aa03bda577ce567570cbd90eb3be378851aad0.zip%22%7D%7D%5D%7D"
target="_blank" rel="noopener noreferrer">
<img
src="https://raw.githubusercontent.com/adamziel/playground-preview/refs/heads/trunk/assets/playground-preview-button.svg"
alt="Open WordPress Playground Preview" width="220" height="57" />
</a>
<!-- wp-playground-preview:end -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

Conflicts resolved while picking onto develop:
- Ability_Table.php, index.js, Ability_TableTest.php and
  abilities-explorer.spec.js: kept the deletion.
- .wp-env.test.json: added only the abilities-explorer-field plugin, not
  the ai-provider-for-anthropic zip from the Workspace branch.
- Admin_Page.php, Ability_Handler.php, index.scss: took this commit's side.
- Ability_HandlerTest.php: kept the type-list test from #1073 beside this
  commit's, with its expected message updated to the ", " join used here.

(cherry picked from commit 0c96aab)

Co-authored-by: Jeff Paul <jeff.paul@fueled.com>
The DataViews rewrite from #1074 was built on the AI Workspace branch
(#1004), which is not on develop. Its "Exposed in" column and the
assistant surface controls read and write AI_Workspace\Tool_Policy, which
does not exist here and would fatal.

Removes:
- The `ai/v1/abilities/surface` route, `policy_state()` and the response
  sequence numbers, which only kept a late list refresh from undoing a
  surface change.
- The surface reason labels and the `show_in_rest`, `show_in_mcp`,
  `conversational_surface`, `surface_reason`, `surface_reason_label` and
  `owner_excluded` list fields. Each row still carries `meta`.
- The "Exposed in" field and filter, the row and policy surface actions,
  and the client response sequencer.
- The surface e2e spec, its helpers and fixtures, and the PHPUnit cases
  for the surface route, assistant state and model declarations.
- The AI Workspace text in the Explorer docs.

The `ai.abilitiesExplorer.fields` filter stays, so the Workspace can add
its column back from its own code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DM4Q4KoGRf8wxhcCQy1hfA
Co-authored-by: Jeff Paul <jeff.paul@fueled.com>
- The "unreachable when AI is globally off" e2e test switched off the
  `wpai_features_enabled` option, which #985 removed from develop, so the
  Explorer stayed reachable and the test failed. The E2E Testing plugin
  now has an `ai-e2e/v1/ai-disabled` switch that turns every feature off
  through the `wpai_features_enabled` filter, which is how AI is globally
  off on develop.
- `test_no_route_is_registered_as_an_ability` boots every experiment, and
  Markdown Feeds (on develop, not on the Workspace branch) adds its feed
  to the global `$wp_rewrite`. The test now restores the feeds, so a later
  Markdown_FeedsTest is not left with a doubled feed URL.
- The manual testing step no longer says to turn AI on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DM4Q4KoGRf8wxhcCQy1hfA
Co-authored-by: Jeff Paul <jeff.paul@fueled.com>
@jeffpaul
jeffpaul requested a review from whyisjake October 14, 2026 13:33
@jeffpaul jeffpaul added this to the 1.5.0 milestone Oct 14, 2026 — with Image Claude
@jeffpaul jeffpaul self-assigned this Oct 14, 2026
@github-actions

Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.62656% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.74%. Comparing base (5bfb356) to head (4ee5785).

Files with missing lines Patch % Lines
...Experiments/Abilities_Explorer/Ability_Handler.php 58.82% 21 Missing ⚠️
...udes/Experiments/Abilities_Explorer/Admin_Page.php 91.42% 3 Missing ⚠️
...s/Abilities_Explorer/REST/Abilities_Controller.php 99.35% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #1181      +/-   ##
=============================================
+ Coverage      82.25%   83.74%   +1.49%     
+ Complexity      3403     3388      -15     
=============================================
  Files            136      136              
  Lines          13225    13062     -163     
=============================================
+ Hits           10878    10939      +61     
+ Misses          2347     2123     -224     
Flag Coverage Δ
unit 83.74% <89.62%> (+1.49%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

claude and others added 2 commits October 14, 2026 15:58
… the next commit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DM4Q4KoGRf8wxhcCQy1hfA
Co-authored-by: Jeff Paul <jeff.paul@fueled.com>
They stay reachable from the previous commit, which the PR description links to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DM4Q4KoGRf8wxhcCQy1hfA
Co-authored-by: Jeff Paul <jeff.paul@fueled.com>
whyisjake
whyisjake previously approved these changes Oct 14, 2026

@whyisjake whyisjake left a comment

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.

Thanks for carrying this over, @jeffpaul. Approving.

I checked the cherry-pick (a51a92e) against the original squash commit (0c96aab): the only differences are the conflict resolutions listed in the description. The Workspace removal in b6c78b5 is complete. Nothing in the Explorer's code, tests or docs still refers to Tool_Policy, AI_Workspace, the surface route, or the response sequence numbers. Dropping the sequencer is safe, since Refresh is disabled while a load is in flight and the surface writes it guarded against are gone. The ai.abilitiesExplorer.fields filter stays, so #1004 can add the "Exposed in" column back from its own code. The test adaptations in 2a77b4b look right: the AI-off switch goes through the same wpai_features_enabled filter Features\Loader checks, and restoring $wp_rewrite->feeds fixes real cross-test pollution.

One optional suggestion: ai_e2e_ai_disabled is a persistent option. If a run is killed while it's on, every later run starts with AI globally off and fails in ways that look unrelated. Deleting it in tests/e2e/config/global-setup.ts, next to the credentials seed, would prevent that.

AI assistance: Yes. Tool(s): Claude Code. Used for: comparing the cherry-pick to the original commit and checking for leftover AI Workspace references.

@whyisjake
whyisjake marked this pull request as ready for review October 14, 2026 17:12
@whyisjake
whyisjake requested a review from a team October 14, 2026 17:12
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

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.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @claude.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: claude.

Co-authored-by: whyisjake <whyisjake@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>
Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: raftaar1191 <raftaar1191@git.wordpress.org>

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

`ai_e2e_ai_disabled` is a stored option, so a run stopped while it was on
left AI globally off for every later run, which then failed in specs
unrelated to the Explorer. The global setup now switches it off before
any spec runs.

Suggested by @whyisjake in review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DM4Q4KoGRf8wxhcCQy1hfA
Co-authored-by: Jeff Paul <jeff.paul@fueled.com>

Copy link
Copy Markdown
Member Author

@whyisjake thanks. Your suggestion is in as d997063: tests/e2e/config/global-setup.ts now switches ai_e2e_ai_disabled off right after seeding the credentials.

I checked it by turning the switch on and then running a spec that never touches it (core-settings-get, "filters by fields"). Without the change the switch stayed on and that spec failed. With it, global setup cleared the option and the spec passed.


Generated by Claude Code

@jeffpaul
jeffpaul requested a review from whyisjake October 14, 2026 17:17

Copy link
Copy Markdown
Member Author

Test PHP 7.4 WP 7.0.4 and Test PHP 8.0 WP trunk failed on d997063 before any test ran, and the failure isn't this PR's.

Both jobs failed while wp-env built its PHP Docker image. The image is based on Debian bullseye. At RUN apt-get -qy install git, archive.debian.org returned 404 for xauth_1.1-1_amd64.deb, on all three of the workflow's own start attempts:

E: Failed to fetch http://archive.debian.org/debian/pool/main/x/xauth/xauth_1.1-1_amd64.deb  404  Not Found
ERROR: process "/bin/sh -c apt-get -qy install git" did not complete successfully: exit code: 100

This commit only changes tests/e2e/config/global-setup.ts, which PHPUnit never loads. The same jobs passed on the previous head, af66bc8, and the image build lives in @wordpress/env, not in this repo. No fix exists to port yet. If the Debian archive stays broken, the fix belongs upstream in wp-env's Dockerfile, for example installing git with --no-install-recommends, or in a separate PR here. I'll re-run the failed jobs once when this run finishes.


Generated by Claude Code

@jeffpaul

Copy link
Copy Markdown
Member Author

Tested and this works as expected, opening up for review.

@dkotter dkotter left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Couple flags around what I'd consider AI slop but otherwise things test well.

Beyond those things, I have some suggestions on the UI:

  1. Currently there's no spacing between the main page header and the content below it:
Image
  1. Personal opinion but I don't love the fixed header as you scroll. Not sure if provides much value here as there's not much in that header. I'd suggest we keep that at the top instead of having it scroll with you.

  2. There's a lot of whitespace now in the table. Can we show the Abilities descriptions in a column? That fills the space and provides some nice details:

Image
  1. Not sure we need a Refresh button in the header. Do Abilities change often enough that someone would want a button they can click, especially when they can just use the browser refresh button?
Image
  1. Might be nice to move the help context menu into the new UI. Right now that sticks out a bit compared to how the rest of the page looks.

whyisjake added a commit that referenced this pull request Oct 15, 2026
…d in the next commit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@whyisjake

Copy link
Copy Markdown
Member

Thanks for the review @dkotter. I opened stacked draft PR #1193 against this branch with the UI fixes. Merging it lands the commits here. It has three commits:

Before After (#1193)
Abilities Explorer list before: no gap under the header, a Refresh button, and no description column Abilities Explorer list after: spacing under the header, no Refresh button, and a wrapped description column

The Abilities Explorer e2e specs pass locally (17 passed).

Two things I need your input on before I touch them:

  • Fixed header: I read "I'd suggest we keep that fixed" as "not fixed". Did you mean the header should scroll away with the page?
  • Help tab: what do you have in mind? Fold its content into the page (a panel or a link in the header, say), or drop it?

You're right about the tests and the docblocks, so I'll do those in a separate follow-up pass:

  • Go through the tests and keep the ones that guard real behaviour (the REST controller, input validation, the test runner).
  • Fold the field fixture into the existing tests/e2e-testing plugin.
  • Drop the plumbing-only tests.
  • Trim docblocks to the ones that add something, starting with includes/Experiments/Abilities_Explorer/REST/Abilities_Controller.php.

AI assistance: Yes. Tool(s): Claude Code. Used for: the changes in #1193 and drafting this comment.

@whyisjake

Copy link
Copy Markdown
Member

@dkotter the follow-up on the tests and comments is up in two more PRs, alongside #1193:

All three pass the Explorer's PHPUnit and e2e tests locally. The open questions on the fixed header and the Help tab still stand.

AI assistance: Yes. Tool(s): Claude Code. Used for: the changes in #1194 and #1196 and drafting this comment.

whyisjake
whyisjake previously approved these changes Oct 15, 2026
@whyisjake
whyisjake dismissed dkotter’s stale review October 15, 2026 21:58

Changes addressed.

whyisjake and others added 2 commits October 15, 2026 15:09
…tton (#1193)

Abilities Explorer: header spacing, description column, no Refresh button

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Darin Kotter <darin.kotter@gmail.com>
whyisjake and others added 2 commits October 15, 2026 16:17
Abilities Explorer: trim the tests to the behavior they guard

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1196)

Abilities Explorer: trim the comments to the ones that explain why

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Darin Kotter <darin.kotter@gmail.com>
@dkotter

dkotter commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Fixed header: I read "I'd suggest we keep that fixed" as "not fixed". Did you mean the header should scroll away with the page?

Sorry, typo on my part. I've fixed this myself so we're good here.

Help tab: what do you have in mind? Fold its content into the page (a panel or a link in the header, say), or drop it?

This requires some exploration/trying things out. I'm assuming there's a component in the @wordpress/admin-ui/@wordpress/ui package we could use that would better fit the new DataViews experience but I don't know off the top of my head.

Fine to leave as-is for now, was flagging as it would have been nice to tackle now instead of pushing off for later.

@dkotter
dkotter merged commit d6b6ad7 into develop Oct 15, 2026
36 checks passed
@dkotter
dkotter deleted the claude/abilities-explorer-dataviews-develop-2as8vr branch October 15, 2026 22:47
whyisjake added a commit that referenced this pull request Oct 29, 2026
The Abilities Explorer reached develop through #1181, which took the
#1074 rewrite off this branch and removed its AI Workspace dependency
(b6c78b5). The Explorer files take develop's version, including the
review changes from #1193, #1194 and #1196, with the workspace's surface
integration put back on top: the "Exposed in" field and filter, the row
and policy surface actions, the response sequencer, the surface route,
the list fields Ability_Handler adds from Tool_Policy, and their tests.

Develop removed the Refresh button, so the list loads once and the
sequencer no longer has a refresh to order against. The e2e test for a
refresh answered after a remove is dropped with it. The list, navigation
and runner specs this branch split out are dropped in favour of
develop's single abilities-explorer.spec.js, and the field-extension
plugin is replaced by develop's e2e_fields switch. The surface spec and
its helpers stay.

The e2e fixtures keep develop's always-on Explorer abilities and add the
workspace's switched assistant fixtures and sequenced mock scenarios.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Move the Abilities Explorer to DataViews Add extensibility hook for custom Ability Table columns

4 participants