Skip to content

Knowledge: Introduce the wp_knowledge custom post type#12201

Open
gziolo wants to merge 1 commit into
WordPress:trunkfrom
gziolo:feature/knowledge-cpt
Open

Knowledge: Introduce the wp_knowledge custom post type#12201
gziolo wants to merge 1 commit into
WordPress:trunkfrom
gziolo:feature/knowledge-cpt

Conversation

@gziolo

@gziolo gziolo commented Jun 17, 2026

Copy link
Copy Markdown
Member

Trac ticket: https://core.trac.wordpress.org/ticket/65476

Summary

Introduces wp_knowledge, a private-by-default custom post type that acts as a storage primitive for structured site knowledge (guidelines, memories, notes), along with its wp_knowledge_type taxonomy and a dedicated REST controller.

This is the WordPress core counterpart to the Knowledge storage primitive being explored in Gutenberg (WordPress/gutenberg#79149). It deliberately lands the storage primitive only — the Guidelines admin UI and the content-guidelines singleton remain consumer-side concerns and are out of scope here.

Details

  • Post type & taxonomy: wp_knowledge and wp_knowledge_type are registered as built-ins in create_initial_post_types() / create_initial_taxonomies(). Both are non-public and headless (show_ui => false); rows are managed via the REST API rather than a wp-admin screen.
  • REST API: WP_REST_Knowledge_Controller serves /wp/v2/knowledge. Reads require an authenticated user with the read capability, collection queries are scoped to rows the current user can read (so totals/pagination respect per-user visibility), callers without the publish capability are limited to the private status, and new rows default to private. Revision history uses the default revisions controller; autosave endpoints are disabled (following the wp_global_styles precedent), since knowledge has no editor session.
  • Capabilities: a graded model is synthesized through the user_has_cap filter (wp_maybe_grant_knowledge_caps()), matching the existing wp_maybe_grant_* pattern. Administrators manage all knowledge; contributors and above may create and fully manage their own private rows. Subscribers and anonymous users are blocked at the post-type door.
  • Type registry: wp_knowledge_types() exposes a filterable set of types (guideline, memory, note); rows saved without a type fall back to the note term.

Testing

New unit tests cover registration, the capability matrix across all roles, the REST controller (CRUD, permission gating, private-by-default), and the type registry. The REST route snapshot in tests/phpunit/tests/rest-api/rest-schema-setup.php is updated accordingly.

npm run test:php -- --group knowledge

Note

The large tests/qunit/fixtures/wp-api-generated.js diff is the auto-generated REST API client fixture, regenerated to add the new /wp/v2/knowledge routes and the wp_knowledge / wp_knowledge_type entries.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown

Hi there! 👋

Thank you for your contribution to WordPress! 💖

It looks like this is your first pull request to wordpress-develop. Here are a few things to be aware of that may help you out!

No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description.

Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making.

More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook.

Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook.

If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook.

The Developer Hub also documents the various coding standards that are followed:

Thank you,
The WordPress Project

@github-actions

github-actions Bot commented Jun 17, 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.

Core Committers: Use this line as a base for the props when committing in SVN:

Props gziolo, jorgefilipecosta.

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

@gziolo gziolo self-assigned this Jun 17, 2026
@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@gziolo gziolo force-pushed the feature/knowledge-cpt branch from 5530424 to 41b499b Compare June 17, 2026 10:24
@gziolo gziolo changed the title Knowledge: Introduce the wp_knowledge custom post type Knowledge: Introduce the wp_knowledge custom post type Jun 17, 2026
@gziolo gziolo force-pushed the feature/knowledge-cpt branch 3 times, most recently from 2f83ce3 to 69b1806 Compare June 17, 2026 13:51
Register wp_knowledge, a private-by-default storage primitive for structured
site knowledge, together with the wp_knowledge_type taxonomy. Both are built-in
and headless (no admin UI); rows are managed through the REST API.

Add WP_REST_Knowledge_Controller at /wp/v2/knowledge: reads require an
authenticated user, collections are scoped to rows the current user can read,
callers without the publish capability are limited to the private status, and
new rows default to private. Revisions use the default controller; autosave
endpoints are disabled.

Synthesize a graded capability set through the user_has_cap filter:
administrators manage all knowledge, while contributors and above create and
manage their own private rows. Knowledge types are filterable via
wp_knowledge_types(), with note as the save-time fallback.

Trac ticket: https://core.trac.wordpress.org/ticket/65476

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gziolo gziolo requested a review from jorgefilipecosta June 17, 2026 13:56
@gziolo gziolo force-pushed the feature/knowledge-cpt branch from 69b1806 to d008aaa Compare June 17, 2026 13:56
gziolo added a commit to WordPress/gutenberg that referenced this pull request Jun 17, 2026
Aligns the experimental Knowledge implementation with the refined version
ported to WordPress core in WordPress/wordpress-develop#12201:

- Built-in type `instruction` renamed to `guideline`; the core type
  registry consolidated on `guideline`/`memory`/`note`. The migration
  target slug, the TERM_GUIDELINE constant, and the content-guidelines
  singleton follow.
- Type titles now use the `_x( ..., 'knowledge type' )` context.
- Capability filter `_wp_knowledge_synthesize_caps` becomes the public
  `wp_maybe_grant_knowledge_caps`, hooked at priority 1 to match the
  `wp_maybe_grant_*` family.
- Disable autosave support for `wp_knowledge` (headless storage with no
  editor session); revision history is retained.
- Register the `wp_knowledge_type` taxonomy as headless (`show_ui => false`).
- Align the private-status REST error message with core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gziolo added a commit to WordPress/gutenberg that referenced this pull request Jun 17, 2026
Aligns the experimental Knowledge implementation with the refined version
ported to WordPress core in WordPress/wordpress-develop#12201:

- Built-in type `instruction` renamed to `guideline`; the core type
  registry consolidated on `guideline`/`memory`/`note`. The migration
  target slug, the TERM_GUIDELINE constant, and the content-guidelines
  singleton follow.
- Type titles now use the `_x( ..., 'knowledge type' )` context.
- Capability filter `_wp_knowledge_synthesize_caps` becomes the public
  `wp_maybe_grant_knowledge_caps`, hooked at priority 1 to match the
  `wp_maybe_grant_*` family.
- Disable autosave support for `wp_knowledge` (headless storage with no
  editor session); revision history is retained.
- Register the `wp_knowledge_type` taxonomy as headless (`show_ui => false`).
- Align the private-status REST error message with core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* @param WP_User $user The user object.
* @return bool[] Filtered array of the user's capabilities.
*/
function wp_maybe_grant_knowledge_caps( array $allcaps, array $caps, array $args, WP_User $user ): array {

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.

Core has historically avoided scalar/array hints on filter callbacks for graceful coercion see wp_maybe_grant_site_health_caps above. But it is new function although not following the previous patterns maybe we should embrace it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, that might make sense.

Aside, the function might need to be marked as private and prefixed with _.

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.

A coding standards change was made some time ago to avoid the _ prefix for private functions so it can just be annotated as such without the underscore.

Comment thread src/wp-includes/post.php
@jorgefilipecosta

Copy link
Copy Markdown
Member

Knowledge, is something done mostly though for LLMS, similar to abilities, I wonder if for knowledge we avoid a specific REST endpoint and we just use abilities as the interface to manage the knowledge everywhere? Instead of calling REST we would always call the abilities.

! $post instanceof WP_Post ||
'wp_knowledge' !== $post->post_type ||
(int) $post->post_author !== (int) $user->ID ||
'private' !== $post->post_status

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.

I think contributor-level users can likely trash their own private knowledge, then lose the ability to permanently delete it. Because grant only applies when post_status === 'private', so once the item is in trash, delete_knowledge is no longer granted.

),
'capabilities' => array(
'manage_terms' => 'manage_options',
'edit_terms' => 'edit_knowledge',

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.

I think we may have an issue here, it seems contributors will be able to create new knowledge_types e.g: over REST, because edit_terms maps to edit_knowledge and contributors will get edit_knowledge.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, this is intentional because we lazily insert new taxonomy terms due to performance issues we had with inserting an upfront predefined list of terms. Therefore, when adding a post, the user needs have a way to create a term, too.

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.

naming things for caps for plural/singular: knowledge_types, knowledge_type or similar.

$request = new WP_REST_Request( 'GET', '/wp/v2/knowledge/' . self::$admin_private );
$response = rest_get_server()->dispatch( $request );

$this->assertContains( $response->get_status(), array( 401, 403 ) );

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.

Nit: here we testing something not authorized I think testing 403 should be enough.

'_builtin' => true,
'show_in_nav_menus' => false,
'show_in_rest' => true,
'show_admin_column' => true,

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.

Given that show_ui for the post type and taxonomy are both false, show_admin_column true does nothing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Here, we should decide whether we want to allow the UI for managing taxonomy terms for wp_knowledge_type. I think it's something that is currently enabled in trunk on Gutenberg.

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.

I think that UI appears as a submenu of the post type menu item, as the post type does not have a menu item, the flag does nothing on this case.

Comment thread src/wp-includes/post.php
);

register_post_type(
'wp_knowledge',

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.

We are not setting delete_with_user, so we use the default and knowledge created by an user is deleted when that user is deleted. I guess that behaviour is correct if the knowledge is private, but it may have unintended consequences for knowledge memory shared between users.

$post_type = get_post_type_object( $this->post_type );
if ( ! current_user_can( $post_type->cap->read ) ) {
return new WP_Error(
'rest_forbidden',

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.

src/wp-includes/rest-api/endpoints/class-wp-rest-font-families-controller.php for a similar check uses error rest_cannot_read. I think both are ok, but maybe we should align?

* @group knowledge
* @group post
*/
class Tests_Knowledge_PostType extends WP_UnitTestCase {

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.

Maybe we should add a test here where Contributor edit/delete their own knowlodge via REST (200) and and then see it 403 on others' knowlodge.

@jorgefilipecosta

Copy link
Copy Markdown
Member

Great work here, things look, just left some comments for considering but it seems to be on a good shape.

@peterwilsoncc peterwilsoncc 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.

I've added a few first pass notes inline...

*
* @param int $post_id Saved post ID.
*/
function _wp_knowledge_ensure_default_type_term( int $post_id ): void {

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.

Per earlier comment, requires changes elsewhere.

Suggested change
function _wp_knowledge_ensure_default_type_term( int $post_id ): void {
function wp_knowledge_ensure_default_type_term( int $post_id ): void {

* @phpstan-param array<non-empty-string, mixed> $data
* @phpstan-return array<non-empty-string, mixed>
*/
function _wp_knowledge_maybe_map_term_label( array $data, string $taxonomy ): array {

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.

Per earlier comment, requires changes elsewhere.

Suggested change
function _wp_knowledge_maybe_map_term_label( array $data, string $taxonomy ): array {
function wp_knowledge_maybe_map_term_label( array $data, string $taxonomy ): array {

Comment thread src/wp-includes/post.php
Comment on lines +677 to +685
/*
* "Knowledge" is a mass noun, so the singular and plural capability
* bases must differ: with both set to `knowledge`, the generated
* per-post meta caps (`edit_knowledge_item`) would collide with the
* primitive caps (`edit_knowledge`). The `*_knowledge_item` forms are
* never granted directly; `map_meta_cap()` resolves them onto the
* primitives, which `wp_maybe_grant_knowledge_caps()` synthesizes.
*/
'capability_type' => array( 'knowledge_item', 'knowledge' ),

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.

Suggested change
/*
* "Knowledge" is a mass noun, so the singular and plural capability
* bases must differ: with both set to `knowledge`, the generated
* per-post meta caps (`edit_knowledge_item`) would collide with the
* primitive caps (`edit_knowledge`). The `*_knowledge_item` forms are
* never granted directly; `map_meta_cap()` resolves them onto the
* primitives, which `wp_maybe_grant_knowledge_caps()` synthesizes.
*/
'capability_type' => array( 'knowledge_item', 'knowledge' ),
'capability_type' => array( 'knowledge_items', 'knowledge_item' ),

...or similar: as a general rule primitives are plural and meta are the singular form of the same. Eg edit_posts allows editing of posts, whereas edit_post allows the editing of a single post.

Requires changes elsewhere.

),
'capabilities' => array(
'manage_terms' => 'manage_options',
'edit_terms' => 'edit_knowledge',

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.

naming things for caps for plural/singular: knowledge_types, knowledge_type or similar.

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.

3 participants