Context
When multiple users apply an AI-generated workflow simultaneously, the Y.Array CRDT can create duplicate entries. This happens because Y.Array's push() operation is always additive - concurrent pushes from different users all get merged.
Current data structure:
ydoc.getArray('jobs') // Y.Array<Y.Map>
ydoc.getArray('triggers') // Y.Array<Y.Map>
ydoc.getArray('edges') // Y.Array<Y.Map>
Current apply logic (in YAMLStateToYDoc.applyToYDoc):
const jobsArray = ydoc.getArray('jobs');
jobsArray.delete(0, jobsArray.length); // Clear
jobsArray.push(transformedJobs); // Add new
When two users click Apply concurrently:
- Both delete operations target the same items
- Both push operations add the same items
- CRDT merge includes BOTH pushes → duplicate entries
Current Solution (Implemented)
We implemented server-coordinated locking via Phoenix Channel:
- When a user clicks Apply, backend broadcasts
workflow_applying to all clients
- All clients disable their Apply buttons
- When apply completes, backend broadcasts
workflow_applied
- All clients re-enable Apply buttons
This is 100% effective because the server is the single source of truth.
Files changed:
lib/lightning_web/channels/workflow_channel.ex - Added start_applying_workflow and done_applying_workflow handlers
assets/js/collaborative-editor/stores/createWorkflowStore.ts - Channel listeners and coordination methods
assets/js/collaborative-editor/components/AIAssistantPanelWrapper.tsx - Calls coordination methods
Proposed Future Improvement
Change the Y.Doc structure to use Y.Map keyed by ID:
ydoc.getMap('jobs') // Y.Map<id, Y.Map>
ydoc.getMap('triggers') // Y.Map<id, Y.Map>
ydoc.getMap('edges') // Y.Map<id, Y.Map>
Why this would be better:
map.set(id, data) is idempotent - two users setting the same key = same result, no duplicates
- More semantically correct (entities are keyed by ID)
- Simpler apply logic without needing coordination
What would need to change:
-
Frontend:
YAMLStateToYDoc.applyToYDoc - Use map.set(id, data) instead of array.push()
createWorkflowStore.ts - Update observers to read from maps
- Convert map values to arrays for Immer state (React expects arrays)
-
Backend:
lib/lightning/collaboration/workflow_serializer.ex - Change Yex.Doc.get_array to Yex.Doc.get_map
- Update initialization and extraction logic
Priority
Low - The server-coordinated solution works perfectly. This is an architectural improvement for cleaner CRDT semantics, not a bug fix.
Context
When multiple users apply an AI-generated workflow simultaneously, the Y.Array CRDT can create duplicate entries. This happens because Y.Array's
push()operation is always additive - concurrent pushes from different users all get merged.Current data structure:
Current apply logic (in
YAMLStateToYDoc.applyToYDoc):When two users click Apply concurrently:
Current Solution (Implemented)
We implemented server-coordinated locking via Phoenix Channel:
workflow_applyingto all clientsworkflow_appliedThis is 100% effective because the server is the single source of truth.
Files changed:
lib/lightning_web/channels/workflow_channel.ex- Addedstart_applying_workflowanddone_applying_workflowhandlersassets/js/collaborative-editor/stores/createWorkflowStore.ts- Channel listeners and coordination methodsassets/js/collaborative-editor/components/AIAssistantPanelWrapper.tsx- Calls coordination methodsProposed Future Improvement
Change the Y.Doc structure to use Y.Map keyed by ID:
Why this would be better:
map.set(id, data)is idempotent - two users setting the same key = same result, no duplicatesWhat would need to change:
Frontend:
YAMLStateToYDoc.applyToYDoc- Usemap.set(id, data)instead ofarray.push()createWorkflowStore.ts- Update observers to read from mapsBackend:
lib/lightning/collaboration/workflow_serializer.ex- ChangeYex.Doc.get_arraytoYex.Doc.get_mapPriority
Low - The server-coordinated solution works perfectly. This is an architectural improvement for cleaner CRDT semantics, not a bug fix.