feat: ADR-0006 §8 steps 4-5 — undo/redo commands + confirm-modal flow

Commands & grammar (step 4):
- AppCommand::Undo/Redo, grammar nodes + REGISTRY entries, catalog
  help/usage + keys; parse tests
- replay skips undo/redo (is_app_lifecycle_entry_word) + completion
  entry-keyword lockstep; replay-skip test extended

Wiring (step 5):
- Action::{PrepareUndo,PrepareRedo,Undo,Redo} + AppEvent::{UndoPrepared,
  UndoUnavailable,UndoSucceeded,UndoFailed}
- App: undo_enabled flag, Modal::UndoConfirm, dispatch + event handling
  + confirm-key handler (Y confirms / N/Esc cancels); "turned off" when
  --no-undo; "nothing to undo/redo" when empty
- ui::render_undo_confirm names the command + snapshot time
- runtime: opens with undo enabled (!--no-undo), threads it through the
  project-switch path, spawn_prepare_undo/spawn_undo (peek->modal,
  restore->refresh tables + schema cache)
- 9 Tier-1 app tests + 3 parse tests

1692 passed / 0 failed / 1 ignored; clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-24 20:48:30 +00:00
parent a97069c02e
commit 25800e3eb5
14 changed files with 541 additions and 9 deletions
+27
View File
@@ -115,6 +115,33 @@ pub enum AppEvent {
RebuildFailed {
error: String,
},
/// Runtime peeked the snapshot `undo`/`redo` would restore and
/// is ready for the user to confirm (ADR-0006 Amendment 1). App
/// opens the confirmation modal naming `command`. `is_redo`
/// selects undo vs redo wording.
UndoPrepared {
command: String,
timestamp: String,
is_redo: bool,
},
/// Nothing to undo / redo (the ring or redo stack was empty).
/// App surfaces a friendly note.
UndoUnavailable {
is_redo: bool,
},
/// Undo / redo completed. App closes the modal, notes the
/// command that was undone/redone; the runtime also refreshes
/// the table list + schema cache.
UndoSucceeded {
command: String,
is_redo: bool,
},
/// Undo / redo failed (a rare restore error). App closes the
/// modal and surfaces the error.
UndoFailed {
error: String,
is_redo: bool,
},
/// Runtime has gathered the list of available projects
/// for the load picker. App opens the picker modal.
LoadPickerReady {