Add core/edit-content ability for targeted post content edits - #945
Add core/edit-content ability for targeted post content edits#945seedprod wants to merge 1 commit into
Conversation
Registers a write twin of core/read-content: a server-side, exact-match text replacement on a post's title, excerpt, or content. The snippet must match exactly expected_matches times (default 1); every failure leaves the post unchanged. Saves through wp_update_post() so revisions and save filters apply, and reports the persisted result compactly without echoing the full field value. The ability registers through its own gated class so write access is an individually gateable unit alongside the read-only gated abilities. See WordPress#40. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #945 +/- ##
=============================================
+ Coverage 76.01% 76.12% +0.11%
- Complexity 3032 3074 +42
=============================================
Files 132 133 +1
Lines 12001 12241 +240
=============================================
+ Hits 9123 9319 +196
- Misses 2878 2922 +44
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
What?
See #40.
Adds a
core/edit-contentAbility — the write twin ofcore/read-content(#739). It applies a server-side, exact-match text replacement to a post's title, excerpt, or content, instead of requiring clients to round-trip the whole field.Why?
Whole-field writes force an AI client to fetch an entire content blob, mutate it in context, and send it all back — expensive, and every untouched byte the model re-emits is a byte it can corrupt. A server-side anchored patch means only the changed snippet ever transits the client. This is the editing contract AI coding tools have broadly converged on (exact string, unique match, fail loudly), applied to WordPress content with capability checks and revisions.
How?
old_contentis matched byte-for-byte against the stored raw value; the match count must equalexpected_matches(default 1) or the edit is refused with the actual count. No regex, no fuzzy matching — a stale or wrong snippet fails loudly rather than editing the wrong place.wp_update_post(), so revisions and normal save filters apply. The response reportsreplaced,status,modified_gmt, and anexact_persistenceflag (false when save filters such as KSES altered the saved value) — deliberately not the full field value.show_in_abilities+current_user_can( 'edit_post' ), resolved before execution; all denials are uniform.Gated\Edit_Content) rather than insideRead_Content, so write access is an individually removable unit via thewpai_gated_abilitiesfilter. Maintainers may prefer a separate experiment for write abilities — open to that.Known limitations (documented in code): the match check and save are not atomic (patch-on-snapshot, not compare-and-swap);
is_serialized()guards whole-value serialization only; the input schema's post type enum snapshots exposure at registration (same as read). Natural follow-up: a bounded raw search/snippet ability so clients can locate exact anchors without reading whole fields.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code (Fable); Codex CLI (independent design review)
Model(s): Claude Fable 5, GPT-5.6-sol
Used for: Implementation and tests, built to my specification and design decisions; the design and final patch were adversarially reviewed with independent AI reviewers before submission. I take responsibility for this contribution.
Testing Instructions
npm run wp-env:test startnpm run test:php -- --filter EditContentTest(18 tests: happy paths for all three fields with revision assertions, every refusal code, capability and exposure denials, serialized refusal, literal$/\handling, fail-closed ID parsing, expected-match semantics)npm run test:php— 1391 tests passing locally;composer lintandcomposer phpstanclean.Changelog Entry
🤖 Generated with Claude Code