refactor(preview): merge dual preview primitives into unified PendingActionStore lane#1171
Merged
refactor(preview): merge dual preview primitives into unified PendingActionStore lane#1171
Conversation
…action_handlers Adds ContentActionHandlers, which wires edit_post_blocks, replace_post_blocks, and insert_content onto the unified datamachine_pending_action_handlers filter. Each handler replays the stored apply_input through the relevant ability's execute() with preview stripped, and gates resolution with a shared edit_post cap check. Also extends PendingActionHelper::stage() to accept a caller-supplied action_id so abilities can embed the id inside their preview_data payload before staging.
…fId → actionId The three content abilities (edit_post_blocks, replace_post_blocks, insert_content) now stage pending previews through PendingActionHelper::stage() instead of the dedicated PendingDiffStore. The same canonical diff payload is built via CanonicalDiffPreview::build() and nested inside the staged envelope as preview_data, so the Gutenberg diff block and any other consumer still reads one stable shape. CanonicalDiffPreview is narrowed to a pure payload builder: the storage-helper method store_pending() and the response wrapper response() are gone, and the canonical field is renamed from diffId to actionId to match the unified id space produced by PendingActionStore::generate_id(). Ability preview responses now expose `action_id` (top-level, from the staged envelope) and a renamable `is_preview` boolean flag; the old `preview` boolean key is repurposed to hold the preview_data payload returned by PendingActionHelper::stage().
…ine_diff_resolved Full-stack removal of the parallel diff-only preview lane. Everything that previously routed through PendingDiffStore / ResolveDiffAbility / datamachine/resolve-diff / POST /datamachine/v1/diff/resolve / datamachine_diff_resolved now routes through the unified PendingAction lane added in the previous two commits. - Deletes inc/Abilities/Content/PendingDiffStore.php. - Deletes inc/Abilities/Content/ResolveDiffAbility.php (the ability, the REST route, and the datamachine_diff_resolved action all go with it). - Drops the corresponding require_once + instantiation lines from the plugin bootstrap. - Adds require_once for inc/Abilities/Content/ContentActionHandlers.php so the filter registrations fire at plugin-load time. - Drops the historical reference to PendingDiffStore from PendingActionStore's class docblock (no more successor language — the store is the primitive). No backwards-compatibility shim. In-flight transients under the old dm_pending_diff_ prefix (1-hour TTL) become unresolvable on upgrade; the worst case is a user re-running the preview.
Adds a Preview & Approval Filters section to core-filters.md covering datamachine_pending_action_handlers, datamachine_pending_action_staged, datamachine_pending_action_resolved, and datamachine_tool_action_policy. Includes a 'which preview primitive should I use?' callout pointing integrators at the one lane that now exists. Also adds tests/Unit/Abilities/Content/ContentActionHandlersTest.php, a WP_UnitTestCase suite that exercises the full stage → resolve round trip for each content kind, verifies the actionId canonical field, confirms rejection leaves content untouched, and checks the can_resolve gate denies unauthorized users.
This was referenced Apr 23, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Collapses Data Machine's two preview/approve primitives into a single
extensible lane. Everything — content edits, socials publishes, and any
future preview-opting tool — now stages through
PendingActionStoreanddispatches via the
datamachine_pending_action_handlersfilter.Removed:
PendingDiffStore(class)ResolveDiffAbility(class,datamachine/resolve-diffability,POST /datamachine/v1/diff/resolveREST route,datamachine_diff_resolvedaction)CanonicalDiffPreview::store_pending()and::response()helpersdm_pending_diff_transient prefix and thediff_id prefixKept, now with unified routing:
CanonicalDiffPreview::build()— pure payload builder, now emitsactionId(wasdiffId) so the payload lines up with the staged envelope's top-levelaction_id.edit_post_blocks,replace_post_blocks,insert_content) still produce the exact same canonical diff shape — just nested insidepreview_dataof thePendingActionenvelope.Added:
inc/Abilities/Content/ContentActionHandlers.php— registers the three kinds ondatamachine_pending_action_handlerswith sharededit_postcap gating.PendingActionHelper::stage()now accepts an optional caller-suppliedaction_idso abilities can embed the id insidepreview_databefore staging.tests/Unit/Abilities/Content/ContentActionHandlersTest.php— full stage → accept/reject round trip for each kind, pluscan_resolvegating.docs/development/hooks/core-filters.mdnow has a Preview & Approval Filters section covering the four hooks (datamachine_pending_action_handlers,datamachine_pending_action_staged,datamachine_pending_action_resolved,datamachine_tool_action_policy) with a "which preview primitive should I use?" callout.Why
The two lanes did almost the same thing.
PendingDiffStorewas a transientwrapper that only knew about three hardcoded ability types via a closed
switch($type)inResolveDiffAbility::apply()— not extensible.PendingActionStorealready has the right filter seam(
datamachine_pending_action_handlers). The merger routes the threecontent kinds through that filter and retires the switch. One class, one
store, one REST endpoint, one hook — applies to everything going forward.
Breaking changes
This is a hard cutover with no backwards-compatibility shim.
\DataMachine\Abilities\Content\PendingDiffStore\DataMachine\Engine\AI\Actions\PendingActionStore\DataMachine\Abilities\Content\ResolveDiffAbility\DataMachine\Engine\AI\Actions\ResolvePendingActionAbilitydatamachine/resolve-diffabilitydatamachine/resolve-pending-actionPOST /datamachine/v1/diff/resolvePOST /datamachine/v1/actions/resolvedo_action( 'datamachine_diff_resolved', ... )do_action( 'datamachine_pending_action_resolved', $decision, $action_id, $kind, $payload, $result )diff_id/diffIdparam & fieldaction_id/actionIddm_pending_diff_*transientsdm_pa_*transientsAny in-flight pending diff transient at the moment of upgrade becomes
unresolvable (1-hour TTL). Callers re-run the preview.
Downstream known consumer:
Extra-Chill/data-machine-editor. A follow-upPR on that repo migrates its REST bridge, Gutenberg block attributes, and
TypeScript surface to the new lane.
Testing
tests/Unit/Abilities/Content/ContentActionHandlersTest.php— 7 test methods covering:apply+can_resolve.edit_post_blockspreview → stage envelope → accept → post content updated; transient deleted.edit_post_blockspreview → reject → post untouched; transient deleted.replace_post_blocksandinsert_contentpreview → stage → accept → applied.can_resolvedenies users withoutedit_post; apply never runs; transient preserved.actionId(notdiffId) and matches top-levelaction_id.datamachine_pending_action_resolvedfires exactly once per resolution.php -l.AI assistance
ContentActionHandlersfilter registrar, the rewrites to each content ability's preview branch, thePendingActionHelper::stage()action_idpassthrough, the docblock/docs updates, and the test suite. Chris reviewed the architectural direction (single-lane unification with no compat shim), the final commit breakdown, and the PR scope boundary (core only; DM-editor migration lands in a separate follow-up PR).