Context
The delta-level source of the v13 gate family's duplication symptoms, captured 2026-06-12 ~00:25 via main-thread logDeltaUpdates during a single overdrag walk (full harvest on #12929; reproducible deterministically in ~10s by the probe spec on the agent/12929-12932-grid-fixes trail). Splits the FIX lane out of #12929 (whose within-region legs PR #12938 delivers) so the trail survives that close.
Release classification: ON the board — v13 gate family (parent #12883).
The Problem (delta evidence)
During one overdrag walk (DOM grid-body count 3 → 5):
update 37 (1): set neo-grid-body-3 ← full-node re-render, end body
update 43 (120): insertNode → neo-grid-body-1__row-N idx 22 ← ID-LESS cell inserts, per row
update 51-75: insertNode → …__row-N idx 23/24/25/26 (100-160 deltas per beat)
update 89 (1): insertNode → neo-grid-view-1 idx 2 ← ID-LESS body insert: a body-3 COPY born
update 107 (1): set neo-grid-body-3
An id-less insertNode means the differ created a new node from vdom instead of matching an existing one — its vnode baseline did not contain what the DOM already held. Two layers, one mechanism:
- view-level: region-body copies born into
neo-grid-view-1 (up to ×14 observed across a session — the operator's "columns duplicate" screenshot)
- row-level: new cells created instead of pool recycling — the two-generation cell-id doubling (#12930) at the moment of birth
Locked regions are irrelevant: the operator's no-locks control reproduces (recorded on #12930) — it is the multi-body-era view→body→row update stack, exercised at walk-beat frequency.
The Architectural Reality
Per walk beat: grid.ScrollManager ingests the scroll → View.syncBodies runs silent per-body sets + createViewData(true) then view.updateDepth = 3; view.update() (throttled 16ms) — concurrently, each body's own update paths fire (buffered re-mounts emitting 100-160 deltas per beat; mid-drag, switchItems additionally runs body.updateDepth = -1; body.update()). Updates whose vnode baselines go stale against each other produce created-not-matched children. The framework's update-collision machinery (VdomLifecycle merging/queueing) is either bypassed by the silent-set + direct-createViewData paths or defeated by the depth-spanning parent update racing child updates.
The Fix (territory — exact patch from the investigation)
Vnode-baseline discipline / update serialization at the collision point: the view's depth-spanning update and the bodies' own updates must not diff against each other's stale baselines. Candidate shapes (to be validated against the probe): route the per-beat body updates THROUGH the view's single update (true silent aggregation), or gate syncBodies' view update on no body-update-in-flight (and vice versa), or fix the baseline capture in the depth-merge path. Symptom-side dedup is explicitly rejected.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
Grid view→body per-beat update flow (View.syncBodies + body createViewData interplay) |
This delta harvest + the framework's VdomLifecycle collision contract |
One coherent baseline per frame: no id-less insertNode for nodes that exist in the DOM; pool cells recycle, never re-create |
Behavior under non-scroll updates (store loads, resize) unchanged — probe forensics cover baseline + drags + scrolls |
JSDoc at the touched flow points |
L3: the 10s probe — verify_component_consistency(view) clean through walk+drop; delta capture shows zero id-less inserts for existing nodes; #12930's repro A/B gestures clean. L4: operator gate session |
grid.Row pool-cell identity (__cell-N ↔ field-named) |
#12930 (the cell-layer manifestation) |
Unchanged by this ticket's fix UNLESS the bisect (#12930, @neo-fable-clio) shows an independent identity seam — the two lanes converge on one patch if not |
Cross-validated with her bisect baselines |
n/a here |
Her #12930 verification matrix |
Acceptance Criteria
Out of Scope
- The already-fixed legs (PR #12938: drop re-sync, single-fire rebuild, echo gate).
- NL motion-forensics granularity (#12931).
Related
- Parent: #12883 · Evidence trail: #12929 (delta harvest comment) · Cell layer: #12930 (@neo-fable-clio's bisect — convergence expected) · Fixture: #12936
Live latest-open sweep: checked at 2026-06-12T01:0xZ; #12930 covers the cell-layer manifestation and its bisect — this ticket owns the update-race FIX; no other equivalent. A2A in-flight sweep: the lane split (her bisect / my fix) is on the record both directions.
Origin Session ID: e605ce21-3668-445c-bc00-45896aa9a092
Retrieval Hint: "id-less insertNode stale vnode baseline view syncBodies update race grid duplication fix"
Context
The delta-level source of the v13 gate family's duplication symptoms, captured 2026-06-12 ~00:25 via main-thread
logDeltaUpdatesduring a single overdrag walk (full harvest on #12929; reproducible deterministically in ~10s by the probe spec on theagent/12929-12932-grid-fixestrail). Splits the FIX lane out of #12929 (whose within-region legs PR #12938 delivers) so the trail survives that close.Release classification:ON the board — v13 gate family (parent #12883).The Problem (delta evidence)
During one overdrag walk (DOM
grid-bodycount 3 → 5):An id-less insertNode means the differ created a new node from vdom instead of matching an existing one — its vnode baseline did not contain what the DOM already held. Two layers, one mechanism:
neo-grid-view-1(up to ×14 observed across a session — the operator's "columns duplicate" screenshot)Locked regions are irrelevant: the operator's no-locks control reproduces (recorded on #12930) — it is the multi-body-era view→body→row update stack, exercised at walk-beat frequency.
The Architectural Reality
Per walk beat:
grid.ScrollManageringests the scroll →View.syncBodiesruns silent per-body sets +createViewData(true)thenview.updateDepth = 3; view.update()(throttled 16ms) — concurrently, each body's own update paths fire (buffered re-mounts emitting 100-160 deltas per beat; mid-drag,switchItemsadditionally runsbody.updateDepth = -1; body.update()). Updates whose vnode baselines go stale against each other produce created-not-matched children. The framework's update-collision machinery (VdomLifecycle merging/queueing) is either bypassed by the silent-set + direct-createViewDatapaths or defeated by the depth-spanning parent update racing child updates.The Fix (territory — exact patch from the investigation)
Vnode-baseline discipline / update serialization at the collision point: the view's depth-spanning update and the bodies' own updates must not diff against each other's stale baselines. Candidate shapes (to be validated against the probe): route the per-beat body updates THROUGH the view's single update (true silent aggregation), or gate
syncBodies' view update on no body-update-in-flight (and vice versa), or fix the baseline capture in the depth-merge path. Symptom-side dedup is explicitly rejected.Contract Ledger Matrix
View.syncBodies+ bodycreateViewDatainterplay)verify_component_consistency(view)clean through walk+drop; delta capture shows zero id-less inserts for existing nodes; #12930's repro A/B gestures clean. L4: operator gate sessiongrid.Rowpool-cell identity (__cell-N↔ field-named)Acceptance Criteria
verify_component_consistencyon the view AND a body sample = zero mismatches at every step.Out of Scope
Related
Live latest-open sweep: checked at 2026-06-12T01:0xZ; #12930 covers the cell-layer manifestation and its bisect — this ticket owns the update-race FIX; no other equivalent. A2A in-flight sweep: the lane split (her bisect / my fix) is on the record both directions.
Origin Session ID: e605ce21-3668-445c-bc00-45896aa9a092
Retrieval Hint: "id-less insertNode stale vnode baseline view syncBodies update race grid duplication fix"