Undo: snapshot/restore for AI-driven changes#19
Merged
Merged
Conversation
Mutating abilities capture a before-state via the Reversible trait + Snapshot helpers and attach it to their result under _undo. gds-assistant peels that off (never shown to the LLM), stores it, and replays it through the gds-mcp/restore_snapshot filter, which RestoreSnapshot dispatches by kind: restore-post (+ partial merge), untrash, trash, restore/delete/recreate-term (term recreate preserves the ORIGINAL id + term_taxonomy_id so references stay valid), restore/delete/recreate-feed, restore-redirect (provider-aware), restore-translation-link, restore-string, and bulk. Restores use low-level APIs so they don't recurse into the destructive-write guards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every reversible write now records how to undo it: content + terms + forms + feeds (create/update/delete/duplicate) + redirects + nav menu items + media + bulk-update + duplicate + blocks-patch + revisions-restore + Polylang create/link/string/machine-translate. Notable: nav-menu-items-delete switched from hard delete to trash (so undo is a simple untrash and child->parent links survive); bulk-update captures only touched status/meta (merge restore) with a size guard; machine-translate branches its snapshot on whether it creates or overwrites a translation; mail-send and cache-clear are intentionally left non-undoable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Member
Author
|
Companion (assistant side: stores snapshots, adds assistant__undo): generoi/gds-assistant#26 |
Term undo captured/replayed with the REST base (e.g. "categories") instead
of the real taxonomy slug ("category"), so termForRecreate silently returned
[] and no _undo was attached; and wp_set_object_terms skipped the freshly
raw-inserted term id, so posts were not reattached. Resolve the REST base to
the slug for all term snapshots, and reattach objects by inserting
term_relationships rows directly. Verified end-to-end on a live site.
Adds UndoRoundTripTest driving the real abilities through their REST-base
inputs (not the restore handlers directly), so this class of bug is caught
at the integration layer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Adds a comprehensive, uniform undo for AI-assistant-driven writes. Builds on the failsafe guards (#18) and follows the per-ability reversibility audit. Companion to the gds-assistant undo PR.
A mutating ability captures a "before" snapshot via the
Reversibletrait +Snapshothelpers and attaches it to its result under_undo. gds-assistant peels that off (never shown to the LLM), stores it on the audit log, and replays it through thegds-mcp/restore_snapshotfilter —RestoreSnapshotdispatches by kind. Restores use low-level APIs so they don't recurse into the destructive-write guards.Reversibility by ability (per the audit)
add_feed(new feed id — noted as a caveat).mail-send(email is irreversible) andcache-clear(transient). Reads have no undo.Caveats surfaced to the user
When a restore can't be perfectly faithful (e.g. a term id had to change because the old one was reused, or DeepL credits were spent), the restore returns
caveatsthat the assistant relays.Tests
RestoreSnapshotTestround-trips the core kinds against real WP, including the term-id-preservation guarantee and the partial (merge) restore.🤖 Generated with Claude Code