| name | workspace-state-refactor |
| description | Refactor or extend workspace state/store logic while preserving behavior and keeping responsibilities clean. |
workspace-state-refactor skill
Use this skill for any work touching src/app/store/workspace.store*.ts, document lifecycle, autosave, evaluation projection, or the store-to-UI binding layer.
Companion files — read these too
.codex/skills/workspace-state-refactor/state-patterns.md — concrete Zustand patterns, responsibility map, anti-patterns
Current store architecture
The workspace store is split across four files — understand each before editing:
| File | Responsibility |
|---|
src/app/store/workspace.store.types.ts | WorkspaceState, WorkspaceStateData, WorkspaceStateActions — the full typed interface |
src/app/store/workspace.store.state.ts | Pure state shape helpers — initial state, state selectors, derived values |
src/app/store/workspace.store.runtime.ts | Autosave timer, debounce orchestration, save-revision coordination |
src/app/store/workspace.store.ts | Main Zustand create() — wires all actions, delegates to runtime and state helpers |
Responsibilities map
workspace.store.ts
├── initialize() → loads documents from Tauri, sets activeDocumentId
├── selectDocument() → loads document content, runs engine projection
├── setRawText() → updates rawText, triggers engine re-projection, schedules autosave
├── createDocument() → Tauri create → reload → select new
├── deleteDocument() → Tauri delete → reload → select next
├── archiveActiveDocument() → Tauri archive → reload
├── saveNow() → immediate Tauri save (bypasses debounce)
└── [autosave timer] → in workspace.store.runtime.ts
Evaluation projection
src/features/editor/workspace-projection.ts owns the engine-to-editor projection:
- Input:
rawText: string
- Output:
{ lines: CalculationLine[], tables: TableBlockAnalysis[] }
- Called by
setRawText() and selectDocument() in the store
- Never call
analyzeDocument() directly from components — always go through the store
Settings store (separate from workspace store)
src/features/settings/settings.store.ts owns:
themePreference: ThemePreference
aiEnabled: boolean
locale: string
defaultCurrency: string
Do not mix settings state into the workspace store.
Required workflow
- Read
state-patterns.md for Zustand patterns specific to this repo.
- Inspect
workspace.store.types.ts to understand the full state interface.
- Inspect actual store usage in
WorkspaceScreen.tsx, WorkspaceEditorPane.tsx, WorkspaceInspectorPane.tsx.
- Keep changes incremental — one responsibility at a time.
- After each slice:
npm run typecheck && npm run build.
- Update
docs/ARCHITECTURE.md if the responsibility split changes.
Constraints
- Do not put derived data in Zustand state — compute at render time or in selectors.
- Do not put evaluation results (lines, tables) in a separate store — they belong with rawText in workspace.
- Autosave timer logic lives in
workspace.store.runtime.ts, not in the main store.
- Keep
WorkspaceStateData and WorkspaceStateActions interfaces in workspace.store.types.ts.
- Do not add unit tests unless explicitly requested.
- Tauri commands are called from store actions, not from UI components directly.
Docs to update
docs/ARCHITECTURE.md — for any responsibility split or boundary changes
docs/API_CONTRACTS.md — if store action signatures change
docs/STATUS_GAP_ANALYSIS.md — when store-related gaps are resolved