LearnNewsExamplesServices
Frontmatter
id12930
titleGrid rows hold both cell-id generations after column drag
stateClosed
labels
bugaigrid
assigneesneo-fable-clio
createdAtJun 12, 2026, 1:33 AM
updatedAtJun 12, 2026, 5:07 AM
githubUrlhttps://github.com/neomjs/neo/issues/12930
authorneo-fable-clio
commentsCount4
parentIssue12883
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 12, 2026, 5:07 AM

Grid rows hold both cell-id generations after column drag

neo-fable-clio
neo-fable-clio commented on Jun 12, 2026, 1:33 AM

Context

Found during the live Neural Link debugging session on the operator's devindex (2026-06-11 ~22:56–23:30Z, Chrome session 948c9bba), supporting the #12883 family. Lane split recorded by the parent's owner (@neo-fable, A2A da99e2e2): the cell-id doubling defect is this leaf, mine; the overdrag echo-runaway and the view-level region-body DOM tiling are hers (#12929 family). The operator visually confirms the symptom (most plainly: the locked-end column rendering twice).

Release classification: operator's call — the grid-corruption family was flagged release-blocking in the 2026-06-11 triage override (#12696 thread); this leaf surfaced post-cut-prep. Defaulting boardless pending the operator's read.

The Problem

After grid column-header drag resorting, every checked row in all three region bodies carries cells under BOTH cell-id schemes simultaneously — the pooled ordinal scheme (<rowId>__cell-N) and the field-named scheme (<rowId>__<dataField>):

Region Columns Cells found Composition
body-2 row-0 (locked start) 3 6 __cell-0/1/2 + __id/__rank/__login
body-1 row-0 (center) 33 34 __cell-0..23 (pool window) + 10 field-named
body-3 row-0 (locked end) 1 2 __cell-0 + __lastUpdated

Crucially vdom == DOM on every check (NL verify_component_consistency): the doubling lives in the App Worker vdom itself — the SSOT is corrupted, so every subsequent diff reconciles against a poisoned baseline. User-visible: duplicated/overlapping cell content, most obvious in the 1-column locked-end region.

The Architectural Reality

Both id schemes are deliberate, coexisting in source at HEAD:

  • Pool scheme: src/grid/Row.mjs:444 + :476 create ${me.id}__cell-${poolIndex}; src/grid/Body.mjs:944 maps columnIndex % me.cellPoolSize; Row.mjs:424 REWRITES oldNode.id in place to the pool id — a live migration step and the prime seam candidate.
  • Field scheme: src/grid/Row.mjs:225 fallback — if (!cellId) cellId = me.getCellId(column.dataField)Body.mjs:922.
  • Body.mjs:1019-1053 (getDataField) is the dual-scheme inverse — the engine expects both schemes to exist, but per cell, not per row.
  • Trigger context: the post-drag redistribution re-render (onColumnLockChange → re-sort/redistribute → body re-renders). The corrupting drag's trace also shows dup-marked double-logged move events (possible double-dispatch) — relevant because the :424 in-place rewrite racing or running twice is exactly the shape that strands a generation.

Hypothesis (to bisect, not assumed): the post-drag re-render emits cells under one scheme while the other generation's nodes are still mounted; the vdom differ matches by id, no id matches across schemes, so neither set evicts the other — both persist.

Evidence-baseline caveat: the live evidence was captured against the operator's dev-server build (post-#12908, pre-#12929-branch aafa9cc8b checkpoint fixes). The bisect must establish behavior on current dev FIRST, then diff against the session build state.

Documented secondary (same family, fold-or-split at fix time): cross-body row-window desync — all three bodies report availableRows: 16 / startIndex: 0 yet mount 18/22/22 rows; each internally consistent, the invariant broken between bodies.

The Fix

Bisect-first, fix-second:

  1. Instrument/bisect when the Row.mjs:225 field-id fallback fires vs the :424 pool rewrite during (a) initial render, (b) post-drag redistribution, (c) horizontal scroll re-render.
  2. Identify which post-drag path renders the second generation over the first's living nodes.
  3. Fix shape (post-bisect): one canonical id scheme per row render pass + a reconciliation guard that evicts the foreign-scheme generation; regression unit test per the custom unit config.

Acceptance Criteria

  • Root cause documented with a deterministic repro (which path emits the second generation, and when).
  • After column drag + redistribution, every row holds exactly one cell per visible column under one id scheme — verified in all three regions.
  • Regression unit test green via the custom unit Playwright config (NOT bare npx playwright test).
  • Secondary row-window desync either resolved by the same fix (with evidence) or split into its own leaf with the captured data.

Out of Scope

  • The overdrag echo-runaway + monotonic scroll-term guard and the dup double-dispatch hunt — @neo-fable's lane (#12929 family; her #12908 follow-up).
  • The view-level region-body DOM tiling (vdom believes 3 bodies, DOM holds up to 17) — hers, evidence on #12929.
  • SortZone landing-index correctness (end from:1 to:27 mis-targeting) — downstream of her echo fix.

Related

  • Parent: #12883 (native sub-link set post-create). Sibling: #12929. Echo lineage: #12906 / #12908.
  • Evidence A2A chain: package fd07a007, addendum c6ac06f4, lane split da99e2e2 (Memory Core, team-readable).
  • NL anchors: session 948c9bba, drag trace zone neo-grid-header-toolbar-sortzone-3 startedAt 1781218587829 (ring buffer preserved), consistency outputs for body-1/2/3__row-0.

Live latest-open sweep: checked latest 20 open issues at 2026-06-11T23:33Z; no equivalent found (#12929 = scrollLeft clamp desync, different layer; #12883 = locked-end re-home, the parent). A2A in-flight sweep: clean — the lane was explicitly handed to me by the parent owner.

Origin Session ID: e6b095bb-4d88-4aeb-a0ec-7d47e0f8f7ce

Retrieval Hint: "grid cell id doubling pool field generation vdom drag corruption devindex"

tobiu referenced in commit c3d3720 - "fix: scrolled-drop re-sync, single-fire lock rebuild, stale-echo gate (#12929, #12932) (#12938) on Jun 12, 2026, 3:55 AM
tobiu referenced in commit 0148c5c - "test(grid): pool-born→permanent one-live-cell invariant net (#12930) (#12948)" on Jun 12, 2026, 5:07 AM
tobiu closed this issue on Jun 12, 2026, 5:07 AM