Skip to content

wp-plugin-development: hook timing, CPT queryability, gotchas/traps, secrets, i18n - #3

Open
sboisvert wants to merge 2 commits into
trunkfrom
principal-wp/wp-plugin-development-improvements
Open

wp-plugin-development: hook timing, CPT queryability, gotchas/traps, secrets, i18n#3
sboisvert wants to merge 2 commits into
trunkfrom
principal-wp/wp-plugin-development-improvements

Conversation

@sboisvert

Copy link
Copy Markdown

Summary

Adds three new reference files and several procedure improvements to the wp-plugin-development skill.

New reference files

File Covers
references/security-deep-cuts.md Nonce internals (lifetime, generation, verification return values), password hashing, capability edge cases (super-admin, primitive vs meta caps), KSES defaults
references/gotchas-and-traps.md Return-value traps that defeat "did it succeed?" checks: get_post_meta returning '', \$wpdb->insert() returning rows-affected not insert ID, update_post_meta() returning false on identical values, is_admin() checking URL not role, wp_redirect() not exiting; plus numeric limits, hook ordering, WP_Query vs get_posts()
references/i18n-gotchas.md Text-domain-must-be-literal-string rule, why locale functions only return reliable values after init, escaping and translating in the same call

SKILL.md updates

  • Hook-registration timing: Register add_action/add_filter at plugin load time — not from inside a method that itself runs on init. Calling add_action() from an init callback re-registers on every request and causes hooks to fire multiple times.
  • CPT publicly_queryable matching: When requirements describe a "landing page" or "URL per resource", the CPT needs publicly_queryable => true (or an explicit alternative documented as a design decision). Mismatched queryability is a common source of "where's my page?" bugs.
  • Server-side validation: Enforce min/max/required/format constraints in save handlers, not only in JS. Direct POST and API requests bypass client-side validation entirely.
  • Secrets storage: API keys and credentials belong in environment variables or a host-provided secrets store, not in wp_options / post meta / transients.
  • New section "Avoid common WordPress gotchas" routes to the new reference files.

Why this is useful

These cover failures that look correct in code review and break under realistic conditions:

  • The add_action-from-an-init-callback bug is invisible until production traffic — at which point the same callback runs 5–50 times per request.
  • update_post_meta() returning false for identical values is the most common "my code seems broken" misread when calls are wrapped in if ( ! update_post_meta(...) ).
  • Client-side-only validation looks complete but is bypassed by every API consumer that isn't the editor UI.

Test plan

  • Confirm all new reference files render correctly on GitHub
  • Confirm SKILL.md frontmatter is upstream-compatible (no Principal-specific fields)
  • (Optional) Add eval scenarios for: hook-registration timing detection, gotcha return-value checks

🤖 Generated with Claude Code

…tchas/traps, secrets, i18n

Adds three new reference files:

- security-deep-cuts.md: nonce internals (lifetime, generation, verify
  return values), password hashing, capability edge cases (super-admin,
  primitive vs meta caps), KSES defaults and how `wp_kses_post()` differs
  from `wp_kses_data()`.
- gotchas-and-traps.md: return-value traps that break "did it succeed?"
  checks (`get_post_meta` returning `''` not `null`, `$wpdb->insert()`
  returning rows-affected not insert ID, `update_post_meta()` returning
  `false` on identical values, `is_admin()` checking the URL not the
  user, `wp_redirect()` not exiting), numeric limits, hook-ordering, and
  `WP_Query` vs `get_posts()` differences.
- i18n-gotchas.md: text-domain-must-be-literal-string rule, why locale
  functions only return reliable values after `init`, sprintf vs
  number-format, escaping and translating in the same call.

SKILL.md changes:

- Hook-registration timing: register `add_action`/`add_filter` at plugin
  load, not from inside a method that itself runs on `init`. Calling
  `add_action()` from an `init` callback re-registers the callback every
  request and causes hooks to fire multiple times.
- CPT publicly_queryable matching: when requirements describe a "landing
  page" or "URL per resource", `publicly_queryable => true` is required
  (or an explicit alternative documented as a design decision).
- Server-side validation: enforce min/max/required/format constraints in
  save handlers, not only in JS. Direct POST and API requests bypass
  client-side validation entirely.
- Secrets storage: API keys and credentials belong in environment
  variables or a host-provided secrets store, never in `wp_options`,
  post meta, or transients.
- New step "Avoid common WordPress gotchas" routes to the two new
  reference files for return-value traps and i18n traps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sboisvert
sboisvert force-pushed the principal-wp/wp-plugin-development-improvements branch from cb37a03 to 054e107 Compare May 6, 2026 23:38
New references/recent-release-deltas.md covering plugin-author-relevant
changes that drive code edits across WP 6.7, 6.8, and 6.9:

- Requires Plugins header (6.5 baseline)
- register_block_template() (6.7) -- ship templates without a child theme
- Speculation Rules ship by default; opt out stateful URLs (6.8)
- bcrypt default password hash + wp_check_password() rehash semantics (6.8)
- Auto translation loading; drop load_plugin_textdomain() at 6.7+
- HTML API maturation (full parser, set_modifiable_text, normalize, serialize_token)
- WP_Block_Processor streaming parser (6.9) -- 3MB post 14GB scenario fixed
- WP_Query cache key changes (6.9) -- breaking for warm-cache plugins
- register_post_type embeddable, wp_next_scheduled as filter (6.8)
- Abilities API registration pattern for plugin features (6.9)
- HTTPS URL escaping default (6.9)
- wp_mail() cid: inline images + state isolation (6.9)
- Admin menu search source change (6.9)
- fetchpriority + in_footer args on scripts and script modules (6.9)
- Template enhancement output buffer -- ob_start replacement (6.9)
- apiVersion 3 deprecation runway (6.9, enforced 7.0)
- IE conditional support removed (6.9)
- UTF-8 modernization with pure-PHP fallback (6.9)

Sources: Make/Core dev notes, 6.7/6.8/6.9 Field Guides, 6.9 Frontend Perf
Field Guide. Each entry has source URL and version anchor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant