Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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).
.claude/skills/wordpress-defensive-data-design/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 78% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 34% | 0% |
Safe defaults for code that can lose someone's work or leave them stuck. Apply them as checks, not intentions.
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.apiFetch vary — see the pitfall in the root AGENTS.md before reading a cause from one.error.data.params. The top-level message only names the field.__unstableStripHTML from @wordpress/dom rather than discarding a message that contains HTML.catch is acceptable only when the failure is expected and the fallback is correct — say which failure, in a comment.Prefer reversible changes, and make both the change and the way back visible. An irreversible one should be harder to trigger.
isDestructive on the confirm button and say so in the label ("Delete permanently", not "Delete"). Follow the destructive actions pattern.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.explicitDismiss or a different notice for anything that must be read.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.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.null: foreach over a non-iterable; an offset on null, an int, a bool or a float. Bad data flows on quietly.$str['slug']); an array or object used as an offset; an object treated as an array. Guard these first.registry.subscribe callback, an async click handler or a promise chain escapes them and can silently drop the user's edit.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | pass→pass | 11,870 | 8,807 | -26% | 1 | 1 | 0% | 1,983 | 2,634 | +33% | 0 | 0 | — |
case-10 | fail→pass | 18,172 | 18,857 | +4% | 1 | 1 | 0% | 3,363 | 4,715 | +40% | 0 | 0 | — |
case-01 | fail→pass | 21,216 | 48,109 | +127% | 1 | 1 | 0% | 4,429 | 7,002 | +58% | 0 | 0 | — |
case-02 | fail→pass | 20,546 | 20,345 | -1% | 1 | 1 | 0% | 3,735 | 5,212 | +40% | 0 | 0 | — |
case-11 | pass→pass | 15,947 | 15,176 | -5% | 1 | 1 | 0% | 2,805 | 3,585 | +28% | 0 | 0 | — |
case-03 | pass→pass | 17,640 | 16,656 | -6% | 1 | 1 | 0% | 3,386 | 4,414 | +30% | 0 | 0 | — |
case-04 | fail→pass | 16,014 | 21,171 | +32% | 1 | 1 | 0% | 3,002 | 5,352 | +78% | 0 | 0 | — |
case-05 | pass→pass | 9,430 | 6,051 | -36% | 1 | 1 | 0% | 1,849 | 2,243 | +21% | 0 | 0 | — |
case-06 | pass→pass | 18,157 | 18,716 | +3% | 1 | 1 | 0% | 3,334 | 3,841 | +15% | 0 | 0 | — |
case-07 | fail→pass | 13,198 | 11,468 | -13% | 1 | 1 | 0% | 2,393 | 3,205 | +34% | 0 | 0 | — |
case-08 | pass→pass | 13,984 | 13,186 | -6% | 1 | 1 | 0% | 2,472 | 3,156 | +28% | 0 | 0 | — |
case-12 | pass→pass | 16,374 | 14,259 | -13% | 1 | 1 | 0% | 3,022 | 3,927 | +30% | 0 | 0 | — |
case-13 | pass→pass | 11,420 | 7,523 | -34% | 1 | 1 | 0% | 2,130 | 2,377 | +12% | 0 | 0 | — |
case-14 | pass→pass | 8,856 | 3,684 | -58% | 1 | 1 | 0% | 1,535 | 1,723 | +12% | 0 | 0 | — |
case-15 | pass→pass | 15,332 | 16,897 | +10% | 1 | 1 | 0% | 2,505 | 3,973 | +59% | 0 | 0 | — |
case-16 | pass→pass | 16,750 | 18,941 | +13% | 1 | 1 | 0% | 2,994 | 4,624 | +54% | 0 | 0 | — |
case-17 | pass→pass | 11,574 | 11,635 | +1% | 1 | 1 | 0% | 1,931 | 3,204 | +66% | 0 | 0 | — |
case-18 | pass→pass | 15,768 | 7,661 | -51% | 1 | 1 | 0% | 2,339 | 2,359 | +1% | 0 | 0 | — |
case-19 | pass→pass | 7,695 | 4,631 | -40% | 1 | 1 | 0% | 1,227 | 1,931 | +57% | 0 | 0 | — |
case-20 | pass→fail | 11,585 | 11,662 | +1% | 1 | 1 | 0% | 1,882 | 3,103 | +65% | 0 | 0 | — |
case-21 | pass→pass | 9,659 | 7,583 | -21% | 1 | 1 | 0% | 1,711 | 2,399 | +40% | 0 | 0 | — |
case-22 | pass→pass | 13,582 | 15,006 | +10% | 1 | 1 | 0% | 2,413 | 3,865 | +60% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.