Frontmatter
| id | 8893 |
| title | Create Unit Test for Grid VDOM Deltas |
| state | Closed |
| labels | aitestingregression |
| assignees | tobiu |
| createdAt | Jan 27, 2026, 1:03 PM |
| updatedAt | Jan 27, 2026, 10:21 PM |
| githubUrl | https://github.com/neomjs/neo/issues/8893 |
| author | tobiu |
| commentsCount | 4 |
| parentIssue | 8891 |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [ ] 8894 Restore Grid Stability (Fix/Revert Teleportation) |
| closedAt | Jan 27, 2026, 10:21 PM |
Create Unit Test for Grid VDOM Deltas

Input from Gemini 3 Pro:
✦ We have created a baseline Unit Test (
test/playwright/unit/grid/Teleportation.spec.mjs) that successfully simulates:
- Grid Instantiation with Component Columns.
- Scroll-triggered Row Recycling.
- Component Content Updates (
text: record.name).Findings:
- The test currently PASSES in the Unit Test environment (
DomApiVnodeCreator).- The VDOM Deltas correctly show
textContentupdates on existing nodes (neo-vnode-XXX), proving that ID matching is working correctly in this "happy path" scenario.- The "Stephanie ++" duplication artifact is NOT reproduced yet.
Next Steps: To catch the bug, we must enhance the test to simulate the specific conditions of the failure:
- Race Conditions: Trigger a second scroll event before the first VDOM update promise resolves.
- Disjoint Updates: Verify if the
Buttonupdate is being processed as a separate batch from theGridBodyupdate (which causes the ID desync).- VdomHelper Mocking: We have added a spy to
VdomHelper.updateto capture all global deltas, which will be essential for analyzing the race condition results.The test file is committed and ready for extension.

Input from Gemini 3 Pro:
✦ ### Internal Mechanics & Hypothesis (For Future Sessions)
The unit test
test/playwright/unit/grid/Teleportation.spec.mjshas been documented with critical context about the VDOM architecture.The "ID Desync" Hypothesis: The duplication artifact ("Stephanie ++Stephanie ++") strongly suggests that the VDOM Worker is treating existing nodes as new insertions because it cannot match them to the old tree.
- The Mechanism: Neo.mjs relies on a feedback loop where
vnodeIDs generated in the VDOM Worker are synced back to the App Worker'svdomobject viasyncVnodeTree->syncVdomState.- The Failure Mode: If Teleportation (Disjoint Updates) or rapid scrolling triggers a new update before this sync completes, the App Worker sends a
vdomtree with missing or stale IDs for child elements (like the text span inside a button).- The Result:
VdomHelpersees a node without an ID, assumes it's new, generates aninsertNodedelta, but potentially fails to generate a correspondingremoveNodedelta for the existing node (possibly due to sparse tree logic pruning the "old" parent container).Next Steps for Reproduction: The current test passes because it runs synchronously in a single thread, where ID syncing is immediate. To reproduce the bug, the test must simulate the latency of the Worker roundtrip, creating a window where
updateVdomis called while thevdomobject is still "dirty" (missing IDs).

Input from Gemini 3 Pro:
✦ ### Unit Test Analysis & Findings
We have enhanced the
Teleportation.spec.mjsunit test to robustly simulate and inspect VDOM deltas during grid scrolling.Current Test Status:
- Happy Path (Normal Scroll): PASSING. We observe correct row recycling (8 moves) and new row insertions (3 inserts) for an 800px scroll.
- Race Condition (Async/Rapid Scroll): PASSING. We simulate disjoint updates with latency. We observe correct recycling (3 moves, 0 inserts, 8 text updates) and NO duplication.
The Discrepancy (The Missing Link): The real-world breaking logs show a "Suspicious Update Pattern" that we are NOT seeing in the test environment yet:
- Recursive Moves: The real app generates
moveNodedeltas for internal stable nodes (like the Icon and Text span inside a Button) even when the parent Button itself is moving correctly with the row.- Stable IDs: These internal nodes have stable
neo-vnode-XXXIDs, meaning the App Worker is correctly syncing IDs.- Why Move? The fact that
VdomHelpergenerates amoveNodefor an internal node (e.g.,index: 0) implies that the VDOM diffing logic believes the node was not at index 0 in the previous state, or that the parent relationship is ambiguous.New Theory (User Provided): The issue might stem from the order of operations combined with ID uniqueness during recycling.
removeNodedeltas happen last.- If a row is "moved" (recycled) from Top to Bottom.
- And we insert a "New" row at the bottom (which reuses the ID of the top row).
- In the VDOM world, the ID is unique.
- In the DOM world, if the "Old" row wasn't removed yet (or if
VdomHelperlogic processes insertions before removals), we might have a transient state where IDs collide or parent pointers are stale.Next Steps: We need to adjust the test to replicate the exact "Scroll Down" scenario where:
- Old VDOM has rows
0..N.- New VDOM has rows
X..N+X.- We force the VDOM engine to grapple with the "Old" rows (Top) that are missing in the New VDOM, while simultaneously handling the "New" rows (Bottom) that reuse the IDs/components.
- We must verify why our test currently handles this cleanly (producing valid moves) while the real app produces "Recursive Moves" that likely lead to the corruption.
The test file
test/playwright/unit/grid/Teleportation.spec.mjsis now a solid baseline to pursue this investigation.

Input from Gemini 3 Pro:
✦ Investigation completed. The root cause was identified as VDOM ID corruption in
Neo.util.VDom.syncVdomStateduring structural shifts.This resulted in the creation of a framework-level fix ticket: #8898. The fix has been implemented and pushed in commit
[dev 7a1b2c] fix: Prevent VDOM ID corruption during structural shifts (Row Recycling) #8893.Closing this investigation ticket.
Create a Playwright Unit Test (
test/playwright/unit/grid/Teleportation.spec.mjs) to inspect the low-level VDOM deltas generated during Grid scrolling.Requirements:
GridContainerandGridBodyin the Node.js test environment.afterSetScrollTopto simulate scrolling.VdomHelper.TreeBuilderoutput for sparse tree generation issues.