Skip to content

Knowledge: add a Knowledge and Guidelines experiment - #949

Draft
gziolo wants to merge 2 commits into
developfrom
add/knowledge-guidelines-experiment
Draft

Knowledge: add a Knowledge and Guidelines experiment#949
gziolo wants to merge 2 commits into
developfrom
add/knowledge-guidelines-experiment

Conversation

@gziolo

@gziolo gziolo commented Aug 18, 2026

Copy link
Copy Markdown
Member

What?

Adds a new Knowledge and Guidelines experiment. It brings the wp_knowledge storage layer and a Settings → Guidelines page into the AI plugin, ported from the Gutenberg plugin's lib/experimental/knowledge and routes/guidelines.

It also fixes the Guidelines service, 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-guidelines experiment turned on. This makes the AI plugin able to provide them by itself.

Guideline injection into prompts was broken. includes/Services/Guidelines.php read the wp_guideline post type and its _guideline_copy style post meta. Gutenberg replaced that model some time ago with wp_knowledge rows, 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:

  1. Guarded functions. Every function in knowledge-functions.php sits inside if ( ! function_exists( ... ) ). The names keep the wp_ prefix on purpose. They are a shared contract, not plugin-private code.
  2. Distinct class names. Gutenberg has Gutenberg_Knowledge_Post_Type. This plugin has the namespaced WordPress\AI\Experiments\Knowledge\Knowledge_Post_Type. There is never a redeclare error, so the decision happens at the behaviour level instead.
  3. A post type check. Knowledge_Post_Type::register() returns early when post_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 init priority 10. This experiment runs on init priority 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 stays options-general.php?page=guidelines-wp-admin no 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_scopes and wp_knowledge_types filters. Those work no matter who owns the base implementation, and the Settings page grows a section automatically. docs/experiments/knowledge.md covers this with an example.

New files

PHP in includes/Experiments/Knowledge/:

File Role
Knowledge.php The experiment. Loads the shared functions, registers the post type, records ownership.
knowledge-functions.php The nine shared wp_* functions, each behind a function_exists() guard.
Knowledge_Post_Type.php The wp_knowledge post type, the wp_knowledge_type taxonomy, and the dynamic capabilities.
Knowledge_REST_Controller.php Locks down /wp/v2/knowledge so reads need a login and new rows default to private.
Guideline_Scopes_REST_Controller.php Read-only registry at /wp/v2/knowledge/guideline-scopes.
Admin_Page.php The Settings → Guidelines submenu, the block library enqueue, and the REST preload.

UI in routes/guidelines/, a port of Gutenberg's route. "guidelines" is added to wpPlugin.pages, which generates ai_guidelines_wp_admin_render_page() and the matching page slug.

Notes on the port

Two things needed changing rather than copying:

  • isContentBlock is a private API. Gutenberg's data.ts unlocks it from @wordpress/blocks, which a plugin cannot do. The route reimplements it on public APIs in routes/guidelines/data.ts. Same logic: a block qualifies if it sets supports.contentRole or has an attribute with the content role.
  • This repo's TypeScript is stricter than Gutenberg's. exactOptionalPropertyTypes, noUncheckedIndexedAccess, and noPropertyAccessFromIndexSignature meant about 20 small adjustments. Worth knowing that noPropertyAccessFromIndexSignature and ESLint's dot-notation rule pull in opposite directions, so the affected spots were restructured to use typed shapes instead of satisfying one and breaking the other.

eslint.config.mjs gains a scoped block that turns off @wordpress/no-unsafe-wp-apis for routes/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/ui covers Stack but has no Heading, no muted Text variant, and its AlertDialog is trigger-based where ConfirmDialog is controlled. Happy to do the full swap instead if reviewers would rather not have the exception.

Guidelines service

includes/Services/Guidelines.php now reads published wp_knowledge rows by exact slug: guideline-{scope} for a scope, and guideline-block-{block_name} for a block. Details worth flagging for review:

  • The public API is unchanged, so the five abilities using it need no edits.
  • Scopes come from 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.
  • Only published rows are read. That matches what the Settings page treats as canonical, and it keeps other users' private rows out of prompts.
  • Block slugs encode the namespace separator as _, not -, so foo/bar-baz and foo-bar/baz cannot 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

  1. Enable the Knowledge and Guidelines experiment under Settings → AI.
  2. Go to Settings → Guidelines. Sections for Site, Copy, Images, Blocks, and Additional should be there.
  3. Save some text into Site and Copy, then reload. The text should still be there.
  4. Open the Blocks section, choose Add guidelines, pick a block, and save. It should appear in the list.
  5. Use Export to download the JSON, clear a guideline, then Import the file back.
  6. Confirm the guidelines reach a prompt. Enable Title Generation, generate a title, and check the request in AI Request Logs for a <guidelines> block.

With Gutenberg, gutenberg-guidelines on

  1. Turn on the Gutenberg experiment and reload wp-admin. There should be exactly one Settings → Guidelines item, served by Gutenberg, at the same URL as before.
  2. Confirm the AI plugin still reads those guidelines into prompts, as in step 6.

With Gutenberg, gutenberg-guidelines off

  1. Turn the Gutenberg experiment off. The AI plugin should take over the page again, with the data intact.

Automated

npm run build
npm run typecheck
npm run lint:js
npm run lint:php
npm run lint:php:stan
npm run test:php

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

Before After

Changelog Entry

Added - New Experiment: Knowledge and Guidelines; stores site guidelines in the shared wp_knowledge post type and adds a Settings → Guidelines page. It stands down when another plugin, such as Gutenberg, already provides the same feature.
Changed - The Guidelines service now reads guidelines from the wp_knowledge post type instead of the removed wp_guideline post type and its post meta, matching current Gutenberg.


Dependency note: package.json gains @wordpress/blob, which the export flow needs. It was already in the tree as a transitive dependency at the same version, so package-lock.json changes by a single line.

🤖 Generated with Claude Code

Open WordPress Playground Preview

gziolo added 2 commits August 18, 2026 12:49
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.
@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 Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.42017% with 117 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.86%. Comparing base (e05758f) to head (62f4fc1).

Files with missing lines Patch % Lines
includes/Experiments/Knowledge/Admin_Page.php 0.00% 45 Missing ⚠️
...udes/Experiments/Knowledge/knowledge-functions.php 74.35% 40 Missing ⚠️
...xperiments/Knowledge/Knowledge_REST_Controller.php 3.33% 29 Missing ⚠️
includes/Services/Guidelines.php 96.49% 2 Missing ⚠️
includes/Experiments/Knowledge/Knowledge.php 95.23% 1 Missing ⚠️
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     
Flag Coverage Δ
unit 73.86% <75.42%> (-0.01%) ⬇️

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.

@gziolo gziolo self-assigned this Aug 18, 2026
@gziolo gziolo added the [Type] Enhancement New feature or request label Aug 18, 2026
@jeffpaul jeffpaul added this to the Future Release milestone Aug 18, 2026
@jeffpaul jeffpaul moved this from Triage to In progress in WordPress AI Roadmap Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Enhancement New feature or request

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

2 participants