Repository navigation
Agents: Add a defensive data design skill and route to the copy guide #81131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
9abb754
Agents: Add a defensive data design skill
ramonjd 30de8a4
Agents: Route to the copy guide and document the apiFetch error shape
ramonjd 02d33e8
Agents: Narrow the apiFetch and undo claims, condense untrusted data
ramonjd 39f51b1
Agents: Correct the rejection, block attribute and PHP failure claims
ramonjd bc3635c
Agents: Cover pre-mutation disclosure, reversibility and legibility
ramonjd d83bec4
Agents: Add failure recovery, the snackbar timeout and a boundary link
ramonjd be68477
Agents: Reword the error message fallback clause
ramonjd e788a65
Agents: Split the PHP failure modes by what is indexed, not by syntax
ramonjd 1560cd2
Agents: Make the legibility rule readable and drop the cost metaphor
ramonjd c28489c
Agents: Tighten the skill wording
ramonjd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| --- | ||
| name: defensive-data-design | ||
| description: Use when adding or changing a user-facing error message or notice, a destructive or irreversible action, or code that reads data it did not create (REST responses, block attributes, post meta, theme.json, localStorage). | ||
| --- | ||
|
|
||
| # Defensive data design | ||
|
|
||
| Safe defaults for code that can lose someone's work or leave them stuck. Apply them as checks, not intentions. | ||
|
|
||
| ## Errors the user sees | ||
|
|
||
| - Say the cause, not just the symptom. Where the failure carries a usable message, show it (and its `code` if that helps); fall back to your own copy when it does not. A `catch` that receives an error and shows a fixed string is a defect. | ||
| - Rejection shapes from `apiFetch` vary — see the pitfall in the root `AGENTS.md` before reading a cause from one. | ||
| - REST validation failures carry per-field reasons in `error.data.params`. The top-level message only names the field. | ||
| - Strip tags with `__unstableStripHTML` from `@wordpress/dom` rather than discarding a message that contains HTML. | ||
| - Never swallow. An empty `catch` is acceptable only when the failure is expected and the fallback is correct — say which failure, in a comment. | ||
| - Show enough of a failure to act on, and make it copyable where there is room. Keep the user's content out of the payload, and the current URL too: it can carry a nonce. | ||
| - Read the [copy guide's Error Messaging section](../../../docs/contributors/documentation/copy-guide.md#error-messaging) before writing the string. Plain words, name the cause, offer a next step. | ||
|
|
||
| ## Mutations and destructive actions | ||
|
|
||
| Prefer reversible changes, and make both the change and the way back visible. An irreversible one should be harder to trigger. | ||
|
|
||
| - Before a consequential mutation, show what it targets, who will see the result, and the state it leaves behind. Default to draft, private or reversible; make public or permanent the deliberate choice. | ||
| - Moving something to the trash should be easy; deleting it should be hard. If both sit in the same menu behind the same confirmation, the confirmation is doing no work. | ||
| - Confirm dialogs echo what is affected: the item's title, or the count for bulk actions. | ||
| - Irreversible confirms use `isDestructive` on the confirm button and say so in the label ("Delete permanently", not "Delete"). Follow the [destructive actions pattern](../../../storybook/stories/design-system/patterns/destructive-actions.mdx). | ||
| - Announce a mutation that succeeds, and offer Undo when the prior value is in scope. Capture that value explicitly rather than popping the undo stack, which would revert unrelated edits too. | ||
| - `Snackbar` renders one action only; more logs a warning and truncates to `actions[0]` (`packages/components/src/snackbar/index.tsx`). Undo or another button, not both. | ||
| - A snackbar dismisses itself six seconds after appearing. Keep it readable in that time, and use `explicitDismiss` or a different notice for anything that must be read. | ||
| - When a mutation fails, leave the user able to try again: keep their input, restore consistent state, release any control left busy, and make a retry safe. | ||
| - `saveEntityRecord` and `deleteEntityRecord` add nothing to the undo stack, so anything already persisted needs its own recovery path. `editEntityRecord` records an undo level unless called with `undoIgnore`. | ||
|
|
||
| ## Legibility | ||
|
|
||
| - Use the space you have: show the value itself rather than a label that flattens it, instead of making someone open a panel to find out. | ||
| - Make sure a label reflects reality, including any qualifier it would otherwise hide. For example: | ||
| - "Published" on a password-protected post reads as publicly readable when it is not. | ||
| - A template reads as the theme's when it carries local edits that belong to the site. | ||
|
|
||
| ## Data you did not create | ||
|
|
||
| Guard where untrusted data enters — a REST response, post meta, `theme.json`, a filtered editor setting, a storage read — not at every read downstream. Block attributes are only partly covered: `packages/blocks/src/api/parser/get-block-attributes.ts` substitutes the declared default when a parsed value fails its type, but the `blocks.getBlockAttributes` filter runs after that check and `updateBlockAttributes` does not check at all, so a wrongly typed value still reaches save functions and PHP. | ||
|
|
||
| - `JSON.parse` at a boundary goes in a `try`/`catch`, and the result is shape-checked (`Array.isArray`, `is_array`) before it is mapped or iterated: valid JSON of the wrong shape is the common case, not malformed JSON. | ||
| - In PHP the failure mode depends on what you touch, and a fatal during rendering is a white screen for every visitor: | ||
| - Warns and yields `null`: `foreach` over a non-iterable; an offset on `null`, an int, a bool or a float. Bad data flows on quietly. | ||
| - Fatal: a string offset on a string (`$str['slug']`); an array or object used as an offset; an object treated as an array. Guard these first. | ||
| - Check where a throw lands. [React error boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) catch render-phase errors only — a throw in a `registry.subscribe` callback, an async click handler or a promise chain escapes them and can silently drop the user's edit. | ||
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.