From 5442cfc0b9208d811fe20000ba8c1c22823571e3 Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Sun, 24 May 2026 20:58:31 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20ADR-0006=20=C2=A78=20step=208=20?= =?UTF-8?q?=E2=80=94=20mark=20undo/snapshot=20implemented?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - requirements.md U1/U2: [ ] -> [x] with implementation notes - CLAUDE.md Safety bullet: implemented; drop U-series from the deliberately-deferred list - ADR-0006 Amendment 1: "not yet implemented" -> implemented, plus an Implementation note (index.yaml, source-gated snapshots, non-fatal snapshot-failure policy, batch primitive, testing + accepted gaps) - ADR README index: undo/snapshot half marked implemented --- CLAUDE.md | 8 ++-- .../adr/0006-undo-snapshots-and-replay-log.md | 40 ++++++++++++++++++- docs/adr/README.md | 2 +- docs/requirements.md | 19 +++++---- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 418972b..92f6cce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,9 +63,9 @@ Current decisions at a glance (each backed by an ADR): (U1/U2): `undo` / `redo` app commands (no sigil) with auto-snapshot before **every** mutation into a persisted N=50 ring; hybrid whole-project snapshot (db backup API + yaml/csv copy); `--no-undo` - to disable (ADR-0006 **Amendment 1**). *(Designed + amended + - planned in `docs/plans/20260524-adr-0006-undo-snapshots.md`; not - yet implemented.)* + to disable (ADR-0006 **Amendment 1**). *(Implemented 2026-05-24 — + `src/undo.rs` ring + worker hook in `src/db.rs`; one undo step per + user command, batch ops collapse to one, `import` excluded.)* - **Sharing:** `export` command produces a zip without the `.db`; no hosted publishing (ADR-0007). - **Testing:** four-tier strategy from `cargo test` units up to @@ -203,8 +203,6 @@ not yet implemented: - **Strong syntax-help in parse errors** (H1a): point users at missing keywords/clauses rather than the unexpected character. -- **Snapshot/replay/undo** (U-series): designed in ADR-0006; - not yet implemented. - **Tutorial/lesson system**: acknowledged as in scope for design; needs its own ADR. - **Session log + Markdown export** (V4): the bigger UX diff --git a/docs/adr/0006-undo-snapshots-and-replay-log.md b/docs/adr/0006-undo-snapshots-and-replay-log.md index fe6776b..ef00724 100644 --- a/docs/adr/0006-undo-snapshots-and-replay-log.md +++ b/docs/adr/0006-undo-snapshots-and-replay-log.md @@ -96,8 +96,8 @@ implementation, and **supersedes the original Decision's "snapshots only before destructive operations" model** and its confirmation rationale. Written with explicit user approval; the implementation plan is `docs/plans/20260524-adr-0006-undo-snapshots.md`. -**Not yet implemented** at the time of writing — this records the -agreed design. +**Implemented 2026-05-24** (see the Implementation note at the end of +this amendment). ### Snapshot scope — every mutation (single-step undo) @@ -207,3 +207,39 @@ is turned off. CLI-only for v1 (no in-app toggle). - The Phase-3 N/A matrix row ("auto-snapshot fires for SQL DML the same as DSL") becomes non-vacuous: the snapshot hook lives in the worker dispatch and covers DSL and SQL mutations uniformly. + +### Implementation note (2026-05-24) + +Shipped across §8 steps 1–7 of the plan. Details and decisions made +during implementation, user-confirmed where they extended the design: + +- **Ring storage** is `src/undo.rs` (`SnapshotStore`): per-snapshot + payload dirs under `/.snapshots//` plus an + **`index.yaml`** manifest (YAML reuses the existing `serde_yml` + dependency — no `serde_json` added). Monotonic ids, reconciled + against on-disk dirs so a crash can't reuse one; `cleanup()` on + open sweeps `.staging/` and orphan payloads. +- **Worker hook**: `snapshot_then` brackets all 19 mutating dispatch + arms in `src/db.rs` (stage → run → finalise/discard); restore + (undo/redo) runs in `worker_loop` with `&mut Connection`. Snapshots + are **gated on a user command `source`** — internal operations that + pass `source = None` (notably the open-time rebuild when `.db` is + missing) are not recorded, so `rebuild` is undoable as a user + command but opening a project never creates a spurious entry. +- **Snapshot-failure policy** (user-confirmed): staging / finalise / + discard failures are **non-fatal** (logged) — the real persistence + is the durable state and a backup hiccup must not fail the user's + work. Only *restore* failures surface (as `UndoFailed`). +- **Batch** uses `BeginBatch`/`EndBatch` worker requests; `replay` + wraps its loop so a multi-command replay is one undo step, + finalised only if a mutation committed. +- **Testing**: `src/undo.rs` Tier-1 (ring/redo/eviction/restore), + `tests/undo_snapshots.rs` Tier-3 (worker, DSL+SQL, db+csv + consistency, persistence across reopen), plus App-level Tier-1 + (dispatch/modal) and parse/replay-filter tests. The thin runtime + glue (`spawn_prepare_undo` / `spawn_undo` + Action arms) is not + loop-tested — the same accepted gap recorded for ADR-0034, with the + App side and worker side each tested. No Tier-2 insta render test + was added for the confirmation modal: the existing modals + (`rebuild` / path / load) are tested at the state level only, and + the undo modal matches that. diff --git a/docs/adr/README.md b/docs/adr/README.md index 89e9cfb..c808b4f 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -11,7 +11,7 @@ This directory contains the project's ADRs, recorded per - [ADR-0003 — Input modes and command dispatch](0003-input-modes-and-command-dispatch.md) - [ADR-0004 — Project file format](0004-project-file-format.md) - [ADR-0005 — Column type vocabulary](0005-column-type-vocabulary.md) -- [ADR-0006 — Undo snapshots and replay log](0006-undo-snapshots-and-replay-log.md) — **Accepted**. The **replay/journal half** (U3/U4) shipped via ADR-0034; the **undo/snapshot half** (U1/U2) is settled by **Amendment 1 (2026-05-24)** but **not yet implemented** (plan: `docs/plans/20260524-adr-0006-undo-snapshots.md`). Amendment 1 **supersedes the original "snapshots only before destructive operations" model**: a snapshot is taken before **every** data/schema mutation (DSL + SQL) for familiar single-step (Ctrl-Z) undo — so the confirmation collapses to *naming the one command being undone* (no db-diff). Snapshot is a **hybrid whole-project copy** — database via the online backup API **plus** `project.yaml`/`data/*.csv` as files — reconciling this ADR with ADR-0015's "text is authoritative, db is derived"; undo restores all three directly. Staged before the mutation's transaction, finalised after the db commit (preserves ADR-0015 §6 commit-db-last); rolled-back ops leave no snapshot. **Persisted** ring under `.snapshots/`, **N = 50** (raised from 10), git-ignored + export-excluded + temp-cleanup-aware. `redo` supported, **redo stack discarded on new work**. **Batch ops record one undo step** (`replay` + future batch via a Begin/EndBatch worker primitive); **`import` is outside undo** (it switches projects per ADR-0015 §11, leaving the current project untouched). A **`--no-undo` CLI flag** disables snapshotting (hardware escape hatch). Adds the `backup` feature to `rusqlite` +- [ADR-0006 — Undo snapshots and replay log](0006-undo-snapshots-and-replay-log.md) — **Accepted**. The **replay/journal half** (U3/U4) shipped via ADR-0034; the **undo/snapshot half** (U1/U2) is settled by **Amendment 1 (2026-05-24)** and **implemented 2026-05-24** (plan: `docs/plans/20260524-adr-0006-undo-snapshots.md`; ring in `src/undo.rs`, worker hook in `src/db.rs`). Amendment 1 **supersedes the original "snapshots only before destructive operations" model**: a snapshot is taken before **every** data/schema mutation (DSL + SQL) for familiar single-step (Ctrl-Z) undo — so the confirmation collapses to *naming the one command being undone* (no db-diff). Snapshot is a **hybrid whole-project copy** — database via the online backup API **plus** `project.yaml`/`data/*.csv` as files — reconciling this ADR with ADR-0015's "text is authoritative, db is derived"; undo restores all three directly. Staged before the mutation's transaction, finalised after the db commit (preserves ADR-0015 §6 commit-db-last); rolled-back ops leave no snapshot. **Persisted** ring under `.snapshots/`, **N = 50** (raised from 10), git-ignored + export-excluded + temp-cleanup-aware. `redo` supported, **redo stack discarded on new work**. **Batch ops record one undo step** (`replay` + future batch via a Begin/EndBatch worker primitive); **`import` is outside undo** (it switches projects per ADR-0015 §11, leaving the current project untouched). A **`--no-undo` CLI flag** disables snapshotting (hardware escape hatch). Adds the `backup` feature to `rusqlite` - [ADR-0007 — Sharing and export](0007-sharing-and-export.md) - [ADR-0008 — Testing approach](0008-testing-approach.md) - [ADR-0009 — DSL command syntax conventions](0009-dsl-command-syntax-conventions.md) diff --git a/docs/requirements.md b/docs/requirements.md index f4e75b6..9610084 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -365,7 +365,7 @@ handoff-14 cleanup; 449 after B2/C2.) ## Undo and replay (per ADR-0006) -- [ ] **U1** Auto-snapshot before **every** data/schema mutation +- [x] **U1** Auto-snapshot before **every** data/schema mutation (DSL + SQL) into a persisted ring buffer (size N=50, tunable), per ADR-0006 Amendment 1 (single-step undo, superseding the original destructive-only model). Snapshot is a hybrid @@ -374,14 +374,19 @@ handoff-14 cleanup; 449 after B2/C2.) finalised after the db commit (preserves ADR-0015 §6). A batch command (`replay` / future batch ops) records one boundary snapshot; `import` takes none. A `--no-undo` CLI flag disables - snapshotting. *(Designed + amended + planned in - `docs/plans/20260524-adr-0006-undo-snapshots.md`; not yet - implemented.)* -- [ ] **U2** `undo` restores the most recent snapshot (database + + snapshotting. *(Implemented 2026-05-24, plan + `docs/plans/20260524-adr-0006-undo-snapshots.md`: `src/undo.rs` + ring + worker dispatch hook in `src/db.rs`; snapshots gated on a + user command `source` so internal ops like open-time rebuild are + not recorded; snapshot-bookkeeping failures are non-fatal, restore + failures surface.)* +- [x] **U2** `undo` restores the most recent snapshot (database + text, directly); `redo` re-applies (redo stack discarded on new work); both prompt for confirmation naming the command being - undone / re-applied (`Y` confirms). *(Designed + amended; not yet - implemented — see U1's plan.)* + undone / re-applied (`Y` confirms). *(Implemented 2026-05-24: + `undo` / `redo` app commands, `Modal::UndoConfirm`, runtime + prepare→confirm→restore→refresh; `--no-undo` reports undo is off, + empty stacks report "nothing to undo/redo".)* - [x] **U3** `history.log` records every submitted command in append-only form, tagged with its outcome (Iteration 2; broadened by ADR-0034). Format: `||`