# Implementation Plan: Undo/Redo **Branch**: `037-undo-redo` | **Date**: 2026-03-26 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `/specs/037-undo-redo/spec.md` ## Summary Add undo/redo capability for all encounter state changes using the memento pattern. Before each state transition, the current `Encounter` is pushed to an undo stack (capped at 50 entries). Undo restores the previous snapshot and pushes the current state to a redo stack; any new action clears the redo stack. Stacks are persisted to localStorage. Triggered via UI buttons (disabled when empty) and keyboard shortcuts (Ctrl+Z / Ctrl+Shift+Z, Cmd on Mac), suppressed during text input focus. ## Technical Context **Language/Version**: TypeScript 5.8 (strict mode, `verbatimModuleSyntax`) **Primary Dependencies**: React 19, Vite 6, Tailwind CSS v4, Lucide React (icons) **Storage**: localStorage (existing key `"initiative:encounter"`, new keys for undo/redo stacks) **Testing**: Vitest (v8 coverage) **Target Platform**: Web browser (desktop + mobile) **Project Type**: Web application (monorepo: `apps/web` + `packages/domain` + `packages/application`) **Performance Goals**: Undo/redo operations complete in < 1 second (per SC-001) **Constraints**: Undo stack capped at 50 snapshots; localStorage quota is best-effort **Scale/Scope**: Encounters have tens of combatants; 50 snapshots of ~2-5 KB each = ~100-250 KB ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Notes | |-----------|--------|-------| | I. Deterministic Domain Core | PASS | Stack management (push, pop, cap) is pure logic with no I/O. Domain functions remain unchanged. | | II. Layered Architecture | PASS | Stack operations are pure functions in domain. Persistence is in the adapter layer (localStorage). Hook orchestrates via application pattern. | | II-A. Context-Based State Flow | PASS | Undo/redo state exposed via existing EncounterContext. No new props needed on components beyond the context consumer. | | III. Clarification-First | PASS | No ambiguities remain; issue #16 and spec fully define behavior. | | IV. Escalation Gates | PASS | All requirements come from the spec; no scope expansion. | | V. MVP Baseline Language | PASS | No permanent bans introduced. | | VI. No Gameplay Rules | PASS | Undo/redo is infrastructure, not gameplay. | **Result**: All gates pass. No violations to justify. ## Project Structure ### Documentation (this feature) ```text specs/037-undo-redo/ ├── plan.md # This file ├── research.md # Phase 0 output ├── data-model.md # Phase 1 output ├── quickstart.md # Phase 1 output └── tasks.md # Phase 2 output (/speckit.tasks) ``` ### Source Code (repository root) ```text packages/domain/src/ ├── undo-redo.ts # Pure stack operations (push, pop, cap, clear) ├── __tests__/undo-redo.test.ts # Unit tests for stack operations packages/application/src/ ├── undo-use-case.ts # Orchestrates undo via EncounterStore + UndoRedoStore ├── redo-use-case.ts # Orchestrates redo via EncounterStore + UndoRedoStore ├── ports.ts # Extended with UndoRedoStore port interface apps/web/src/ ├── hooks/use-encounter.ts # Modified: wrap actions with snapshot capture, expose undo/redo ├── persistence/undo-redo-storage.ts # localStorage save/load for undo/redo stacks ├── contexts/encounter-context.tsx # Modified: expose undo/redo + stack emptiness flags ├── components/turn-navigation.tsx # Modified: add undo/redo buttons (inboard of turn step buttons) ├── hooks/use-undo-redo-shortcuts.ts # Keyboard shortcut handler (Ctrl+Z, Ctrl+Shift+Z) ``` **Structure Decision**: Follows existing layered architecture. Pure stack operations in domain, use cases in application, persistence and UI in web adapter. No new packages or structural changes needed.