feat(audit): undo / redo / restore-to-point (v0.6)#146
Merged
Conversation
Phase 3 of the audit-log feature (#19): step backward and forward through logged writes from the GUI. Schema (audit.rs): - New `Kind::Restore` variant and a `restore` field on `Record` carrying `RestoreMeta` { target_id, direction, files } — the per-file before/after snapshots a later undo needs to invert the restore itself. - Dropped the never-emitted `Actor::Restore`; `Kind::Restore` now carries that meaning, and `actor` stays orthogonal (gui/cli/skill). Undo/redo state machine: - `undo_redo_state()` replays the append-only log into the next undoable op, the next redoable op, and a sequence-break flag (a write after an undo strands the redo stack — redo is withheld, not discarded). Snapshot-restore (commands.rs): - Undo writes each affected file's top-level key back to the logged `key_before` snapshot rather than synthesizing a reverse move/add/delete request. This is faithful by construction and sidesteps a faithfulness bug in the invert-to-request approach #124 originally proposed (undoing a move into a scope that already shared the rule would wrongly drop that scope's copy). Deviation noted on the issue. - `RestorePlan` / `build_restore_plan` / `apply_restore_plan` (atomic N-file write with full rollback) / `preview_restore_plan` with state-mismatch detection against the log's expected value. Tauri commands: `audit_undo_status`, `audit_undo_preview`, `audit_redo_preview`, `audit_apply_undo`, `audit_apply_redo`. Apply verifies the target id still matches the log so a concurrent write can't redirect a confirmed restore. Frontend: - Undo / Redo topbar buttons with descriptive tooltips; Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z, skipped in inputs and modals like the `/` shortcut. - Restore-confirm modal reusing the shared modal shell, with a warning band when a file was hand-edited since the logged op. - History view renders `restore` entries (verb, the entry acted on, and the affected scopes from `restore.files`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 4 of the audit-log feature (#19): roll the affected files back to their state before a chosen History entry, reverting that entry and everything logged after it. Backend (commands.rs): - `plan_restore_to(records, target_id)` walks the log forward from the target to HEAD, aggregating a per-file delta — the first `key_before` seen for a file (its window-start state) is the restore value, the last `key_after` is the expected-current value for state-mismatch detection. `restore` entries inside the window are walked like any other write. Rejects an unknown target and a target that is itself a restore entry. - Reuses the Phase 3 `RestorePlan` / `apply_restore_plan` (atomic N-file write with full rollback) and `preview_restore_plan`; `RestorePlan` gains `ops_spanned` so the modal can say "Restoring to N ops back". - Commands `audit_restore_to_point_preview` and `audit_apply_restore_to_point`; apply verifies `expected_ops_spanned` so a concurrent write can't widen the reverted span. Frontend: - Each original-write History row gets a "Restore" button (rejected on `restore` rows, which the backend won't target anyway). The History dialog closes itself — via a new `openModal` `onReady` hook exposing `close` — before the restore-confirm modal opens. - The shared restore-confirm modal already handles the `to_point` direction: "Restoring to N ops back" line, "Restore" confirm button. - Undo / redo / restore-to-point now share one `runRestoreFlow` driver. Tests: restore-to-point plan + apply end-to-end, last-entry case, unknown/restore-entry rejection, restore-of-a-restore, and a Unix mid-batch rollback test (locked directory). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 5 of the audit-log feature (#19): bring undo / redo / restore-to-point to claude-scope-cli, wrapping the same audit helpers the GUI commands use — no new domain logic. - `claude-scope-cli undo` / `redo` — act on the most recent undoable / redoable entry from `audit::undo_redo_state`. `redo` exits non-zero after a sequence break, matching the GUI's greyed-out button. - `claude-scope-cli restore <id>` — restore-to-point; `<id>` is a full ULID or any unique prefix (`resolve_entry_id` reports an ambiguous prefix with the candidate list and exits non-zero). - Shared `--dry-run` (print the planned restore, write nothing), `--yes` (skip the `Apply? [y/N]` prompt), and `--json` flags. `--json` emits the `RestorePreview` for a dry run, otherwise the new `restore` entry as an `AuditRecordView` — the same wire shape `history --json` produces. - CLI restores log a `restore` entry with `actor: cli`, so a GUI session sees CLI-driven undos in its History. Log-append failure is fail-open: a warning, not a command failure (the restore already hit disk). - `history --kind restore` filtering: `AuditKindArg` gains `Restore`. Tests: undo reverts a logged move and logs a cli-actor restore entry, dry-run writes nothing, empty-log and sequence-break error paths, and full-ULID / case-insensitive / ambiguous-prefix id resolution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.6 audit-log work shipped — sync the docs: - README: a feature bullet for the audit log + undo/redo/restore, and the Ctrl/Cmd+Z / Ctrl/Cmd+Shift+Z shortcuts in the keyboard table. - docs/user-guide/undo-redo.md: phase table marked shipped; new "Undo and redo" and "Restore to before an entry" sections (sequence break, external-edit warning); CLI section covers undo/redo/restore. - docs/reference/cli.md: real `undo` / `redo` / `restore` subcommand docs replace the "Pending" stub; `history --kind restore`. - docs/architecture/audit-log.md: record-shape comments corrected (no `restore` actor; `actor` is who-triggered), new "Restore entries" section covering RestoreMeta, the replay-derived cursor, and why snapshot-restore is used over invert-to-request. - docs/SMOKE_TEST.md: a non-sandbox History / undo / redo / restore checklist (audit log isn't written under --home). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Implements the v0.6 — Audit log undo/redo/restore milestone (#19 Phases 3-5) on one branch. All three feature issues are done.
Closes #124. Closes #125. Closes #126.
Design note — snapshot-restore
#124 specified inverting an op into a reverse move/add/delete request and reusing
apply_*. That isn't faithful when a move's destination already shared the rule (the reverse move would wrongly strip the destination's own copy). This branch uses snapshot-restore: a restore writes each affected file's top-level key back to akey_beforesnapshot the log already captured — faithful by construction, uniform across rules/lists/keys, and Phase 4 falls out as the same primitive over an aggregated delta. Full rationale in a comment on #124.Phase 3 — undo / redo (#124)
Kind::Restore+RestoreMeta(target id, direction, per-file snapshots) on the audit record;undo_redo_state()replays the append-only log into undoable / redoable + sequence-break detection.RestorePlanwith atomic N-file write + full rollback; state-mismatch detection against the log's expected value.audit_undo_status/audit_undo_preview/audit_redo_preview/audit_apply_undo/audit_apply_redo(apply verifies the target id so a concurrent write can't redirect a confirmed restore).Ctrl/Cmd+Z/Ctrl/Cmd+Shift+Z, restore-confirm modal with the external-edit warning band, History rendering forrestoreentries.Phase 4 — restore-to-point (#125)
plan_restore_to()walks the log forward from the target to HEAD, aggregating a per-file delta; rejects an unknown target or a restore-entry target.audit_restore_to_point_preview/audit_apply_restore_to_point(apply verifiesexpected_ops_spanned).openModalonReadyhook) before the confirm modal opens.Phase 5 — CLI (#126)
claude-scope-cli undo / redo / restore <id>wrapping the same helpers.--dry-run/--yes/--json; ULID-prefix resolution forrestore;redoexits non-zero after a sequence break. CLI restores logactor: cliso a GUI session sees them.Docs
README, mdbook (
undo-redo/cli/audit-log), andSMOKE_TEST.mdsynced.Tests
279 Rust tests (238 lib + 26 cli + 15 bin) + 120 frontend tests pass; clippy clean,
tscclean, biome clean. Verified end-to-end via the CLI against a crafted audit log (undo dry-run/apply, restore-entry logging). Undo/redo aren't exercisable in sandbox mode (the audit log is deliberately not written under--home), so a GUI run-through against a real~/.claudeis left for manual verification before merge — see the new non-sandbox section inSMOKE_TEST.md.🤖 Generated with Claude Code