docs: add comprehensive JSDoc for all state sync points

Implements Phase 6.1 of the state management refactoring plan.

Added detailed documentation for all 5 critical sync points in the
state management architecture, making data flow explicit and traceable.

Sync Points Documented:

1. Document → graphStore (useActiveDocument.ts:67-80)
   - When: Active document switches
   - What: Loads timeline state into editor
   - Direction: ConstellationDocument → graphStore

2. graphStore → timeline current state (useActiveDocument.ts:197-214)
   - When: Graph changes detected
   - What: Updates timeline's current state
   - Direction: graphStore → timeline.states[currentStateId]

3. timeline → document (workspaceStore.ts:789-818)
   - When: Document save
   - What: Serializes timeline to storage
   - Direction: timelineStore → document.timeline → localStorage

4. document types → graphStore (workspaceStore.ts:1018-1034)
   - When: Type management operations
   - What: Syncs types to editor if active
   - Direction: document → graphStore (if active)

5. timeline → graphStore (timelineStore.ts:286-311)
   - When: Timeline state switch
   - What: Loads state's graph into editor
   - Direction: timeline.states[targetId] → graphStore

Each sync point includes:
-  Visual separator for easy identification
-  Trigger condition (when it occurs)
-  Data being synchronized (what changes)
-  Source of truth clarification
-  Data flow direction
-  Context and rationale

Benefits:
- Clear understanding of data flow through the system
- Easier onboarding for new developers
- Prevents accidental breaking of sync relationships
- Makes debugging state issues much faster

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jan-Henrik Bruhn 2025-10-20 15:17:36 +02:00
parent f29c55a1b8
commit 4b60c4b7b2
3 changed files with 75 additions and 5 deletions

View file

@ -283,8 +283,23 @@ export const useTimelineStore = create<TimelineStore & TimelineActions>(
return { timelines: newTimelines }; return { timelines: newTimelines };
}); });
// Load target state's graph (nodes, edges, and groups - types are global) /**
// IMPORTANT: Use loadGraphState for atomic update to prevent React Flow errors *
* SYNC POINT 5: timeline graphStore
*
*
* When: Timeline state switch (user navigates to different state in timeline)
* What: Loads target state's graph (nodes, edges, groups) into graphStore
* Source of Truth: timelineStore (targetState.graph)
* Direction: timeline.states[targetStateId].graph graphStore
*
* When switching between timeline states, we load the target state's graph
* into the editor. Types and labels remain the same (document-level config),
* only nodes/edges/groups change between states.
*
* IMPORTANT: Uses loadGraphState for atomic update to prevent React Flow
* "Parent node not found" errors when groups and their children load.
*/
const graphStore = useGraphStore.getState(); const graphStore = useGraphStore.getState();
graphStore.loadGraphState({ graphStore.loadGraphState({
nodes: targetState.graph.nodes as unknown as Actor[], nodes: targetState.graph.nodes as unknown as Actor[],

View file

@ -64,7 +64,20 @@ export function useActiveDocument() {
labels: [], labels: [],
}); });
// Load active document into graphStore when it changes /**
*
* SYNC POINT 1: Document graphStore
*
*
* When: Active document switches (document tab change)
* What: Loads nodes, edges, groups, types, labels from document into graphStore
* Source of Truth: ConstellationDocument (persistent storage)
* Direction: document.timeline.currentState graphStore (working copy)
*
* This is the entry point for loading a document's data into the editor.
* It deserializes the document's current timeline state and populates the
* graphStore with the working copy that React Flow will render.
*/
useEffect(() => { useEffect(() => {
if (activeDocument && activeDocumentId) { if (activeDocument && activeDocumentId) {
console.log(`Loading document into graph editor: ${activeDocumentId}`, activeDocument.metadata.title); console.log(`Loading document into graph editor: ${activeDocumentId}`, activeDocument.metadata.title);
@ -181,7 +194,19 @@ export function useActiveDocument() {
labels: graphLabels as LabelConfig[], labels: graphLabels as LabelConfig[],
}; };
// Update the timeline's current state with the new graph data (nodes, edges, and groups) /**
*
* SYNC POINT 2: graphStore timeline current state
*
*
* When: Graph changes detected (node/edge/group add/delete/move)
* What: Updates timeline.states[currentStateId].graph with latest graphStore data
* Source of Truth: graphStore (working copy)
* Direction: graphStore timeline.states[currentStateId].graph
*
* This keeps the timeline's current state in sync with the editor's working copy.
* When the document is saved, this updated timeline will be serialized to storage.
*/
useTimelineStore.getState().saveCurrentGraph({ useTimelineStore.getState().saveCurrentGraph({
nodes: graphNodes as never[], nodes: graphNodes as never[],
edges: graphEdges as never[], edges: graphEdges as never[],

View file

@ -786,7 +786,20 @@ export const useWorkspaceStore = create<Workspace & WorkspaceActions>((set, get)
// and are managed via workspaceStore's type management actions. // and are managed via workspaceStore's type management actions.
// We do NOT copy them from graphStore because the document is the source of truth. // We do NOT copy them from graphStore because the document is the source of truth.
// Save timeline data if exists /**
*
* SYNC POINT 3: timeline document
*
*
* When: Document save (auto-save or manual)
* What: Serializes entire timeline (all states) to document.timeline
* Source of Truth: timelineStore (transient working copy)
* Direction: timelineStore document.timeline localStorage
*
* This persists the complete timeline structure to storage. The timeline's
* current state has already been updated by SYNC POINT 2, so we're saving
* the latest graph data along with all historical timeline branches.
*/
const timelineState = useTimelineStore.getState(); const timelineState = useTimelineStore.getState();
const timeline = timelineState.timelines.get(documentId); const timeline = timelineState.timelines.get(documentId);
@ -1002,6 +1015,23 @@ export const useWorkspaceStore = create<Workspace & WorkspaceActions>((set, get)
} }
}, },
/**
*
* SYNC POINT 4: document types graphStore
*
*
* When: Type management operations (add/update/delete node/edge types, labels)
* What: Updates document types/labels and syncs to graphStore if document is active
* Source of Truth: ConstellationDocument (document.nodeTypes, document.edgeTypes, document.labels)
* Direction: document graphStore (if active document)
*
* Type configurations are document-level properties. When modified, changes
* are persisted to the document first, then synced to graphStore if this is
* the currently active document. This ensures the editor always displays the
* correct types for the current document.
*
* All type operations use atomic transactions with rollback (Phase 3.1).
*/
addNodeTypeToDocument: (documentId: string, nodeType) => { addNodeTypeToDocument: (documentId: string, nodeType) => {
const state = get(); const state = get();
const doc = state.documents.get(documentId); const doc = state.documents.get(documentId);