From a0d7f51145e218d873b6d2feb4ba988c0e6f270a Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 13 Jan 2026 07:14:37 +1300 Subject: [PATCH] docs(16): create phase plan for store modularization Phase 16: Store Modularization - 3 plans created - 9 total tasks defined - Ready for execution Plans: - 16-01: Extract localStorage helpers and node defaults - 16-02: Extract execution logic into dedicated modules - 16-03: Create Zustand slices and compose store --- .../16-store-modularization/16-01-PLAN.md | 139 ++++++++++++ .../16-store-modularization/16-02-PLAN.md | 168 ++++++++++++++ .../16-store-modularization/16-03-PLAN.md | 212 ++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 .planning/phases/16-store-modularization/16-01-PLAN.md create mode 100644 .planning/phases/16-store-modularization/16-02-PLAN.md create mode 100644 .planning/phases/16-store-modularization/16-03-PLAN.md diff --git a/.planning/phases/16-store-modularization/16-01-PLAN.md b/.planning/phases/16-store-modularization/16-01-PLAN.md new file mode 100644 index 00000000..735a3e62 --- /dev/null +++ b/.planning/phases/16-store-modularization/16-01-PLAN.md @@ -0,0 +1,139 @@ +--- +phase: 16-store-modularization +plan: 01 +type: execute +--- + + +Extract localStorage helpers and configuration utilities from workflowStore.ts into dedicated modules. + +Purpose: Begin store modularization by extracting pure utility functions that don't depend on Zustand state, making them easier to test and reducing the main store file size. +Output: New utility modules for localStorage operations and default node data creation, with workflowStore.ts importing from these modules. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/15-test-infrastructure/15-01-SUMMARY.md +@src/store/workflowStore.ts + +**From Phase 15 Summary:** +- Vitest and React Testing Library configured +- Zustand store mocking patterns established +- Test infrastructure ready for validating refactored modules + +**Current Store Analysis (2786 lines):** +The workflowStore.ts contains several pure utility functions at the top that don't depend on Zustand state: +- localStorage helpers for workflow configs (~40 lines) +- localStorage helpers for cost tracking (~40 lines) +- localStorage helpers for GenerateImage defaults (~40 lines) +- localStorage helpers for provider settings (~50 lines) +- createDefaultNodeData function (~90 lines) +- GROUP_COLORS and GROUP_COLOR_ORDER constants (~15 lines) +- generateWorkflowId utility (~3 lines) + + + + + + Task 1: Create store utilities module + src/store/utils/localStorage.ts + +Extract all localStorage helpers into a new module: +1. Create src/store/utils/localStorage.ts +2. Move these functions/constants: + - STORAGE_KEY constant + - COST_DATA_STORAGE_KEY constant + - GENERATE_IMAGE_DEFAULTS_KEY constant + - PROVIDER_SETTINGS_KEY constant + - loadSaveConfigs() + - saveSaveConfig() + - loadWorkflowCostData() + - saveWorkflowCostData() + - loadGenerateImageDefaults() (rename from loadNanoBananaDefaults) + - saveGenerateImageDefaults() (existing export, keep as-is) + - getProviderSettings() + - saveProviderSettings() + - defaultProviderSettings constant + - GenerateImageDefaults interface +3. Export all functions and types +4. Keep backward-compatible aliases (NanoBananaDefaults, saveNanoBananaDefaults) in workflowStore.ts but have them re-export from the new module + +Note: These functions use localStorage directly and don't depend on Zustand state, making them safe to extract. + + npm run build succeeds with no TypeScript errors + All localStorage helpers extracted, workflowStore.ts imports from new module, build passes + + + + Task 2: Create node defaults module + src/store/utils/nodeDefaults.ts + +Extract node creation utilities: +1. Create src/store/utils/nodeDefaults.ts +2. Move these items: + - createDefaultNodeData() function (~90 lines) + - defaultDimensions record (duplicated in addNode and createGroup - consolidate) + - GROUP_COLORS record + - GROUP_COLOR_ORDER array + - GroupColor type import from @/types +3. The createDefaultNodeData function calls loadGenerateImageDefaults() - update import to use new localStorage module +4. Export createDefaultNodeData, defaultNodeDimensions, GROUP_COLORS, GROUP_COLOR_ORDER +5. Update workflowStore.ts to import these from the new module + + npm run build succeeds, npm test passes all 112+ tests + Node defaults extracted, GROUP_COLORS exported from new location, all imports updated, tests pass + + + + Task 3: Add unit tests for extracted modules + src/store/utils/__tests__/localStorage.test.ts, src/store/utils/__tests__/nodeDefaults.test.ts + +Create tests for the extracted modules: +1. Create src/store/utils/__tests__/localStorage.test.ts: + - Test loadSaveConfigs returns empty object when localStorage empty + - Test saveSaveConfig stores and retrieves config + - Test loadWorkflowCostData handles missing data + - Test loadGenerateImageDefaults returns defaults when localStorage empty + - Test getProviderSettings merges with defaults for new providers + - Mock localStorage using vi.stubGlobal or in-memory implementation + +2. Create src/store/utils/__tests__/nodeDefaults.test.ts: + - Test createDefaultNodeData for each node type returns correct structure + - Test defaultNodeDimensions has all expected node types + - Test GROUP_COLORS has expected color keys + - Mock localStorage for createDefaultNodeData's loadGenerateImageDefaults call + + npm test passes with new tests included, coverage for new modules + Unit tests added for both modules, all tests pass including new ones + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npm test` passes all tests (existing + new) +- [ ] workflowStore.ts is reduced by ~280 lines +- [ ] New modules are properly typed with exports +- [ ] Backward compatibility maintained (saveNanoBananaDefaults still works) + + + + +- All tasks completed +- All verification checks pass +- No TypeScript errors +- workflowStore.ts imports from new utility modules +- New modules have test coverage + + + +After completion, create `.planning/phases/16-store-modularization/16-01-SUMMARY.md` + diff --git a/.planning/phases/16-store-modularization/16-02-PLAN.md b/.planning/phases/16-store-modularization/16-02-PLAN.md new file mode 100644 index 00000000..707a6482 --- /dev/null +++ b/.planning/phases/16-store-modularization/16-02-PLAN.md @@ -0,0 +1,168 @@ +--- +phase: 16-store-modularization +plan: 02 +type: execute +--- + + +Extract workflow execution logic from workflowStore.ts into a dedicated execution module. + +Purpose: The execution logic (~800 lines) is the largest section and most complex. Extracting it enables focused testing and cleaner separation of concerns between state management and execution orchestration. +Output: New execution module with executeWorkflow, regenerateNode, and supporting execution utilities. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/16-store-modularization/16-01-PLAN.md +@src/store/workflowStore.ts + +**Dependencies from 16-01:** +- localStorage utilities extracted +- nodeDefaults module exists + +**Execution Logic Analysis (~800 lines):** +The executeWorkflow and regenerateNode functions are tightly coupled to store state but can be extracted as action creators that receive state getters/setters. Key components: +- Topological sort algorithm (~30 lines) +- Node execution switch statement (~600 lines for all node types) +- API call patterns (repeated for each provider) +- Logger calls throughout +- State updates via updateNodeData + + + + + + Task 1: Extract execution utilities + src/store/execution/utils.ts + +Create src/store/execution/utils.ts with reusable execution utilities: + +1. Extract topologicalSort function: + - Takes nodes and edges as input + - Returns sorted WorkflowNode array + - Throws on cycle detection + +2. Extract buildProviderHeaders function: + - Takes provider type and providerSettings + - Returns headers object with API key headers + - Consolidates the repeated header building logic from executeWorkflow/regenerateNode + +3. Extract getSourceOutput helper: + - Takes a WorkflowNode + - Returns { type: "image" | "text" | "video", value: string | null } + - Currently duplicated in getConnectedInputs + +4. Export types: + - ExecutionResult = { success: boolean; error?: string; data?: unknown } + - NodeExecutionContext = { nodeId: string; get: () => StoreState; set: (partial: Partial) => void } + +These are pure functions that don't directly depend on Zustand, making them testable. + + npm run build succeeds, new module compiles without errors + Execution utilities extracted, types defined, build passes + + + + Task 2: Extract node executors + src/store/execution/nodeExecutors.ts + +Create src/store/execution/nodeExecutors.ts with individual node type executors: + +1. Create executor type: + ```typescript + type NodeExecutor = ( + node: WorkflowNode, + context: { + getConnectedInputs: (nodeId: string) => ConnectedInputs; + updateNodeData: (nodeId: string, data: Partial) => void; + get: () => WorkflowStoreState; + set: (partial: Partial) => void; + providerSettings: ProviderSettings; + } + ) => Promise<{ continue: boolean; error?: string }>; + ``` + +2. Extract executors for each node type: + - executeImageInput (no-op, returns continue: true) + - executeAnnotation + - executePrompt (no-op) + - executeNanoBanana (image generation) + - executeGenerateVideo (video generation) + - executeLlmGenerate (text generation) + - executeSplitGrid + - executeOutput + +3. Create nodeExecutorMap: + ```typescript + const nodeExecutorMap: Record = { ... } + ``` + +4. Export executeNode function that dispatches to the correct executor + +This keeps the individual node logic but allows testing each executor in isolation. + + npm run build succeeds, no TypeScript errors + Node executors extracted as individual functions with shared interface + + + + Task 3: Refactor executeWorkflow to use extracted modules + src/store/workflowStore.ts + +Update executeWorkflow in workflowStore.ts to use the extracted modules: + +1. Import from execution/utils.ts: + - topologicalSort + - buildProviderHeaders + +2. Import from execution/nodeExecutors.ts: + - executeNode + +3. Refactor executeWorkflow to use the imported utilities: + - Replace inline topological sort with topologicalSort() + - Replace switch statement with executeNode() calls + - Keep the overall orchestration logic (pause handling, locked groups, logging) in the store + +4. Refactor regenerateNode similarly: + - Use buildProviderHeaders + - Could call individual executors but for now keep inline (regenerate has slightly different flow) + +5. Ensure getConnectedInputs stays in the store (it needs edge state) + +The goal is to reduce executeWorkflow from ~800 lines to ~200 lines of orchestration logic. + + npm run build succeeds, npm test passes, manual test of workflow execution works + executeWorkflow refactored to ~200 lines, uses extracted modules, all tests pass + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npm test` passes all tests +- [ ] executeWorkflow function reduced from ~800 to ~200 lines +- [ ] Workflow execution still works (test with a simple prompt -> generate -> output flow) +- [ ] regenerateNode still works +- [ ] Error handling preserved + + + + +- All tasks completed +- All verification checks pass +- executeWorkflow is significantly shorter and more readable +- Node execution logic is isolated and testable +- No regression in workflow execution behavior + + + +After completion, create `.planning/phases/16-store-modularization/16-02-SUMMARY.md` + diff --git a/.planning/phases/16-store-modularization/16-03-PLAN.md b/.planning/phases/16-store-modularization/16-03-PLAN.md new file mode 100644 index 00000000..3ec5e805 --- /dev/null +++ b/.planning/phases/16-store-modularization/16-03-PLAN.md @@ -0,0 +1,212 @@ +--- +phase: 16-store-modularization +plan: 03 +type: execute +--- + + +Extract remaining store slices into focused modules using Zustand slice pattern. + +Purpose: Break up the remaining monolithic store into logical slices (groups, clipboard, persistence, providers) that can be developed and tested independently while maintaining a single unified store. +Output: Zustand slices for groups, clipboard, persistence, and provider settings, composed into the main store. + + + +~/.claude/get-shit-done/workflows/execute-phase.md +~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/16-store-modularization/16-01-PLAN.md +@.planning/phases/16-store-modularization/16-02-PLAN.md +@src/store/workflowStore.ts + +**Dependencies from 16-01 and 16-02:** +- localStorage utilities extracted to src/store/utils/localStorage.ts +- Node defaults extracted to src/store/utils/nodeDefaults.ts +- Execution logic extracted to src/store/execution/ + +**Remaining Store Sections:** +After 16-01 and 16-02, the store still contains: +- Group operations (~200 lines): createGroup, deleteGroup, addNodesToGroup, etc. +- Clipboard operations (~100 lines): copySelectedNodes, pasteNodes, clearClipboard +- Persistence operations (~300 lines): saveWorkflow, loadWorkflow, auto-save +- Provider settings (~100 lines): updateProviderSettings, updateProviderApiKey +- Core node/edge operations (~200 lines): These stay in main store +- UI state (~50 lines): modal count, quickstart - stays in main store + + + + + + Task 1: Create group operations slice + src/store/slices/groupSlice.ts + +Create src/store/slices/groupSlice.ts using Zustand slice pattern: + +1. Define GroupSlice interface: + ```typescript + interface GroupSlice { + groups: Record; + createGroup: (nodeIds: string[]) => string; + deleteGroup: (groupId: string) => void; + addNodesToGroup: (nodeIds: string[], groupId: string) => void; + removeNodesFromGroup: (nodeIds: string[]) => void; + updateGroup: (groupId: string, updates: Partial) => void; + toggleGroupLock: (groupId: string) => void; + moveGroupNodes: (groupId: string, delta: { x: number; y: number }) => void; + setNodeGroupId: (nodeId: string, groupId: string | undefined) => void; + } + ``` + +2. Create slice creator function: + ```typescript + export const createGroupSlice: StateCreator< + WorkflowStore, + [], + [], + GroupSlice + > = (set, get) => ({ + groups: {}, + createGroup: (nodeIds) => { ... }, + // ... other methods + }); + ``` + +3. Import GROUP_COLORS and GROUP_COLOR_ORDER from utils/nodeDefaults +4. Import defaultNodeDimensions for bounding box calculation +5. Manage groupIdCounter within the slice + +The slice needs access to nodes for createGroup bounding box calculation, which it gets via get(). + + npm run build succeeds, slice compiles without errors + Group slice created with all group operations + + + + Task 2: Create persistence slice + src/store/slices/persistenceSlice.ts + +Create src/store/slices/persistenceSlice.ts: + +1. Define PersistenceSlice interface: + ```typescript + interface PersistenceSlice { + // State + workflowId: string | null; + workflowName: string | null; + saveDirectoryPath: string | null; + generationsPath: string | null; + lastSavedAt: number | null; + hasUnsavedChanges: boolean; + autoSaveEnabled: boolean; + isSaving: boolean; + useExternalImageStorage: boolean; + + // Actions + setWorkflowMetadata: (id: string, name: string, path: string, generationsPath?: string | null) => void; + setWorkflowName: (name: string) => void; + setGenerationsPath: (path: string | null) => void; + setAutoSaveEnabled: (enabled: boolean) => void; + setUseExternalImageStorage: (enabled: boolean) => void; + markAsUnsaved: () => void; + saveToFile: () => Promise; + initializeAutoSave: () => void; + cleanupAutoSave: () => void; + saveWorkflow: (name?: string) => void; + loadWorkflow: (workflow: WorkflowFile, workflowPath?: string) => Promise; + clearWorkflow: () => void; + } + ``` + +2. Move autoSaveIntervalId to slice file scope +3. Import localStorage helpers from utils/localStorage +4. Import externalizeWorkflowImages, hydrateWorkflowImages from @/utils/imageStorage +5. Keep nodeIdCounter and groupIdCounter management in main store (loadWorkflow needs to update them) + +Note: loadWorkflow also needs to reset execution state (isRunning, currentNodeId) which stays in main store. The slice's loadWorkflow will need to call through to main store's set(). + + npm run build succeeds, slice compiles without errors + Persistence slice created with save/load/auto-save logic + + + + Task 3: Compose slices into main store + src/store/workflowStore.ts + +Refactor workflowStore.ts to compose the slices: + +1. Import slices: + ```typescript + import { createGroupSlice, GroupSlice } from './slices/groupSlice'; + import { createPersistenceSlice, PersistenceSlice } from './slices/persistenceSlice'; + ``` + +2. Define combined store type: + ```typescript + type WorkflowStore = CoreSlice & GroupSlice & PersistenceSlice & ExecutionSlice; + ``` + +3. Update store creation using spread: + ```typescript + export const useWorkflowStore = create()((...args) => ({ + // Core state and actions inline + nodes: [], + edges: [], + // ... + + // Spread slices + ...createGroupSlice(...args), + ...createPersistenceSlice(...args), + })); + ``` + +4. Keep in main store (CoreSlice): + - nodes, edges, edgeStyle state + - Node CRUD: addNode, updateNodeData, removeNode, onNodesChange + - Edge operations: onEdgesChange, onConnect, addEdgeWithType, removeEdge, toggleEdgePause + - getNodeById, getConnectedInputs, validateWorkflow + - UI state: openModalCount, isModalOpen, showQuickstart + - Execution state: isRunning, currentNodeId, pausedAtNodeId + - executeWorkflow, regenerateNode, stopWorkflow (uses extracted execution modules) + - globalImageHistory, incurredCost state and actions + - Provider settings state and actions + - Model search dialog state + +5. Export backward-compatible aliases (GROUP_COLORS, saveNanoBananaDefaults, etc.) + +Target: Main store file reduced from ~2800 to ~1500 lines. + + npm run build succeeds, npm test passes all tests, manual test of group creation and save/load works + Store composed from slices, main file significantly reduced, all functionality preserved + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npm test` passes all tests +- [ ] workflowStore.ts reduced from ~2800 to ~1500 lines +- [ ] Group operations work (create, lock, delete) +- [ ] Save/load workflow works +- [ ] Auto-save works +- [ ] All exports maintained for backward compatibility + + + + +- All tasks completed +- All verification checks pass +- Store properly composed from slices +- Each slice is focused and cohesive +- No regression in any store functionality +- Phase 16 complete: Store Modularization achieved + + + +After completion, create `.planning/phases/16-store-modularization/16-03-SUMMARY.md` +