Knowledge: Dissolve the Guidelines singleton into per-scope rows#79263
Draft
aagam-shah wants to merge 8 commits into
Draft
Conversation
Renames the storage-primitive identifiers per the consolidation proposal in WordPress#77230: the CPT slug (wp_guideline -> wp_knowledge), the type taxonomy (wp_guideline_type -> wp_knowledge_type), the types registry and filter (wp_guideline_types -> wp_knowledge_types), the capability namespace (*_guidelines -> *_knowledge), and the generic REST route (/wp/v2/guidelines -> /wp/v2/knowledge). The capability registration uses capability_type => array( 'knowledge_item', 'knowledge' ) because "knowledge" is a mass noun: with identical singular/plural bases the generated per-post meta caps would collide with the primitives. The *_knowledge_item forms are never granted; map_meta_cap() resolves them. User-facing surfaces are intentionally unchanged: the Settings > Guidelines page, the /wp/v2/content-guidelines singleton route, all Guidelines labels, the gutenberg-guidelines experiment id, and the _guideline_* meta keys. The built-in type slugs (content, artifact, memory) are renamed separately. Ships a one-time data migration (lib/upgrade.php) that moves existing wp_guideline rows and wp_guideline_type terms to the new identifiers so sites already building on the experiment are not orphaned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Renames two of the three built-in knowledge types per the WordPress#77230 proposal so every type is defined by behavior rather than by relation: - `content` -> `instruction`: loaded by default when applicable. The site-wide guidelines singleton managed by Settings > Guidelines now carries the `instruction` term; the /wp/v2/content-guidelines route is otherwise unchanged. - `artifact` -> `note`: private freeform working text, and the fallback term assigned on save when no type is given. - `memory` stays as is. The one-time migration in lib/upgrade.php now also re-slugs existing terms (content -> instruction, artifact -> note), replacing term names only when they still match the previous default labels so customized labels survive. The re-slug runs independently of the legacy taxonomy flip so partially migrated rows are covered too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gutenberg-guidelines flag gates more than the Settings page since the storage primitive rename: it also enables the wp_knowledge CPT, the /wp/v2/knowledge API, and the capability namespace integrators build on. The experiment id and label stay unchanged — the id is persisted in the gutenberg-experiments option on sites that enabled it, and the label follows the proposal's rule that the user-facing name stays Guidelines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every other test file in the directory carries a group annotation; this one was missed when it was introduced, so group-filtered runs silently skipped it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the content-guidelines singleton + meta model with one `guideline`-typed `wp_knowledge` row per scope (content in post_content), addressed by a `guideline-` slug prefix. - Rename the `wp_knowledge_type` term `instruction` -> `guideline` (wp_knowledge_types(), TERM_GUIDELINE, and the upgrade migration, which now converges both `content` and the interim `instruction` onto `guideline`). - Add the `wp_guideline_scopes` registry filter and a read-only `/wp/v2/knowledge/guideline-scopes` controller; preload it on the Settings page. - Enforce identity with a reservation guard (force the guideline term, keep slugs unique with no suffix, reject duplicate creates) and a save filter (sanitize content + cap length, re-stamp scope titles in the site locale). - Delete both specialized controllers and the meta machinery; data flows through the standard /wp/v2/knowledge collection. - Drive the Settings UI through @wordpress/core-data (useEntityRecords + a runtime guidelineScope entity); delete the hand-rolled store/api. - Render sections from the registry, hide revision history, and keep the import/export JSON shape unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Importing onto (or editing) a guideline row that was loaded via the collection request after a reload threw "Cannot read properties of undefined (reading 'content')". The `wp_knowledge` (postType) entity already fetches with `context: 'edit'` via its `baseURLParams`, so the collection response includes raw field values regardless of the query. Passing `context: 'edit'` in the query as well only changed the *storage bucket* to `edit`, where `editEntityRecord`/`getRawEntityRecord` (which read the `default` bucket) could not find the row — so `editEntityRecord` dereferenced `undefined`. - Drop `context: 'edit'` from the collection query; raw content still arrives via the entity baseURLParams, and rows now land in the `default` bucket where edits resolve. - Pass options to `saveEditedEntityRecord` in the correct argument position so `throwOnError` is honored on updates. - Gate the loading spinner on `hasResolved` rather than `isResolving` so the form doesn't mount with empty content and clobber freshly-typed text when the rows arrive. - Add an e2e regression test that edits a scope guideline after a reload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…update Two correctness fixes for the guideline-row identity model. - Block slugs were lossy: deriving `guideline-block-<ns>-<name>` by replacing `/` with `-` collapsed distinct block names such as `foo/bar-baz` and `foo-bar/baz` onto the same slug, so one block's guideline could overwrite another's. Encode the namespace separator as `_` instead — block names match `[a-z0-9-]+/[a-z0-9-]+` and never contain `_`, so the mapping is injective. The canonical block name still lives in the row title. - The reservation guard only checked slug uniqueness on create, so a REST update could repoint an existing row's slug onto an already-used `guideline-` slug, producing duplicate-identity rows. Run the check on update too, excluding the row itself so content-only saves still pass. Adds reservation tests for the update-collision and content-only-update cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The guidelines route's package.json dropped @wordpress/api-fetch and @wordpress/date and added @wordpress/core-data; regenerate the lockfile so the check-local-changes CI gate passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4e1214d to
44f408c
Compare
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?
Follow up to #79149. Part of the Guidelines/Knowledge effort (#77230).
Dissolves the Settings → Guidelines singleton-plus-meta model into one
guideline-typedwp_knowledgerow per scope (content stored inpost_content), driven entirely through the standard/wp/v2/knowledgeentity. Both specialized REST controllers (~1,150 lines) and the hand-rolled client store/api are removed. The visible Settings → Guidelines UI is unchanged.Why?
The old screen rested on special-purpose machinery: a single
wp_knowledgepost holding every category as_guideline_*meta, a dedicated/wp/v2/content-guidelinescontroller, a custom revisions controller, and a bespoke client API/store, with the categories hardcoded in the UI. Re-using thewp_knowledgeprimitive's own building blocks removes that machinery, gives each scope a real row (so per-row revision history later comes free from the default endpoint), and makes the data model addressable and extensible via a filter instead of hardcoded lists.How?
guideline-typed row addressed by aguideline-slug prefix (guideline-copy,guideline-block-core_paragraph). The canonical block name is stored in the row title; the namespace separator is encoded as_so distinct block names can't collide on one slug.wp_guideline_scopesfilter is the source of truth for the Settings sections (plugins can add sections); a small read-only/wp/v2/knowledge/guideline-scopescontroller exposes it (gated onread_knowledge, the/wp/v2/statusespattern), preloaded on the page.guidelineterm onto prefixed slugs, keeps them unique (409 on a duplicate, on create and update) and verbatim (no-2suffix), sanitizespost_contentto plain text capped at 5000 chars, and re-stamps registry scope titles in the site locale.wp_knowledge_typebuilt-in terminstructionbecomesguideline; the upgrade migration convergescontentand the interiminstructionontoguideline.routes/guidelinesnow reads/writes through@wordpress/core-data(useEntityRecords+ a runtimeguidelineScopeentity); sections render from the registry; revision history is hidden; import/export keeps the same JSON shape so existing files round-trip.class-gutenberg-content-guidelines-rest-controller.php,class-gutenberg-content-guidelines-revisions-controller.php, the guideline meta registration/helpers, and the clientstore.ts/api.ts/revision-history.tsx.Note: existing singleton-meta data is not migrated (the feature is experimental and flag-gated); see discussion for whether a one-time migration is warranted.
Testing Instructions
gutenberg-experimentsoption to{"gutenberg-guidelines":"1"})./wp/v2/knowledgeand/wp/v2/knowledge/guideline-scopes(and never/wp/v2/content-guidelines).Automated:
npm run test:unit:php -- --group knowledge(PHPUnit, incl. the scopes controller and reservation-guard tests).npm run test:e2e -- test/e2e/specs/admin/guidelines.spec.js.Testing Instructions for Keyboard
Tab to Settings → Guidelines, expand a section with Enter/Space, Tab into its textarea, type, and Tab to Save guidelines / Clear guidelines. For Blocks, Tab to Add guidelines, operate the block combobox and guideline textarea, and reach the modal's Save/Remove with Tab; confirm focus returns to the page on close.
Screenshots or screencast
No visual change — the Settings → Guidelines UI is intentionally identical; only the backend storage and client data layer changed.
Use of AI Tools
This PR was authored with the assistance of Claude Code (Anthropic, Claude Opus 4.8). AI was used for the implementation, the PHPUnit/e2e tests, and this description; all changes were reviewed and verified by a human (PHPUnit
--group knowledge, the e2e spec, PHPCS, and a manual browser pass), and the author takes responsibility for the contents per the WordPress AI Guidelines.🤖 Generated with Claude Code