Frontmatter
| id | 9370 |
| title | Upgrade ScrollSync Locking Mechanism |
| state | Closed |
| labels | enhancementaiarchitecture |
| assignees | tobiu |
| createdAt | Mar 7, 2026, 5:38 PM |
| updatedAt | Mar 7, 2026, 5:54 PM |
| githubUrl | https://github.com/neomjs/neo/issues/9370 |
| author | tobiu |
| commentsCount | 1 |
| parentIssue | 9369 |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Mar 7, 2026, 5:53 PM |
Upgrade ScrollSync Locking Mechanism

Input from Gemini 3.1 Pro:
✦ During implementation of the lock mechanism, we pivoted the architectural design to avoid a critical race condition.
Instead of a single global
isCoordinating = trueflag (which would have locked the entire application and prevented multiple grids from being scrolled simultaneously), we implemented a granular Set-based lock (this.coordinatingNodes = new Set()).When
syncTooronScrollmutates a DOM node, it adds that specific node'sidto the Set. The interceptor then only ignores incoming nativescrollevents if theirevent.target.idis currently in the Set. This allows parallel scrolling of unrelated components while still perfectly preventing ping-pong loops within synchronized pairs.The
syncToAPI is now in place and uses this granular locking. I will close this ticket and #9371. We will create a separate sub-task to handle the complexities of releasing the lock during continuous momentum scrolling (rAF vs setTimeout).
Part of Epic #9369
Refactor
src/main/addon/ScrollSync.mjsto introduce an internalisCoordinatinglock. When a scroll event is received from a source node, the addon must acquire the lock, mutate the target node, and release the lock only after the resulting native events have cleared. This stateful coordination prevents infinite "ping-pong" feedback loops during two-way synchronization.