Knowledge: add a Knowledge and Guidelines experiment - #949
Draft
gziolo wants to merge 2 commits into
Draft
Conversation
Adds the `wp_knowledge` storage layer and a Settings > Guidelines page, ported from the Gutenberg plugin's `lib/experimental/knowledge` and `routes/guidelines`. The Gutenberg plugin ships the same feature behind its `gutenberg-guidelines` flag. Both share one contract, so only one may provide it. The rule is first to declare it wins: every shared `wp_*` function sits behind `function_exists()`, the classes are namespaced so there is no redeclare, and `Knowledge_Post_Type::register()` returns early when `wp_knowledge` already exists. Ownership then gates the scopes route and the admin page, so the two plugins never produce a duplicate page or route. The Guidelines service read the removed `wp_guideline` post type and its post meta, so guideline injection into prompts had stopped working against current Gutenberg. It now reads `wp_knowledge` rows by slug. The public API is unchanged, so the abilities using it need no edits. `isContentBlock` is a private API in `@wordpress/blocks` that a plugin cannot unlock, so the route reimplements it on public APIs.
The lock was regenerated with npm 11 on Node 24, but CI follows .nvmrc and runs npm 10 on Node 22. The newer npm dropped four transitive entries that npm 10 still resolves, so npm ci failed on a lock that was out of sync with package.json. Regenerated with Node 22, which leaves a single added line for the new @wordpress/blob dependency.
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #949 +/- ##
=============================================
- Coverage 73.86% 73.86% -0.01%
- Complexity 3032 3089 +57
=============================================
Files 132 138 +6
Lines 12004 12443 +439
=============================================
+ Hits 8867 9191 +324
- Misses 3137 3252 +115
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
Adds a new Knowledge and Guidelines experiment. It brings the
wp_knowledgestorage layer and a Settings → Guidelines page into the AI plugin, ported from the Gutenberg plugin'slib/experimental/knowledgeandroutes/guidelines.It also fixes the
Guidelinesservice, which had quietly stopped working.Why?
Two reasons.
The AI plugin could not manage guidelines on its own. Site guidelines are useful context for a lot of what this plugin already does. Until now they only existed if you also ran the Gutenberg plugin with its
gutenberg-guidelinesexperiment turned on. This makes the AI plugin able to provide them by itself.Guideline injection into prompts was broken.
includes/Services/Guidelines.phpread thewp_guidelinepost type and its_guideline_copystyle post meta. Gutenberg replaced that model some time ago withwp_knowledgerows, so on current Gutenberg the service found nothing and every prompt went out without guidelines. Five abilities were affected: Title Generation, Excerpt Generation, Meta Description, Editorial Notes, and Type Ahead.How?
First plugin to declare it wins
Gutenberg ships the same feature. Both plugins share one contract, so only one of them may provide it. This PR uses the same three mechanisms Gutenberg already uses, so neither side needs to know about the other:
knowledge-functions.phpsits insideif ( ! function_exists( ... ) ). The names keep thewp_prefix on purpose. They are a shared contract, not plugin-private code.Gutenberg_Knowledge_Post_Type. This plugin has the namespacedWordPress\AI\Experiments\Knowledge\Knowledge_Post_Type. There is never a redeclare error, so the decision happens at the behaviour level instead.Knowledge_Post_Type::register()returns early whenpost_type_exists( 'wp_knowledge' )is already true, and reports back whether it registered anything.Knowledge::register()stores that answer. The scopes REST route and the Settings page are registered only when this plugin owns the feature, so the two plugins can never produce a duplicate page or a duplicate route.Gutenberg loads its copy at plugin-load time and registers the post type on
initpriority 10. This experiment runs oninitpriority 15. So Gutenberg wins whenever its flag is on, and this plugin provides everything when the flag is off or Gutenberg is not installed.The admin page slug is
guidelines-wp-admin, the same one Gutenberg uses. The URL staysoptions-general.php?page=guidelines-wp-adminno matter which plugin serves the page.Owning versus extending
These are separate. Anything that only wants to add a scope or a knowledge type should use the
wp_guideline_scopesandwp_knowledge_typesfilters. Those work no matter who owns the base implementation, and the Settings page grows a section automatically.docs/experiments/knowledge.mdcovers this with an example.New files
PHP in
includes/Experiments/Knowledge/:Knowledge.phpknowledge-functions.phpwp_*functions, each behind afunction_exists()guard.Knowledge_Post_Type.phpwp_knowledgepost type, thewp_knowledge_typetaxonomy, and the dynamic capabilities.Knowledge_REST_Controller.php/wp/v2/knowledgeso reads need a login and new rows default toprivate.Guideline_Scopes_REST_Controller.php/wp/v2/knowledge/guideline-scopes.Admin_Page.phpUI in
routes/guidelines/, a port of Gutenberg's route."guidelines"is added towpPlugin.pages, which generatesai_guidelines_wp_admin_render_page()and the matching page slug.Notes on the port
Two things needed changing rather than copying:
isContentBlockis a private API. Gutenberg'sdata.tsunlocks it from@wordpress/blocks, which a plugin cannot do. The route reimplements it on public APIs inroutes/guidelines/data.ts. Same logic: a block qualifies if it setssupports.contentRoleor has an attribute with thecontentrole.exactOptionalPropertyTypes,noUncheckedIndexedAccess, andnoPropertyAccessFromIndexSignaturemeant about 20 small adjustments. Worth knowing thatnoPropertyAccessFromIndexSignatureand ESLint'sdot-notationrule pull in opposite directions, so the affected spots were restructured to use typed shapes instead of satisfying one and breaking the other.eslint.config.mjsgains a scoped block that turns off@wordpress/no-unsafe-wp-apisforroutes/guidelines/**only. The reason is to keep the port close to the upstream source, so future Gutenberg changes stay easy to diff and re-apply.@wordpress/uicoversStackbut has noHeading, no mutedTextvariant, and itsAlertDialogis trigger-based whereConfirmDialogis controlled. Happy to do the full swap instead if reviewers would rather not have the exception.Guidelines service
includes/Services/Guidelines.phpnow reads publishedwp_knowledgerows by exact slug:guideline-{scope}for a scope, andguideline-block-{block_name}for a block. Details worth flagging for review:wp_guideline_scopes()when it is available, so scopes added by other plugins are picked up too. There is a fallback list for the case where the post type exists but the registry function does not._, not-, sofoo/bar-bazandfoo-bar/bazcannot collide. There is a test for this.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Reading the Gutenberg source, writing the port and the adaptation work, and drafting the tests and docs. All of it was reviewed and adjusted by me.
Testing Instructions
Without Gutenberg
<guidelines>block.With Gutenberg,
gutenberg-guidelinesonWith Gutenberg,
gutenberg-guidelinesoffAutomated
All pass. The PHP suite is 1384 tests and 3956 assertions, with 44 pre-existing skips. 11 of those tests are new and cover the experiment, including that a second
register()call stands down.Screenshots or screencast
Changelog Entry
Dependency note:
package.jsongains@wordpress/blob, which the export flow needs. It was already in the tree as a transitive dependency at the same version, sopackage-lock.jsonchanges by a single line.🤖 Generated with Claude Code