docs: ADR-0006 §8 step 8 — mark undo/snapshot implemented

- 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
This commit is contained in:
claude@clouddev1
2026-05-24 20:58:31 +00:00
parent cf53023a71
commit 5442cfc0b9
4 changed files with 54 additions and 15 deletions
+38 -2
View File
@@ -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 17 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 `<project>/.snapshots/<id>/` 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.