Fix CREATE operation race condition that overwrote user typing#840
Conversation
When a user typed content while a CREATE operation was in flight, the
persistence code would overwrite their new content with stale data
captured at the start of the operation.
Bug scenario:
1. User creates new node with content "## " (header)
2. User deletes "## " (becomes text node)
3. User presses Tab to indent
4. User starts typing "Hello World"
5. CREATE operation completes and fetches version from backend
6. nodes.set() was called with stale `currentNode` from step 1
7. User's typing ("Hello World") was overwritten with "## "
Fix: After CREATE completes, read fresh node state from store and only
update the version field, preserving user's local content changes.
Applied fix to two code paths:
- setNode path for new nodes (lines ~1172-1176)
- commitBatch path (lines ~2518-2521)
Also includes refactoring of SimplePersistenceCoordinator to queue
operations properly, ensuring DELETE operations aren't lost when
queued during an executing UPDATE.
Added 3 regression tests to prevent this bug from recurring.
Co-Authored-By: Claude <noreply@anthropic.com>
Code Review: PR #840 - Fix CREATE Operation Race ConditionReview TypeInitial Review (No prior reviews found) Summary AssessmentThis PR addresses a critical data loss bug where user-typed content was being overwritten when CREATE operations completed asynchronously. The fix is well-targeted, correctly preserves user content during race conditions, and includes comprehensive regression tests. Overall Verdict: APPROVE - This is a high-quality bug fix that solves a real user-facing issue with minimal, focused changes. Requirements Validation
Code Review FindingsArchitectural Design & Integrity[APPROVE] The fix demonstrates excellent understanding of the existing architecture:
The refactoring of Functionality & Correctness[APPROVE] The fix correctly addresses the root cause: Before (Bug): currentNode.version = createdNode.version;
this.nodes.set(nodeId, currentNode); // Overwrites with stale snapshotAfter (Fix): const latestLocalNode = this.nodes.get(nodeId);
if (latestLocalNode) {
latestLocalNode.version = createdNode.version;
this.nodes.set(nodeId, latestLocalNode);
}The fix correctly:
SecurityNo security concerns - this is an internal state management fix that doesn't introduce any new attack vectors, data exposure, or authentication concerns. Maintainability & ReadabilitySuggestions:
return;
}
// Check if node has been persisted...
Testing Strategy & Robustness[APPROVE] The test coverage is comprehensive and well-structured:
The tests correctly:
Performance & ScalabilityNo performance concerns. The fix adds one additional Minor ObservationsExtra Blank Line[Nit] Line 1132 has an extra blank line that should be removed for consistency. Test Timeout ConsiderationsThe tests use Final RecommendationAPPROVE This PR:
The fix demonstrates solid engineering judgment - it solves the immediate problem while also improving the underlying infrastructure (operation queueing) to prevent similar issues in the future. Reviewed by: Principal Engineer AI Reviewer |
malibio
left a comment
There was a problem hiding this comment.
Code review complete - recommending APPROVE. See full review comment above.
Removes accidental double blank line at line 1132 identified during code review. Severity: 🟢 Nit Co-Authored-By: Claude <noreply@anthropic.com>
Review Recommendations AddressedSummary
Changes Made🟢 Nit: Extra blank line at line 1132
Verification
Addressed by: Implementation Agent |
* Fix CREATE operation race condition that overwrote user typing
When a user typed content while a CREATE operation was in flight, the
persistence code would overwrite their new content with stale data
captured at the start of the operation.
Bug scenario:
1. User creates new node with content "## " (header)
2. User deletes "## " (becomes text node)
3. User presses Tab to indent
4. User starts typing "Hello World"
5. CREATE operation completes and fetches version from backend
6. nodes.set() was called with stale `currentNode` from step 1
7. User's typing ("Hello World") was overwritten with "## "
Fix: After CREATE completes, read fresh node state from store and only
update the version field, preserving user's local content changes.
Applied fix to two code paths:
- setNode path for new nodes (lines ~1172-1176)
- commitBatch path (lines ~2518-2521)
Also includes refactoring of SimplePersistenceCoordinator to queue
operations properly, ensuring DELETE operations aren't lost when
queued during an executing UPDATE.
Added 3 regression tests to prevent this bug from recurring.
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review: Remove extra blank line
Removes accidental double blank line at line 1132 identified
during code review.
Severity: 🟢 Nit
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Fix CREATE operation race condition that overwrote user typing
When a user typed content while a CREATE operation was in flight, the
persistence code would overwrite their new content with stale data
captured at the start of the operation.
Bug scenario:
1. User creates new node with content "## " (header)
2. User deletes "## " (becomes text node)
3. User presses Tab to indent
4. User starts typing "Hello World"
5. CREATE operation completes and fetches version from backend
6. nodes.set() was called with stale `currentNode` from step 1
7. User's typing ("Hello World") was overwritten with "## "
Fix: After CREATE completes, read fresh node state from store and only
update the version field, preserving user's local content changes.
Applied fix to two code paths:
- setNode path for new nodes (lines ~1172-1176)
- commitBatch path (lines ~2518-2521)
Also includes refactoring of SimplePersistenceCoordinator to queue
operations properly, ensuring DELETE operations aren't lost when
queued during an executing UPDATE.
Added 3 regression tests to prevent this bug from recurring.
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review: Remove extra blank line
Removes accidental double blank line at line 1132 identified
during code review.
Severity: 🟢 Nit
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Fix CREATE operation race condition that overwrote user typing
When a user typed content while a CREATE operation was in flight, the
persistence code would overwrite their new content with stale data
captured at the start of the operation.
Bug scenario:
1. User creates new node with content "## " (header)
2. User deletes "## " (becomes text node)
3. User presses Tab to indent
4. User starts typing "Hello World"
5. CREATE operation completes and fetches version from backend
6. nodes.set() was called with stale `currentNode` from step 1
7. User's typing ("Hello World") was overwritten with "## "
Fix: After CREATE completes, read fresh node state from store and only
update the version field, preserving user's local content changes.
Applied fix to two code paths:
- setNode path for new nodes (lines ~1172-1176)
- commitBatch path (lines ~2518-2521)
Also includes refactoring of SimplePersistenceCoordinator to queue
operations properly, ensuring DELETE operations aren't lost when
queued during an executing UPDATE.
Added 3 regression tests to prevent this bug from recurring.
Co-Authored-By: Claude <noreply@anthropic.com>
* Address review: Remove extra blank line
Removes accidental double blank line at line 1132 identified
during code review.
Severity: 🟢 Nit
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Summary
Problem
When a user typed content while a CREATE operation was in flight, the persistence code would overwrite their new content with stale data captured at the start of the operation.
Bug scenario:
##(header)##(becomes text node)nodes.set()was called with stalecurrentNodefrom step 1##Solution
After CREATE completes, read fresh node state from store and only update the version field, preserving user's local content changes.
Applied fix to two code paths:
Also includes refactoring of SimplePersistenceCoordinator to queue operations properly, ensuring DELETE operations aren't lost when queued during an executing UPDATE.
Test plan
🤖 Generated with Claude Code