{"owner":"WordPress","repo":"gutenberg","hasSkills":true,"totalSkillsCount":6,"totalTokensCount":4282,"categories":["root-instruction","copilot-instructions","mcp-config","marketplace","plugin-manifest","cursor-rule"],"hasMcp":true,"mcpConfig":{"mcpServers":{"gutenberg":{"command":"npx","args":["-y","@modelcontextprotocol/server-gutenberg"]}}},"found":["AGENTS.md",".github/copilot-instructions.md",".cursor/mcp.json",".claude-plugin/marketplace.json",".claude-plugin/plugin.json",".cursorrules (wordpress-php-guzzle-gutenberg-cursorrules-prompt-.mdc)"],"skills":{"AGENTS.md":"# AGENTS.md\n\n## Dev environment tips\n\n```bash\n# Setup\nnvm use                    # Use the required node version\nnpm install && composer install\nnpm run wp-env-test status      # Always check status first.\nnpm run wp-env-test start       # Only start if not already running.\n\n# Development\nnpm start     # Development with watch\nnpm run build # Production build; emits types with --noCheck, does NOT type check\n```\n\nBefore verification or a commit in a fresh Git worktree, follow the [worktree setup instructions](docs/contributors/code/getting-started-with-code-contribution.md#set-up-each-worktree).\n\n`npm run build` never fails on type errors. After changing TypeScript or checked JS, run `npm run typecheck`.\n\n### Key Directories\n\n-   `/packages/` - JavaScript packages (each has README.md and CHANGELOG.md)\n-   `/routes/` - Route entry points for the admin single-page apps, one directory per route; each declares the page(s) it belongs to (the extensible site editor, the dashboard, the media editor, and others)\n-   `/lib/` - PHP code\n-   `/lib/compat/wordpress-X.Y/` - Version-specific features (new PHP features usually go here)\n-   `/phpunit/` - PHP tests\n-   `/schemas/json/` - JSON Schemas for `block.json`, `theme.json`, `font-collection.json`, and `wp-env.json` (published to `schemas.wp.org`; tests in `/test/integration/`)\n-   `/docs/` - Documentation\n    -   `/docs/contributors/` - Contributing guides\n    -   `/docs/explanations/architecture/` - System architecture docs\n    -   `/docs/how-to-guides/` - Implementation tutorials\n    -   `/docs/reference-guides/` - API documentation\n\n## Progressive discovery\n\nRead only what your task needs, when it needs it:\n\n-   **Contributor docs**: before starting a task, check `docs/contributors/code/` for the guide covering that kind of work (coding guidelines, backward compatibility, workspaces, releases) and read the relevant one.\n-   **User-facing copy**: before writing or changing a string a user reads, read `docs/contributors/documentation/copy-guide.md` — it covers terminology, capitalization, and how to word an error message.\n-   **Directory guides**: some directories carry their own `AGENTS.md` and `README.md` with rules for working there (e.g. `packages/components/AGENTS.md`) — read it before changing files in that directory.\n\n## Markdown files\n\nMarkdown in this repository is soft-wrapped: a paragraph is one long line, and the editor wraps it on screen. Do not hard-wrap it.\n\n-   Keep each paragraph, list item, and table row on a single line, however long it gets. There is no line length limit; `MD013` is disabled in `.markdownlint.json`.\n-   This covers every `.md` file (docs, `README.md`, `CHANGELOG.md`, templates) and the Markdown written into pull request descriptions, issues, and comments.\n-   This is an accessibility rule, not only a tidiness one: a screen reader reads a plain `.md` file line by line and a braille display shows one line at a time, so a hard-wrapped sentence arrives as fragments. See [Write accessible documentation](https://developers.google.com/style/accessibility).\n-   When editing an existing paragraph, rewrite the line in place. Never re-flow or re-wrap lines the change does not otherwise touch: it turns a one-word edit into a diff over the whole paragraph and buries the real change.\n-   Line breaks still belong where Markdown needs them: between list items, between table rows, and inside fenced code blocks, which are never re-wrapped either.\n-   A few older files are still hard-wrapped. Leave them as they are unless the task is to reflow them; do not match their style in new prose.\n\nSee [Documentation Contributions](docs/contributors/documentation/README.md) for the rest of the documentation conventions.\n\n## Code quality\n\nFix ESLint and Stylelint violations in the code whenever possible. Add entries or increase counts in `tools/eslint/suppressions.json` or `tools/stylelint/stylelint-suppressions.json` only as a last resort, and explain in the PR why a code fix is not practical. Keep suppressions limited to the specific violations that need them. After fixing suppressed violations, run `npm run lint:js:prune-suppressions` for ESLint. For Stylelint, first ensure `npm run lint:css` passes, then run `npm run lint:css:update-suppressions`. Review and commit the reductions.\n\n```bash\nnpm run format            # Fix JS formatting\nnpm run lint:js          # Check JS linting\nnpm run typecheck        # Type check sources, plus TypeScript tests and stories\nvendor/bin/phpcbf        # Fix PHP standards\nvendor/bin/phpcs         # Check PHP standards\n\n# Specific files\nvendor/bin/phpcbf <path_to_php_file.php>\n```\n\n## Architectural decisions\n\n-   **Package layering**: Three editor layers — `block-editor` (generic, WP-agnostic) → `editor` (WordPress post-type-aware) → `edit-post`/`edit-site` (full screens). Lower layers MUST NOT depend on higher ones.\n-   **Site editor parity**: the site editor exists twice — `packages/edit-site` (v1) and the extensible site editor (v2: the `routes/*` entry points declaring the `site-editor-v2` page, booted by `packages/boot`, behind the `gutenberg-extensible-site-editor` experiment). Any feature or enhancement added to the site editor MUST be added to the extensible site editor in the same change, so the two do not drift. If a feature deliberately does not belong in v2, say so explicitly instead of silently skipping it.\n-   **Block data model**: Blocks are in-memory tree structures during editing, serialized as HTML with comment delimiters (`<!-- wp:name -->`). Work with the block tree via APIs, not the serialized HTML.\n-   **Data layer**: Uses `@wordpress/data` (Redux-like stores). Edit entities through `core-data` actions (`editEntityRecord` / `saveEditedEntityRecord`), not direct state manipulation.\n-   **Styles system**: Three-layer merge — WordPress defaults < `theme.json` < user preferences. Use Block Supports API and CSS custom properties (`--wp--preset--*`), not hardcoded values.\n-   **Modularity**: Packages are available both as npm packages and WordPress scripts (`wp-*` handles). Production packages must work in both contexts.\n\nFor full architecture details, see `docs/explanations/architecture/`.\n\n## Common pitfalls\n\n-   Dependencies are isolated, not hoisted: `.npmrc` sets `install-strategy = \"linked\"`. Packages live in `node_modules/.store/` and are symlinked only into the `node_modules` of the workspaces that declare them, so a near-empty root `node_modules` (or a missing `node_modules/typescript`) is expected and does not mean the install failed. To check an install, look for `node_modules/.store/` (`ls -a`, since it is a dotfile) or just run the npm script you need.\n-   Do not add dependencies to the root `package.json`. Add them to the workspace that uses them, or create a new workspace under `tools/` (or `test/` for test infrastructure). See [Workspace Development](docs/contributors/code/workspace-development.md).\n-   Published package runtime files and emitted type declarations must resolve through the package's declared dependency surface; never rely on root hoisting or workspace links. Prefer fixing accidentally leaked public types over adding an unrelated heavy dependency, and run `npm run lint:published-deps` when changing package dependencies or exported types.\n-   PHP features in `lib/compat/` MUST go in the `wordpress-X.Y/` directory for their intended WordPress release. Inspect the available compatibility directories first; do not assume the newest one is right.\n-   Avoid using private APIs in bundled packages (packages without `wpScript` or `wpModuleExports`). Private APIs are intended for Core usage; bundled packages may also be imported via npm into plugin scripts, causing incompatibilities.\n-   Avoid adding new APIs prefixed with `__experimental` or `__unstable`. This pattern is now not used. Instead use private APIs or in bundled packages regular exports.\n-   `block-editor` is a WordPress-agnostic package. NEVER add `core-data` dependencies or direct REST API calls to it.\n-   `@wordpress/build` (`packages/wp-build`) is a generic build tool used both in Gutenberg and by plugins targeting WordPress Core directly. Avoid Gutenberg-specific changes in it.\n-   Pages and routes are stable by default. Mark experimental pages with `\"experimental\": true` in `wpPlugin.pages`; WordPress Core builds exclude those pages and routes used only by experimental pages. Do not introduce separate `stable` or `core` flags for this distinction.\n-   Prefer the `package.json` scripts, which accept file paths, such as `npm run test:unit -- <file>`, `npm run lint:js <file>` (which also checks Prettier formatting), and `npm run typecheck -- <path/to/tsconfig.json>`; see the [Testing Overview](docs/contributors/code/testing-overview.md) for more test commands. If no script fits, ask before running a binary directly, since a missing script can be a gap to fix and not to work around.\n-   Run a binary from an installed npm package with `npm exec --no -- <binary>`, which fails if it is not installed locally. Since `npx` and a bare `npm exec` download whatever the npm registry serves as a package under that name, use them only with a full package name, such as `npx @wordpress/create-block@latest`.\n-   PHP function and class names are renamed at build time (`gutenberg_*` prefix, `*_Gutenberg` suffix) to avoid conflicts with WordPress Core — the built names, not the source names, are what runs (and what tests must call). See `docs/contributors/code/build-system-function-prefixing.md`.\n-   For changes that affect package consumers, add a short entry to the package's `CHANGELOG.md`. Omit preserved behavior and internal migration steps. Follow [Maintaining Changelogs](packages/README.md#maintaining-changelogs).\n-   Packages with TypeScript dev files split their configs: `tsconfig.build.json` (src only, emits `build-types`) and the default `tsconfig.json` (dev project: tests and stories, `noEmit`, jest types). Packages without dev files keep a single `tsconfig.json` build project, and a few packages deviate (a dev-only project with handwritten declarations, or specialized build projects); see the TypeScript section in `packages/README.md`. Reference a split package by `../<pkg>/tsconfig.build.json` and an unsplit one by `../<pkg>`. `npm run build` never type checks (it emits declarations with `--noCheck`); use `npm run typecheck`, and never add jest types to a build project.\n-   A rejected `apiFetch` is not always an `Error`: a REST error arrives as a plain object (`{ code, message, data }`), `parse: false` rejects with the `Response` (which carries `status`, not `message`), an aborted request rethrows an `AbortError`, and a handler set via `setFetchHandler` can reject anything. Do not interpolate the rejection into a string (`` `${ error }` `` gives `[object Object]`) or branch on `instanceof Error`. Normalise it to a message before showing the user anything, and supply your own copy when there is none — `ensureError` in `packages/core-data/src/private-actions.js` is the reference implementation, though it is local to that file rather than exported.\n-   Treat block attributes and `theme.json` values as untrusted when their types haven't been validated. Raw values include `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Guard them before strict string operations. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated.\n\n## PR instructions\n\n-   Ensure build passes.\n-   Fix all formatting/linting issues; these are enforced through CI in PRs.\n",".github/copilot-instructions.md":"## PR review guidelines\n\nWhen reviewing pull requests:\n\n- Only comment on semantically meaningful issues: bugs, incorrect logic, security problems, accessibility regressions, or API contract violations.\n- Skip style, formatting, naming, and whitespace observations — these are enforced by lint and PHPCS.\n- Keep each comment short — one or two sentences maximum.\n- Do not write long descriptions or summaries of what the code does.\n- Do not suggest refactors or improvements unrelated to the PR's stated goal.\n- Keep the top-level review body empty unless a finding genuinely spans multiple files and can't be attached to a specific line. Put findings in inline comments wherever possible, and never use the review body to restate what the PR is doing.\n\n## What not to flag\n\n- **Do not speculate about external code.** Verify WordPress, PHP, and Node APIs, function signatures, and version constraints against the diff or repo files (`composer.json`, `package.json`, etc.) before flagging compatibility or contract issues. Do not invent strings, error messages, or behaviour in linked codebases such as WordPress core. If a claim cannot be verified from the diff or repository, do not include it.\n- **Calibrate edge-case warnings.** Do not flag theoretical edge cases (`Number.MAX_VALUE`, subnormals, inputs that cannot occur given current callers). Flag edge cases only when they correspond to inputs that can plausibly reach the code.\n\n## Gutenberg-specific context\n\n- Do not suggest replacing `@wordpress/data` selectors / actions with local React state — this is the project's intentional state pattern.\n- Do not suggest replacing `__()` / `_x()` / `_n()` calls with template literals — these are WordPress i18n functions.\n- Do not suggest moving code between `block-editor`, `editor`, and `edit-post` packages without considering the layering rule (`block-editor` is WordPress-agnostic; lower layers must not depend on higher ones).\n- Flag unguarded block attribute or `theme.json` values reaching a strict string operation such as `preg_match()`, `explode()`, `trim()` or `esc_url()`, but only where the value is raw: `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated — do not flag those. `sprintf( '%s' )`, `str_replace()`, `preg_replace()` and interpolation only warn, so they are not findings.\n\n## Writing Markdown\n\n- Do not hard-wrap Markdown. Keep each paragraph, list item, and table row on a single line; there is no line length limit (`MD013` is disabled).\n- Hard wrapping is an accessibility problem: screen readers and braille displays read the source file line by line, so a wrapped sentence arrives as fragments.\n- Do not reflow lines a change does not otherwise touch, in either a suggestion or a commit.\n- This applies to `.md` files and to the Markdown in pull request descriptions and comments.\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/plugin.json)",".cursorrules (wordpress-php-guzzle-gutenberg-cursorrules-prompt-.mdc)":"---\ndescription: \"Cursor rules for WordPress development with PHP, Guzzle, and Gutenberg integration.\"\nglobs: **/*\nalwaysApply: false\n---\n- You are operating in a WordPress plugin context, that has a Guzzle-based HTTP client, WP REST endpoint addition(s), and new Gutenberg editor blocks.\n\n- Always use WordPress coding standards when writing PHP, JavaScript, and TypeScript.\n\n- Always type hint PHP code.\n\n- Prefer writing TypeScript over JavaScript.\n\n- Favor functional paradigms over object-oriented ones, favor composition over inheritance, but be consistent with WordPress ecosystem best practices.\n\n- Optimize for readability.\n"},"files":{"AGENTS.md":"# AGENTS.md\n\n## Dev environment tips\n\n```bash\n# Setup\nnvm use                    # Use the required node version\nnpm install && composer install\nnpm run wp-env-test status      # Always check status first.\nnpm run wp-env-test start       # Only start if not already running.\n\n# Development\nnpm start     # Development with watch\nnpm run build # Production build; emits types with --noCheck, does NOT type check\n```\n\nBefore verification or a commit in a fresh Git worktree, follow the [worktree setup instructions](docs/contributors/code/getting-started-with-code-contribution.md#set-up-each-worktree).\n\n`npm run build` never fails on type errors. After changing TypeScript or checked JS, run `npm run typecheck`.\n\n### Key Directories\n\n-   `/packages/` - JavaScript packages (each has README.md and CHANGELOG.md)\n-   `/routes/` - Route entry points for the admin single-page apps, one directory per route; each declares the page(s) it belongs to (the extensible site editor, the dashboard, the media editor, and others)\n-   `/lib/` - PHP code\n-   `/lib/compat/wordpress-X.Y/` - Version-specific features (new PHP features usually go here)\n-   `/phpunit/` - PHP tests\n-   `/schemas/json/` - JSON Schemas for `block.json`, `theme.json`, `font-collection.json`, and `wp-env.json` (published to `schemas.wp.org`; tests in `/test/integration/`)\n-   `/docs/` - Documentation\n    -   `/docs/contributors/` - Contributing guides\n    -   `/docs/explanations/architecture/` - System architecture docs\n    -   `/docs/how-to-guides/` - Implementation tutorials\n    -   `/docs/reference-guides/` - API documentation\n\n## Progressive discovery\n\nRead only what your task needs, when it needs it:\n\n-   **Contributor docs**: before starting a task, check `docs/contributors/code/` for the guide covering that kind of work (coding guidelines, backward compatibility, workspaces, releases) and read the relevant one.\n-   **User-facing copy**: before writing or changing a string a user reads, read `docs/contributors/documentation/copy-guide.md` — it covers terminology, capitalization, and how to word an error message.\n-   **Directory guides**: some directories carry their own `AGENTS.md` and `README.md` with rules for working there (e.g. `packages/components/AGENTS.md`) — read it before changing files in that directory.\n\n## Markdown files\n\nMarkdown in this repository is soft-wrapped: a paragraph is one long line, and the editor wraps it on screen. Do not hard-wrap it.\n\n-   Keep each paragraph, list item, and table row on a single line, however long it gets. There is no line length limit; `MD013` is disabled in `.markdownlint.json`.\n-   This covers every `.md` file (docs, `README.md`, `CHANGELOG.md`, templates) and the Markdown written into pull request descriptions, issues, and comments.\n-   This is an accessibility rule, not only a tidiness one: a screen reader reads a plain `.md` file line by line and a braille display shows one line at a time, so a hard-wrapped sentence arrives as fragments. See [Write accessible documentation](https://developers.google.com/style/accessibility).\n-   When editing an existing paragraph, rewrite the line in place. Never re-flow or re-wrap lines the change does not otherwise touch: it turns a one-word edit into a diff over the whole paragraph and buries the real change.\n-   Line breaks still belong where Markdown needs them: between list items, between table rows, and inside fenced code blocks, which are never re-wrapped either.\n-   A few older files are still hard-wrapped. Leave them as they are unless the task is to reflow them; do not match their style in new prose.\n\nSee [Documentation Contributions](docs/contributors/documentation/README.md) for the rest of the documentation conventions.\n\n## Code quality\n\nFix ESLint and Stylelint violations in the code whenever possible. Add entries or increase counts in `tools/eslint/suppressions.json` or `tools/stylelint/stylelint-suppressions.json` only as a last resort, and explain in the PR why a code fix is not practical. Keep suppressions limited to the specific violations that need them. After fixing suppressed violations, run `npm run lint:js:prune-suppressions` for ESLint. For Stylelint, first ensure `npm run lint:css` passes, then run `npm run lint:css:update-suppressions`. Review and commit the reductions.\n\n```bash\nnpm run format            # Fix JS formatting\nnpm run lint:js          # Check JS linting\nnpm run typecheck        # Type check sources, plus TypeScript tests and stories\nvendor/bin/phpcbf        # Fix PHP standards\nvendor/bin/phpcs         # Check PHP standards\n\n# Specific files\nvendor/bin/phpcbf <path_to_php_file.php>\n```\n\n## Architectural decisions\n\n-   **Package layering**: Three editor layers — `block-editor` (generic, WP-agnostic) → `editor` (WordPress post-type-aware) → `edit-post`/`edit-site` (full screens). Lower layers MUST NOT depend on higher ones.\n-   **Site editor parity**: the site editor exists twice — `packages/edit-site` (v1) and the extensible site editor (v2: the `routes/*` entry points declaring the `site-editor-v2` page, booted by `packages/boot`, behind the `gutenberg-extensible-site-editor` experiment). Any feature or enhancement added to the site editor MUST be added to the extensible site editor in the same change, so the two do not drift. If a feature deliberately does not belong in v2, say so explicitly instead of silently skipping it.\n-   **Block data model**: Blocks are in-memory tree structures during editing, serialized as HTML with comment delimiters (`<!-- wp:name -->`). Work with the block tree via APIs, not the serialized HTML.\n-   **Data layer**: Uses `@wordpress/data` (Redux-like stores). Edit entities through `core-data` actions (`editEntityRecord` / `saveEditedEntityRecord`), not direct state manipulation.\n-   **Styles system**: Three-layer merge — WordPress defaults < `theme.json` < user preferences. Use Block Supports API and CSS custom properties (`--wp--preset--*`), not hardcoded values.\n-   **Modularity**: Packages are available both as npm packages and WordPress scripts (`wp-*` handles). Production packages must work in both contexts.\n\nFor full architecture details, see `docs/explanations/architecture/`.\n\n## Common pitfalls\n\n-   Dependencies are isolated, not hoisted: `.npmrc` sets `install-strategy = \"linked\"`. Packages live in `node_modules/.store/` and are symlinked only into the `node_modules` of the workspaces that declare them, so a near-empty root `node_modules` (or a missing `node_modules/typescript`) is expected and does not mean the install failed. To check an install, look for `node_modules/.store/` (`ls -a`, since it is a dotfile) or just run the npm script you need.\n-   Do not add dependencies to the root `package.json`. Add them to the workspace that uses them, or create a new workspace under `tools/` (or `test/` for test infrastructure). See [Workspace Development](docs/contributors/code/workspace-development.md).\n-   Published package runtime files and emitted type declarations must resolve through the package's declared dependency surface; never rely on root hoisting or workspace links. Prefer fixing accidentally leaked public types over adding an unrelated heavy dependency, and run `npm run lint:published-deps` when changing package dependencies or exported types.\n-   PHP features in `lib/compat/` MUST go in the `wordpress-X.Y/` directory for their intended WordPress release. Inspect the available compatibility directories first; do not assume the newest one is right.\n-   Avoid using private APIs in bundled packages (packages without `wpScript` or `wpModuleExports`). Private APIs are intended for Core usage; bundled packages may also be imported via npm into plugin scripts, causing incompatibilities.\n-   Avoid adding new APIs prefixed with `__experimental` or `__unstable`. This pattern is now not used. Instead use private APIs or in bundled packages regular exports.\n-   `block-editor` is a WordPress-agnostic package. NEVER add `core-data` dependencies or direct REST API calls to it.\n-   `@wordpress/build` (`packages/wp-build`) is a generic build tool used both in Gutenberg and by plugins targeting WordPress Core directly. Avoid Gutenberg-specific changes in it.\n-   Pages and routes are stable by default. Mark experimental pages with `\"experimental\": true` in `wpPlugin.pages`; WordPress Core builds exclude those pages and routes used only by experimental pages. Do not introduce separate `stable` or `core` flags for this distinction.\n-   Prefer the `package.json` scripts, which accept file paths, such as `npm run test:unit -- <file>`, `npm run lint:js <file>` (which also checks Prettier formatting), and `npm run typecheck -- <path/to/tsconfig.json>`; see the [Testing Overview](docs/contributors/code/testing-overview.md) for more test commands. If no script fits, ask before running a binary directly, since a missing script can be a gap to fix and not to work around.\n-   Run a binary from an installed npm package with `npm exec --no -- <binary>`, which fails if it is not installed locally. Since `npx` and a bare `npm exec` download whatever the npm registry serves as a package under that name, use them only with a full package name, such as `npx @wordpress/create-block@latest`.\n-   PHP function and class names are renamed at build time (`gutenberg_*` prefix, `*_Gutenberg` suffix) to avoid conflicts with WordPress Core — the built names, not the source names, are what runs (and what tests must call). See `docs/contributors/code/build-system-function-prefixing.md`.\n-   For changes that affect package consumers, add a short entry to the package's `CHANGELOG.md`. Omit preserved behavior and internal migration steps. Follow [Maintaining Changelogs](packages/README.md#maintaining-changelogs).\n-   Packages with TypeScript dev files split their configs: `tsconfig.build.json` (src only, emits `build-types`) and the default `tsconfig.json` (dev project: tests and stories, `noEmit`, jest types). Packages without dev files keep a single `tsconfig.json` build project, and a few packages deviate (a dev-only project with handwritten declarations, or specialized build projects); see the TypeScript section in `packages/README.md`. Reference a split package by `../<pkg>/tsconfig.build.json` and an unsplit one by `../<pkg>`. `npm run build` never type checks (it emits declarations with `--noCheck`); use `npm run typecheck`, and never add jest types to a build project.\n-   A rejected `apiFetch` is not always an `Error`: a REST error arrives as a plain object (`{ code, message, data }`), `parse: false` rejects with the `Response` (which carries `status`, not `message`), an aborted request rethrows an `AbortError`, and a handler set via `setFetchHandler` can reject anything. Do not interpolate the rejection into a string (`` `${ error }` `` gives `[object Object]`) or branch on `instanceof Error`. Normalise it to a message before showing the user anything, and supply your own copy when there is none — `ensureError` in `packages/core-data/src/private-actions.js` is the reference implementation, though it is local to that file rather than exported.\n-   Treat block attributes and `theme.json` values as untrusted when their types haven't been validated. Raw values include `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Guard them before strict string operations. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated.\n\n## PR instructions\n\n-   Ensure build passes.\n-   Fix all formatting/linting issues; these are enforced through CI in PRs.\n",".github/copilot-instructions.md":"## PR review guidelines\n\nWhen reviewing pull requests:\n\n- Only comment on semantically meaningful issues: bugs, incorrect logic, security problems, accessibility regressions, or API contract violations.\n- Skip style, formatting, naming, and whitespace observations — these are enforced by lint and PHPCS.\n- Keep each comment short — one or two sentences maximum.\n- Do not write long descriptions or summaries of what the code does.\n- Do not suggest refactors or improvements unrelated to the PR's stated goal.\n- Keep the top-level review body empty unless a finding genuinely spans multiple files and can't be attached to a specific line. Put findings in inline comments wherever possible, and never use the review body to restate what the PR is doing.\n\n## What not to flag\n\n- **Do not speculate about external code.** Verify WordPress, PHP, and Node APIs, function signatures, and version constraints against the diff or repo files (`composer.json`, `package.json`, etc.) before flagging compatibility or contract issues. Do not invent strings, error messages, or behaviour in linked codebases such as WordPress core. If a claim cannot be verified from the diff or repository, do not include it.\n- **Calibrate edge-case warnings.** Do not flag theoretical edge cases (`Number.MAX_VALUE`, subnormals, inputs that cannot occur given current callers). Flag edge cases only when they correspond to inputs that can plausibly reach the code.\n\n## Gutenberg-specific context\n\n- Do not suggest replacing `@wordpress/data` selectors / actions with local React state — this is the project's intentional state pattern.\n- Do not suggest replacing `__()` / `_x()` / `_n()` calls with template literals — these are WordPress i18n functions.\n- Do not suggest moving code between `block-editor`, `editor`, and `edit-post` packages without considering the layering rule (`block-editor` is WordPress-agnostic; lower layers must not depend on higher ones).\n- Flag unguarded block attribute or `theme.json` values reaching a strict string operation such as `preg_match()`, `explode()`, `trim()` or `esc_url()`, but only where the value is raw: `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated — do not flag those. `sprintf( '%s' )`, `str_replace()`, `preg_replace()` and interpolation only warn, so they are not findings.\n\n## Writing Markdown\n\n- Do not hard-wrap Markdown. Keep each paragraph, list item, and table row on a single line; there is no line length limit (`MD013` is disabled).\n- Hard wrapping is an accessibility problem: screen readers and braille displays read the source file line by line, so a wrapped sentence arrives as fragments.\n- Do not reflow lines a change does not otherwise touch, in either a suggestion or a commit.\n- This applies to `.md` files and to the Markdown in pull request descriptions and comments.\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/plugin.json)",".cursorrules (wordpress-php-guzzle-gutenberg-cursorrules-prompt-.mdc)":"---\ndescription: \"Cursor rules for WordPress development with PHP, Guzzle, and Gutenberg integration.\"\nglobs: **/*\nalwaysApply: false\n---\n- You are operating in a WordPress plugin context, that has a Guzzle-based HTTP client, WP REST endpoint addition(s), and new Gutenberg editor blocks.\n\n- Always use WordPress coding standards when writing PHP, JavaScript, and TypeScript.\n\n- Always type hint PHP code.\n\n- Prefer writing TypeScript over JavaScript.\n\n- Favor functional paradigms over object-oriented ones, favor composition over inheritance, but be consistent with WordPress ecosystem best practices.\n\n- Optimize for readability.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\n## Dev environment tips\n\n```bash\n# Setup\nnvm use                    # Use the required node version\nnpm install && composer install\nnpm run wp-env-test status      # Always check status first.\nnpm run wp-env-test start       # Only start if not already running.\n\n# Development\nnpm start     # Development with watch\nnpm run build # Production build; emits types with --noCheck, does NOT type check\n```\n\nBefore verification or a commit in a fresh Git worktree, follow the [worktree setup instructions](docs/contributors/code/getting-started-with-code-contribution.md#set-up-each-worktree).\n\n`npm run build` never fails on type errors. After changing TypeScript or checked JS, run `npm run typecheck`.\n\n### Key Directories\n\n-   `/packages/` - JavaScript packages (each has README.md and CHANGELOG.md)\n-   `/routes/` - Route entry points for the admin single-page apps, one directory per route; each declares the page(s) it belongs to (the extensible site editor, the dashboard, the media editor, and others)\n-   `/lib/` - PHP code\n-   `/lib/compat/wordpress-X.Y/` - Version-specific features (new PHP features usually go here)\n-   `/phpunit/` - PHP tests\n-   `/schemas/json/` - JSON Schemas for `block.json`, `theme.json`, `font-collection.json`, and `wp-env.json` (published to `schemas.wp.org`; tests in `/test/integration/`)\n-   `/docs/` - Documentation\n    -   `/docs/contributors/` - Contributing guides\n    -   `/docs/explanations/architecture/` - System architecture docs\n    -   `/docs/how-to-guides/` - Implementation tutorials\n    -   `/docs/reference-guides/` - API documentation\n\n## Progressive discovery\n\nRead only what your task needs, when it needs it:\n\n-   **Contributor docs**: before starting a task, check `docs/contributors/code/` for the guide covering that kind of work (coding guidelines, backward compatibility, workspaces, releases) and read the relevant one.\n-   **User-facing copy**: before writing or changing a string a user reads, read `docs/contributors/documentation/copy-guide.md` — it covers terminology, capitalization, and how to word an error message.\n-   **Directory guides**: some directories carry their own `AGENTS.md` and `README.md` with rules for working there (e.g. `packages/components/AGENTS.md`) — read it before changing files in that directory.\n\n## Markdown files\n\nMarkdown in this repository is soft-wrapped: a paragraph is one long line, and the editor wraps it on screen. Do not hard-wrap it.\n\n-   Keep each paragraph, list item, and table row on a single line, however long it gets. There is no line length limit; `MD013` is disabled in `.markdownlint.json`.\n-   This covers every `.md` file (docs, `README.md`, `CHANGELOG.md`, templates) and the Markdown written into pull request descriptions, issues, and comments.\n-   This is an accessibility rule, not only a tidiness one: a screen reader reads a plain `.md` file line by line and a braille display shows one line at a time, so a hard-wrapped sentence arrives as fragments. See [Write accessible documentation](https://developers.google.com/style/accessibility).\n-   When editing an existing paragraph, rewrite the line in place. Never re-flow or re-wrap lines the change does not otherwise touch: it turns a one-word edit into a diff over the whole paragraph and buries the real change.\n-   Line breaks still belong where Markdown needs them: between list items, between table rows, and inside fenced code blocks, which are never re-wrapped either.\n-   A few older files are still hard-wrapped. Leave them as they are unless the task is to reflow them; do not match their style in new prose.\n\nSee [Documentation Contributions](docs/contributors/documentation/README.md) for the rest of the documentation conventions.\n\n## Code quality\n\nFix ESLint and Stylelint violations in the code whenever possible. Add entries or increase counts in `tools/eslint/suppressions.json` or `tools/stylelint/stylelint-suppressions.json` only as a last resort, and explain in the PR why a code fix is not practical. Keep suppressions limited to the specific violations that need them. After fixing suppressed violations, run `npm run lint:js:prune-suppressions` for ESLint. For Stylelint, first ensure `npm run lint:css` passes, then run `npm run lint:css:update-suppressions`. Review and commit the reductions.\n\n```bash\nnpm run format            # Fix JS formatting\nnpm run lint:js          # Check JS linting\nnpm run typecheck        # Type check sources, plus TypeScript tests and stories\nvendor/bin/phpcbf        # Fix PHP standards\nvendor/bin/phpcs         # Check PHP standards\n\n# Specific files\nvendor/bin/phpcbf <path_to_php_file.php>\n```\n\n## Architectural decisions\n\n-   **Package layering**: Three editor layers — `block-editor` (generic, WP-agnostic) → `editor` (WordPress post-type-aware) → `edit-post`/`edit-site` (full screens). Lower layers MUST NOT depend on higher ones.\n-   **Site editor parity**: the site editor exists twice — `packages/edit-site` (v1) and the extensible site editor (v2: the `routes/*` entry points declaring the `site-editor-v2` page, booted by `packages/boot`, behind the `gutenberg-extensible-site-editor` experiment). Any feature or enhancement added to the site editor MUST be added to the extensible site editor in the same change, so the two do not drift. If a feature deliberately does not belong in v2, say so explicitly instead of silently skipping it.\n-   **Block data model**: Blocks are in-memory tree structures during editing, serialized as HTML with comment delimiters (`<!-- wp:name -->`). Work with the block tree via APIs, not the serialized HTML.\n-   **Data layer**: Uses `@wordpress/data` (Redux-like stores). Edit entities through `core-data` actions (`editEntityRecord` / `saveEditedEntityRecord`), not direct state manipulation.\n-   **Styles system**: Three-layer merge — WordPress defaults < `theme.json` < user preferences. Use Block Supports API and CSS custom properties (`--wp--preset--*`), not hardcoded values.\n-   **Modularity**: Packages are available both as npm packages and WordPress scripts (`wp-*` handles). Production packages must work in both contexts.\n\nFor full architecture details, see `docs/explanations/architecture/`.\n\n## Common pitfalls\n\n-   Dependencies are isolated, not hoisted: `.npmrc` sets `install-strategy = \"linked\"`. Packages live in `node_modules/.store/` and are symlinked only into the `node_modules` of the workspaces that declare them, so a near-empty root `node_modules` (or a missing `node_modules/typescript`) is expected and does not mean the install failed. To check an install, look for `node_modules/.store/` (`ls -a`, since it is a dotfile) or just run the npm script you need.\n-   Do not add dependencies to the root `package.json`. Add them to the workspace that uses them, or create a new workspace under `tools/` (or `test/` for test infrastructure). See [Workspace Development](docs/contributors/code/workspace-development.md).\n-   Published package runtime files and emitted type declarations must resolve through the package's declared dependency surface; never rely on root hoisting or workspace links. Prefer fixing accidentally leaked public types over adding an unrelated heavy dependency, and run `npm run lint:published-deps` when changing package dependencies or exported types.\n-   PHP features in `lib/compat/` MUST go in the `wordpress-X.Y/` directory for their intended WordPress release. Inspect the available compatibility directories first; do not assume the newest one is right.\n-   Avoid using private APIs in bundled packages (packages without `wpScript` or `wpModuleExports`). Private APIs are intended for Core usage; bundled packages may also be imported via npm into plugin scripts, causing incompatibilities.\n-   Avoid adding new APIs prefixed with `__experimental` or `__unstable`. This pattern is now not used. Instead use private APIs or in bundled packages regular exports.\n-   `block-editor` is a WordPress-agnostic package. NEVER add `core-data` dependencies or direct REST API calls to it.\n-   `@wordpress/build` (`packages/wp-build`) is a generic build tool used both in Gutenberg and by plugins targeting WordPress Core directly. Avoid Gutenberg-specific changes in it.\n-   Pages and routes are stable by default. Mark experimental pages with `\"experimental\": true` in `wpPlugin.pages`; WordPress Core builds exclude those pages and routes used only by experimental pages. Do not introduce separate `stable` or `core` flags for this distinction.\n-   Prefer the `package.json` scripts, which accept file paths, such as `npm run test:unit -- <file>`, `npm run lint:js <file>` (which also checks Prettier formatting), and `npm run typecheck -- <path/to/tsconfig.json>`; see the [Testing Overview](docs/contributors/code/testing-overview.md) for more test commands. If no script fits, ask before running a binary directly, since a missing script can be a gap to fix and not to work around.\n-   Run a binary from an installed npm package with `npm exec --no -- <binary>`, which fails if it is not installed locally. Since `npx` and a bare `npm exec` download whatever the npm registry serves as a package under that name, use them only with a full package name, such as `npx @wordpress/create-block@latest`.\n-   PHP function and class names are renamed at build time (`gutenberg_*` prefix, `*_Gutenberg` suffix) to avoid conflicts with WordPress Core — the built names, not the source names, are what runs (and what tests must call). See `docs/contributors/code/build-system-function-prefixing.md`.\n-   For changes that affect package consumers, add a short entry to the package's `CHANGELOG.md`. Omit preserved behavior and internal migration steps. Follow [Maintaining Changelogs](packages/README.md#maintaining-changelogs).\n-   Packages with TypeScript dev files split their configs: `tsconfig.build.json` (src only, emits `build-types`) and the default `tsconfig.json` (dev project: tests and stories, `noEmit`, jest types). Packages without dev files keep a single `tsconfig.json` build project, and a few packages deviate (a dev-only project with handwritten declarations, or specialized build projects); see the TypeScript section in `packages/README.md`. Reference a split package by `../<pkg>/tsconfig.build.json` and an unsplit one by `../<pkg>`. `npm run build` never type checks (it emits declarations with `--noCheck`); use `npm run typecheck`, and never add jest types to a build project.\n-   A rejected `apiFetch` is not always an `Error`: a REST error arrives as a plain object (`{ code, message, data }`), `parse: false` rejects with the `Response` (which carries `status`, not `message`), an aborted request rethrows an `AbortError`, and a handler set via `setFetchHandler` can reject anything. Do not interpolate the rejection into a string (`` `${ error }` `` gives `[object Object]`) or branch on `instanceof Error`. Normalise it to a message before showing the user anything, and supply your own copy when there is none — `ensureError` in `packages/core-data/src/private-actions.js` is the reference implementation, though it is local to that file rather than exported.\n-   Treat block attributes and `theme.json` values as untrusted when their types haven't been validated. Raw values include `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Guard them before strict string operations. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated.\n\n## PR instructions\n\n-   Ensure build passes.\n-   Fix all formatting/linting issues; these are enforced through CI in PRs.\n","isInternal":false,"tokens":2637,"sizeBytes":11669},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"## PR review guidelines\n\nWhen reviewing pull requests:\n\n- Only comment on semantically meaningful issues: bugs, incorrect logic, security problems, accessibility regressions, or API contract violations.\n- Skip style, formatting, naming, and whitespace observations — these are enforced by lint and PHPCS.\n- Keep each comment short — one or two sentences maximum.\n- Do not write long descriptions or summaries of what the code does.\n- Do not suggest refactors or improvements unrelated to the PR's stated goal.\n- Keep the top-level review body empty unless a finding genuinely spans multiple files and can't be attached to a specific line. Put findings in inline comments wherever possible, and never use the review body to restate what the PR is doing.\n\n## What not to flag\n\n- **Do not speculate about external code.** Verify WordPress, PHP, and Node APIs, function signatures, and version constraints against the diff or repo files (`composer.json`, `package.json`, etc.) before flagging compatibility or contract issues. Do not invent strings, error messages, or behaviour in linked codebases such as WordPress core. If a claim cannot be verified from the diff or repository, do not include it.\n- **Calibrate edge-case warnings.** Do not flag theoretical edge cases (`Number.MAX_VALUE`, subnormals, inputs that cannot occur given current callers). Flag edge cases only when they correspond to inputs that can plausibly reach the code.\n\n## Gutenberg-specific context\n\n- Do not suggest replacing `@wordpress/data` selectors / actions with local React state — this is the project's intentional state pattern.\n- Do not suggest replacing `__()` / `_x()` / `_n()` calls with template literals — these are WordPress i18n functions.\n- Do not suggest moving code between `block-editor`, `editor`, and `edit-post` packages without considering the layering rule (`block-editor` is WordPress-agnostic; lower layers must not depend on higher ones).\n- Flag unguarded block attribute or `theme.json` values reaching a strict string operation such as `preg_match()`, `explode()`, `trim()` or `esc_url()`, but only where the value is raw: `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated — do not flag those. `sprintf( '%s' )`, `str_replace()`, `preg_replace()` and interpolation only warn, so they are not findings.\n\n## Writing Markdown\n\n- Do not hard-wrap Markdown. Keep each paragraph, list item, and table row on a single line; there is no line length limit (`MD013` is disabled).\n- Hard wrapping is an accessibility problem: screen readers and braille displays read the source file line by line, so a wrapped sentence arrives as fragments.\n- Do not reflow lines a change does not otherwise touch, in either a suggestion or a commit.\n- This applies to `.md` files and to the Markdown in pull request descriptions and comments.\n","isInternal":false,"tokens":626,"sizeBytes":3001},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/marketplace.json","title":"Claude Plugin Marketplace Catalog","category":"marketplace","format":"json","content":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/marketplace.json)","tokens":300,"sizeBytes":198},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":184},{"name":"mcp.json","path":".cursor/mcp.json","rawUrl":"https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.cursor/mcp.json","title":"Model Context Protocol (MCP) Configuration","category":"mcp-config","format":"json","content":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.cursor/mcp.json)","tokens":300,"sizeBytes":177},{"name":".cursorrules","path":".cursorrules (wordpress-php-guzzle-gutenberg-cursorrules-prompt-.mdc)","rawUrl":"https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/main/rules/wordpress-php-guzzle-gutenberg-cursorrules-prompt-.mdc","title":"Awesome Cursorrules: wordpress php guzzle gutenberg cursorrules prompt ","category":"cursor-rule","format":"markdown","content":"---\ndescription: \"Cursor rules for WordPress development with PHP, Guzzle, and Gutenberg integration.\"\nglobs: **/*\nalwaysApply: false\n---\n- You are operating in a WordPress plugin context, that has a Guzzle-based HTTP client, WP REST endpoint addition(s), and new Gutenberg editor blocks.\n\n- Always use WordPress coding standards when writing PHP, JavaScript, and TypeScript.\n\n- Always type hint PHP code.\n\n- Prefer writing TypeScript over JavaScript.\n\n- Favor functional paradigms over object-oriented ones, favor composition over inheritance, but be consistent with WordPress ecosystem best practices.\n\n- Optimize for readability.\n","tokens":119,"sizeBytes":633}],"systemPromptSnippet":"<yakaai_skills repo=\"WordPress/gutenberg\">\n<!-- File: AGENTS.md (Tokens: ~2637 | Category: root-instruction) -->\n# AGENTS.md\n\n## Dev environment tips\n\n```bash\n# Setup\nnvm use                    # Use the required node version\nnpm install && composer install\nnpm run wp-env-test status      # Always check status first.\nnpm run wp-env-test start       # Only start if not already running.\n\n# Development\nnpm start     # Development with watch\nnpm run build # Production build; emits types with --noCheck, does NOT type check\n```\n\nBefore verification or a commit in a fresh Git worktree, follow the [worktree setup instructions](docs/contributors/code/getting-started-with-code-contribution.md#set-up-each-worktree).\n\n`npm run build` never fails on type errors. After changing TypeScript or checked JS, run `npm run typecheck`.\n\n### Key Directories\n\n-   `/packages/` - JavaScript packages (each has README.md and CHANGELOG.md)\n-   `/routes/` - Route entry points for the admin single-page apps, one directory per route; each declares the page(s) it belongs to (the extensible site editor, the dashboard, the media editor, and others)\n-   `/lib/` - PHP code\n-   `/lib/compat/wordpress-X.Y/` - Version-specific features (new PHP features usually go here)\n-   `/phpunit/` - PHP tests\n-   `/schemas/json/` - JSON Schemas for `block.json`, `theme.json`, `font-collection.json`, and `wp-env.json` (published to `schemas.wp.org`; tests in `/test/integration/`)\n-   `/docs/` - Documentation\n    -   `/docs/contributors/` - Contributing guides\n    -   `/docs/explanations/architecture/` - System architecture docs\n    -   `/docs/how-to-guides/` - Implementation tutorials\n    -   `/docs/reference-guides/` - API documentation\n\n## Progressive discovery\n\nRead only what your task needs, when it needs it:\n\n-   **Contributor docs**: before starting a task, check `docs/contributors/code/` for the guide covering that kind of work (coding guidelines, backward compatibility, workspaces, releases) and read the relevant one.\n-   **User-facing copy**: before writing or changing a string a user reads, read `docs/contributors/documentation/copy-guide.md` — it covers terminology, capitalization, and how to word an error message.\n-   **Directory guides**: some directories carry their own `AGENTS.md` and `README.md` with rules for working there (e.g. `packages/components/AGENTS.md`) — read it before changing files in that directory.\n\n## Markdown files\n\nMarkdown in this repository is soft-wrapped: a paragraph is one long line, and the editor wraps it on screen. Do not hard-wrap it.\n\n-   Keep each paragraph, list item, and table row on a single line, however long it gets. There is no line length limit; `MD013` is disabled in `.markdownlint.json`.\n-   This covers every `.md` file (docs, `README.md`, `CHANGELOG.md`, templates) and the Markdown written into pull request descriptions, issues, and comments.\n-   This is an accessibility rule, not only a tidiness one: a screen reader reads a plain `.md` file line by line and a braille display shows one line at a time, so a hard-wrapped sentence arrives as fragments. See [Write accessible documentation](https://developers.google.com/style/accessibility).\n-   When editing an existing paragraph, rewrite the line in place. Never re-flow or re-wrap lines the change does not otherwise touch: it turns a one-word edit into a diff over the whole paragraph and buries the real change.\n-   Line breaks still belong where Markdown needs them: between list items, between table rows, and inside fenced code blocks, which are never re-wrapped either.\n-   A few older files are still hard-wrapped. Leave them as they are unless the task is to reflow them; do not match their style in new prose.\n\nSee [Documentation Contributions](docs/contributors/documentation/README.md) for the rest of the documentation conventions.\n\n## Code quality\n\nFix ESLint and Stylelint violations in the code whenever possible. Add entries or increase counts in `tools/eslint/suppressions.json` or `tools/stylelint/stylelint-suppressions.json` only as a last resort, and explain in the PR why a code fix is not practical. Keep suppressions limited to the specific violations that need them. After fixing suppressed violations, run `npm run lint:js:prune-suppressions` for ESLint. For Stylelint, first ensure `npm run lint:css` passes, then run `npm run lint:css:update-suppressions`. Review and commit the reductions.\n\n```bash\nnpm run format            # Fix JS formatting\nnpm run lint:js          # Check JS linting\nnpm run typecheck        # Type check sources, plus TypeScript tests and stories\nvendor/bin/phpcbf        # Fix PHP standards\nvendor/bin/phpcs         # Check PHP standards\n\n# Specific files\nvendor/bin/phpcbf <path_to_php_file.php>\n```\n\n## Architectural decisions\n\n-   **Package layering**: Three editor layers — `block-editor` (generic, WP-agnostic) → `editor` (WordPress post-type-aware) → `edit-post`/`edit-site` (full screens). Lower layers MUST NOT depend on higher ones.\n-   **Site editor parity**: the site editor exists twice — `packages/edit-site` (v1) and the extensible site editor (v2: the `routes/*` entry points declaring the `site-editor-v2` page, booted by `packages/boot`, behind the `gutenberg-extensible-site-editor` experiment). Any feature or enhancement added to the site editor MUST be added to the extensible site editor in the same change, so the two do not drift. If a feature deliberately does not belong in v2, say so explicitly instead of silently skipping it.\n-   **Block data model**: Blocks are in-memory tree structures during editing, serialized as HTML with comment delimiters (`<!-- wp:name -->`). Work with the block tree via APIs, not the serialized HTML.\n-   **Data layer**: Uses `@wordpress/data` (Redux-like stores). Edit entities through `core-data` actions (`editEntityRecord` / `saveEditedEntityRecord`), not direct state manipulation.\n-   **Styles system**: Three-layer merge — WordPress defaults < `theme.json` < user preferences. Use Block Supports API and CSS custom properties (`--wp--preset--*`), not hardcoded values.\n-   **Modularity**: Packages are available both as npm packages and WordPress scripts (`wp-*` handles). Production packages must work in both contexts.\n\nFor full architecture details, see `docs/explanations/architecture/`.\n\n## Common pitfalls\n\n-   Dependencies are isolated, not hoisted: `.npmrc` sets `install-strategy = \"linked\"`. Packages live in `node_modules/.store/` and are symlinked only into the `node_modules` of the workspaces that declare them, so a near-empty root `node_modules` (or a missing `node_modules/typescript`) is expected and does not mean the install failed. To check an install, look for `node_modules/.store/` (`ls -a`, since it is a dotfile) or just run the npm script you need.\n-   Do not add dependencies to the root `package.json`. Add them to the workspace that uses them, or create a new workspace under `tools/` (or `test/` for test infrastructure). See [Workspace Development](docs/contributors/code/workspace-development.md).\n-   Published package runtime files and emitted type declarations must resolve through the package's declared dependency surface; never rely on root hoisting or workspace links. Prefer fixing accidentally leaked public types over adding an unrelated heavy dependency, and run `npm run lint:published-deps` when changing package dependencies or exported types.\n-   PHP features in `lib/compat/` MUST go in the `wordpress-X.Y/` directory for their intended WordPress release. Inspect the available compatibility directories first; do not assume the newest one is right.\n-   Avoid using private APIs in bundled packages (packages without `wpScript` or `wpModuleExports`). Private APIs are intended for Core usage; bundled packages may also be imported via npm into plugin scripts, causing incompatibilities.\n-   Avoid adding new APIs prefixed with `__experimental` or `__unstable`. This pattern is now not used. Instead use private APIs or in bundled packages regular exports.\n-   `block-editor` is a WordPress-agnostic package. NEVER add `core-data` dependencies or direct REST API calls to it.\n-   `@wordpress/build` (`packages/wp-build`) is a generic build tool used both in Gutenberg and by plugins targeting WordPress Core directly. Avoid Gutenberg-specific changes in it.\n-   Pages and routes are stable by default. Mark experimental pages with `\"experimental\": true` in `wpPlugin.pages`; WordPress Core builds exclude those pages and routes used only by experimental pages. Do not introduce separate `stable` or `core` flags for this distinction.\n-   Prefer the `package.json` scripts, which accept file paths, such as `npm run test:unit -- <file>`, `npm run lint:js <file>` (which also checks Prettier formatting), and `npm run typecheck -- <path/to/tsconfig.json>`; see the [Testing Overview](docs/contributors/code/testing-overview.md) for more test commands. If no script fits, ask before running a binary directly, since a missing script can be a gap to fix and not to work around.\n-   Run a binary from an installed npm package with `npm exec --no -- <binary>`, which fails if it is not installed locally. Since `npx` and a bare `npm exec` download whatever the npm registry serves as a package under that name, use them only with a full package name, such as `npx @wordpress/create-block@latest`.\n-   PHP function and class names are renamed at build time (`gutenberg_*` prefix, `*_Gutenberg` suffix) to avoid conflicts with WordPress Core — the built names, not the source names, are what runs (and what tests must call). See `docs/contributors/code/build-system-function-prefixing.md`.\n-   For changes that affect package consumers, add a short entry to the package's `CHANGELOG.md`. Omit preserved behavior and internal migration steps. Follow [Maintaining Changelogs](packages/README.md#maintaining-changelogs).\n-   Packages with TypeScript dev files split their configs: `tsconfig.build.json` (src only, emits `build-types`) and the default `tsconfig.json` (dev project: tests and stories, `noEmit`, jest types). Packages without dev files keep a single `tsconfig.json` build project, and a few packages deviate (a dev-only project with handwritten declarations, or specialized build projects); see the TypeScript section in `packages/README.md`. Reference a split package by `../<pkg>/tsconfig.build.json` and an unsplit one by `../<pkg>`. `npm run build` never type checks (it emits declarations with `--noCheck`); use `npm run typecheck`, and never add jest types to a build project.\n-   A rejected `apiFetch` is not always an `Error`: a REST error arrives as a plain object (`{ code, message, data }`), `parse: false` rejects with the `Response` (which carries `status`, not `message`), an aborted request rethrows an `AbortError`, and a handler set via `setFetchHandler` can reject anything. Do not interpolate the rejection into a string (`` `${ error }` `` gives `[object Object]`) or branch on `instanceof Error`. Normalise it to a message before showing the user anything, and supply your own copy when there is none — `ensureError` in `packages/core-data/src/private-actions.js` is the reference implementation, though it is local to that file rather than exported.\n-   Treat block attributes and `theme.json` values as untrusted when their types haven't been validated. Raw values include `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Guard them before strict string operations. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated.\n\n## PR instructions\n\n-   Ensure build passes.\n-   Fix all formatting/linting issues; these are enforced through CI in PRs.\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~626 | Category: copilot-instructions) -->\n## PR review guidelines\n\nWhen reviewing pull requests:\n\n- Only comment on semantically meaningful issues: bugs, incorrect logic, security problems, accessibility regressions, or API contract violations.\n- Skip style, formatting, naming, and whitespace observations — these are enforced by lint and PHPCS.\n- Keep each comment short — one or two sentences maximum.\n- Do not write long descriptions or summaries of what the code does.\n- Do not suggest refactors or improvements unrelated to the PR's stated goal.\n- Keep the top-level review body empty unless a finding genuinely spans multiple files and can't be attached to a specific line. Put findings in inline comments wherever possible, and never use the review body to restate what the PR is doing.\n\n## What not to flag\n\n- **Do not speculate about external code.** Verify WordPress, PHP, and Node APIs, function signatures, and version constraints against the diff or repo files (`composer.json`, `package.json`, etc.) before flagging compatibility or contract issues. Do not invent strings, error messages, or behaviour in linked codebases such as WordPress core. If a claim cannot be verified from the diff or repository, do not include it.\n- **Calibrate edge-case warnings.** Do not flag theoretical edge cases (`Number.MAX_VALUE`, subnormals, inputs that cannot occur given current callers). Flag edge cases only when they correspond to inputs that can plausibly reach the code.\n\n## Gutenberg-specific context\n\n- Do not suggest replacing `@wordpress/data` selectors / actions with local React state — this is the project's intentional state pattern.\n- Do not suggest replacing `__()` / `_x()` / `_n()` calls with template literals — these are WordPress i18n functions.\n- Do not suggest moving code between `block-editor`, `editor`, and `edit-post` packages without considering the layering rule (`block-editor` is WordPress-agnostic; lower layers must not depend on higher ones).\n- Flag unguarded block attribute or `theme.json` values reaching a strict string operation such as `preg_match()`, `explode()`, `trim()` or `esc_url()`, but only where the value is raw: `$block['attrs']` in render filters, unregistered attributes, and nested `style` or `layout` values. Registered top-level attributes passed to render callbacks or block-support `apply` callbacks are already schema-validated — do not flag those. `sprintf( '%s' )`, `str_replace()`, `preg_replace()` and interpolation only warn, so they are not findings.\n\n## Writing Markdown\n\n- Do not hard-wrap Markdown. Keep each paragraph, list item, and table row on a single line; there is no line length limit (`MD013` is disabled).\n- Hard wrapping is an accessibility problem: screen readers and braille displays read the source file line by line, so a wrapped sentence arrives as fragments.\n- Do not reflow lines a change does not otherwise touch, in either a suggestion or a commit.\n- This applies to `.md` files and to the Markdown in pull request descriptions and comments.\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~300 | Category: marketplace) -->\n# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/marketplace.json)\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.claude-plugin/plugin.json)\n\n<!-- File: .cursor/mcp.json (Tokens: ~300 | Category: mcp-config) -->\n# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/.cursor/mcp.json)\n\n<!-- File: .cursorrules (wordpress-php-guzzle-gutenberg-cursorrules-prompt-.mdc) (Tokens: ~119 | Category: cursor-rule) -->\n---\ndescription: \"Cursor rules for WordPress development with PHP, Guzzle, and Gutenberg integration.\"\nglobs: **/*\nalwaysApply: false\n---\n- You are operating in a WordPress plugin context, that has a Guzzle-based HTTP client, WP REST endpoint addition(s), and new Gutenberg editor blocks.\n\n- Always use WordPress coding standards when writing PHP, JavaScript, and TypeScript.\n\n- Always type hint PHP code.\n\n- Prefer writing TypeScript over JavaScript.\n\n- Favor functional paradigms over object-oriented ones, favor composition over inheritance, but be consistent with WordPress ecosystem best practices.\n\n- Optimize for readability.\n</yakaai_skills>"}