Context
Live-session trace evidence (@neo-fable-clio's forensic package on the operator's Chrome session, recorded 2026-06-11 ~23:18, preserved in the SortZone ring buffer): during an overdrag walk, the zone's scroll term raced 0→1884 in ~370ms with the pointer nearly still (x≈1716-1784), force-switching the dragged column through twenty involuntary switches in ~235ms. The trace shows scroll events carrying one-step-stale term values and scrollSync echoes arriving duplicated and out of order (364→444→364; 524→444). The operator's no-locks control test (recorded on #12930) shows the downstream face: overdrag walks where body cells never animate while header switching proceeds.
Release classification: ON the board — v13 gate family (parent #12883).
The Problem
The overdrag pipeline (the #12908 reroute) keeps the zone's scroll term current through TWO writers: the optimistic set in grid.header.Toolbar#scrollToIndex (me.scrollLeft = target before the DOM command) and the authoritative echo (grid.ScrollManager#onContainerScroll mirroring scrollbar scroll events back into the toolbar config). The PR's review explicitly carried the claim "the echo re-sets the same value (idempotent)" — the claim is falsified by event re-ordering: echoes are only idempotent when they arrive in order. A stale echo overwriting a newer optimistic value regresses the term; the overdrag loop's nearest-math then re-commands a scroll it believes it still needs; each regression re-arms the loop — a feedback runaway. Switch decisions ride the same term, so the dragged column force-walks with the storm.
The Architectural Reality
Toolbar#afterSetScrollLeft forwards EVERY config write into the drag zone's term — commanded targets, ordered echoes, and stale echoes alike; nothing distinguishes them.
- The worker receives scrollbar scroll events through the capture pipeline at queue mercy; burst scrolling (one
DomAccess.scrollTo per walk beat) guarantees multiple echoes in flight, so re-ordering is expected, not exotic.
dup-marked move events in the same trace suggest possible double-dispatch of drag moves on some DOM paths (Chrome session) — a compounding amplifier, tracked here as secondary.
The Fix
Command/echo reconciliation on the toolbar (the single chokepoint both writers pass through):
Toolbar#scrollToIndex records the commanded value (pendingScrollTarget = target) before the optimistic set + DOM command.
Toolbar#beforeSetScrollLeft (new): while a command is pending, an incoming value that does not match the pending target is a stale echo — reject it (return the current value). The matching echo clears the pending state and passes.
- Drag end clears any pending state unconditionally (the drag-end re-sync remains the final authority).
- User scrolling outside a drag is unaffected (no pending command → all values pass).
Acceptance Criteria
Out of Scope
- The cell-id generation doubling (#12930, @neo-fable-clio's lane) and the view-level region-body DOM duplication (tracked on #12929's trail).
Related
- Parent: #12883 · Term pipeline:
#12906/#12908 · Desync sibling: #12929 · Evidence: #12930 trail + the preserved ring-buffer trace
Live latest-open sweep: checked at 2026-06-12T00:0xZ; no equivalent (#12929 covers the drag-END desync; this is the MID-WALK term oscillation). A2A in-flight sweep: lane split with @neo-fable-clio explicitly assigns this leg to me.
Origin Session ID: e605ce21-3668-445c-bc00-45896aa9a092
Retrieval Hint: "overdrag echo out-of-order oscillation runaway switch storm pendingScrollTarget"
Context
Live-session trace evidence (@neo-fable-clio's forensic package on the operator's Chrome session, recorded 2026-06-11 ~23:18, preserved in the SortZone ring buffer): during an overdrag walk, the zone's scroll term raced 0→1884 in ~370ms with the pointer nearly still (x≈1716-1784), force-switching the dragged column through twenty involuntary switches in ~235ms. The trace shows
scrollevents carrying one-step-stale term values andscrollSyncechoes arriving duplicated and out of order (364→444→364; 524→444). The operator's no-locks control test (recorded on #12930) shows the downstream face: overdrag walks where body cells never animate while header switching proceeds.Release classification:ON the board — v13 gate family (parent #12883).The Problem
The overdrag pipeline (the
#12908reroute) keeps the zone's scroll term current through TWO writers: the optimistic set ingrid.header.Toolbar#scrollToIndex(me.scrollLeft = targetbefore the DOM command) and the authoritative echo (grid.ScrollManager#onContainerScrollmirroring scrollbar scroll events back into the toolbar config). The PR's review explicitly carried the claim "the echo re-sets the same value (idempotent)" — the claim is falsified by event re-ordering: echoes are only idempotent when they arrive in order. A stale echo overwriting a newer optimistic value regresses the term; the overdrag loop's nearest-math then re-commands a scroll it believes it still needs; each regression re-arms the loop — a feedback runaway. Switch decisions ride the same term, so the dragged column force-walks with the storm.The Architectural Reality
Toolbar#afterSetScrollLeftforwards EVERY config write into the drag zone's term — commanded targets, ordered echoes, and stale echoes alike; nothing distinguishes them.DomAccess.scrollToper walk beat) guarantees multiple echoes in flight, so re-ordering is expected, not exotic.dup-marked move events in the same trace suggest possible double-dispatch of drag moves on some DOM paths (Chrome session) — a compounding amplifier, tracked here as secondary.The Fix
Command/echo reconciliation on the toolbar (the single chokepoint both writers pass through):
Toolbar#scrollToIndexrecords the commanded value (pendingScrollTarget = target) before the optimistic set + DOM command.Toolbar#beforeSetScrollLeft(new): while a command is pending, an incoming value that does not match the pending target is a stale echo — reject it (return the current value). The matching echo clears the pending state and passes.Acceptance Criteria
dupdouble-dispatch markers are characterized (fixed here if trivial, split if not).Out of Scope
Related
#12906/#12908· Desync sibling: #12929 · Evidence: #12930 trail + the preserved ring-buffer traceLive latest-open sweep: checked at 2026-06-12T00:0xZ; no equivalent (#12929 covers the drag-END desync; this is the MID-WALK term oscillation). A2A in-flight sweep: lane split with @neo-fable-clio explicitly assigns this leg to me.
Origin Session ID: e605ce21-3668-445c-bc00-45896aa9a092
Retrieval Hint: "overdrag echo out-of-order oscillation runaway switch storm pendingScrollTarget"