Context
test/playwright/e2e/grid/KeyboardNavigationScroll.spec.mjs (the arm #18871 / PR #18873 introduced: arrowing right past the visible columns and back left keeps the selected cell rendered and visible) has now gone red on CI twice with the same shape, with no test or source change that explains it:
| when |
where |
observed |
| 2026-09-18 |
a CI shard of the cell-editing day (recorded on #18910 and #18919 as "tracked separately" — it never was, this ticket is that follow-up) |
the first poll saw {inside: false, text: 'r3c15'}; locally under --workers=10 --repeat-each=30 the arm ended STUCK on {inside: false, text: 'r3c12'} once in 30; a state-dumping probe over 130 further runs never reproduced it. Unclassified. |
| 2026-10-09 |
PR #19509 at c3e55797840904a3c644ecc20334ae83779aa452, run 37958019884 attempt 1, job 113913447127 (@neo-gpt-sophie's receipt: https://github.com/neomjs/neo/pull/19509#issuecomment-6084797298) |
line 61: expected {inside: true, text: 'r3c3'}, got {inside: false, text: 'r3c12'}; the trace records twelve ArrowRight then twelve ArrowLeft; the right terminal r3c15 was correct, the left terminal stayed r3c12 through the whole 5 s poll; the final grid View is active; 69 other tests of the shard passed; the arm is 5/5 locally at that head and at the preceding 828712fc5f (Node 24.19, headless branded Chrome, macOS); the requested rerun went green. Narrowed (Sophie, the retained trace's frame snapshots, https://github.com/neomjs/neo/issues/19515#issuecomment-6085142673): the terminal DOM holds TWO selected cells — pooled slot __row-2__cell-0 = c12 (neo-selected, aria-selected="true", left 1650 px; stale since the ArrowLeft at call 1850, where c12 and c11 were both selected) and slot __row-2__cell-2 = c3 (selected, left 300 px) — with the body's --grid-scroll-left ended at 300 px (down from 1150 px). The spec's first-match read returned the stale c12: "the navigation never reached c3" is disproved for this occurrence, which points at the second producer below (a stale projection); whether the extra class originates in the row's vdom, its adopted vnode or Main's applied deltas is open — the trace carries no worker-side state. |
Sophie retains today's trace (artifact 11630205953) and a diagnostic witness. A green rerun is not causal proof; neither occurrence captured worker-side selection state, so the producer is OPEN, not classified. Her bounded probes at c3e5579784 (terminal-state dumps 30/30, CPU throttling ×4 15/15, a controlled late-selection race 8/8, a scroll/recycling crossing 2/2 — receipts in the comment above) did not reproduce the failure and prove nothing harmless.
The Problem
Three producers fit both sightings and nothing in the recorded evidence tells them apart:
- lost input — keydowns dropped or coalesced under shard load (three ArrowLeft of twelve would leave c15 → c12);
- stale selection projection — the model is on c3 but a
.neo-selected class still sits on a pooled cell: the spec's selectedCell() reads querySelector of the selected-cell selector, the first match in DOM order, and pooled cells are not in column order;
- a skipped scroll —
scrollByColumns declines because body.scrollLeft trails the requested scroll (the unmeasured note on #18873), so the selected cell is correct in the model but outside the visible width.
The October occurrence is narrowed toward the second (the two-selected-cells terminal DOM above); the September occurrence remains unclassified against all three. Only the third is a scroll defect; the first is an input-path defect; the second is a projection defect that any keyboard user of a pooled grid would see as a wrong highlight. The arm's assertion is the user-facing contract from #18871 and must stay as it is — no per-key waits to green it, and no read that picks the other selected node.
The Architectural Reality
Neo.selection.grid.CellModel#onNavKeyColumn is purely logical (model and selection only). Neo.grid.Container#scrollByColumns (rewritten geometrically in #18873: visible width = container width minus the scrollbar bands, position by dataField, locked columns skipped) issues the scroll; Neo.grid.Body#afterSetScrollLeft recomputes the column window; the body pools cells across scrolls, so the DOM order of cells is the pool's, not the columns'. The spec's selectedCell(page) and scrollLeft(page) helpers read the DOM in the real browser; the trace carries every key event and the final DOM, no worker state.
Design authority: not applicable — a red assertion against the arm's own contract, no behavior is called wrong here.
The Fix
- Reproduce under the same conditions (
--workers=10 --repeat-each=30, the full grid e2e shard beside it) with a worker-state probe aligned to the complete arrow sequence: selectionModel.items, the selected row's vdom, the adopted vnode, and Main's selected-cell classes — collected inside the event path or asynchronously at the terminal, never by awaiting a worker read after each key (that changes the timing under diagnosis); a per-key scrollLeft read beside it separates producer 3 from the others.
- Classify (lost input / stale projection / skipped scroll) from the fingerprint, then repair in the owning layer: the key path, the selection projection's class bookkeeping on pooled cells, or
scrollByColumns' read of the current scroll position.
- Keep the arm; add the producer's own arm so the mechanism cannot return silently.
Acceptance Criteria
Out of Scope
The cell-editing arms and the text-field trailing-space defect (#18910, fixed); any per-key wait or retry inside the arm.
Related
#18871 / #18873 (the arm's origin and the scrollByColumns rewrite), #18910, #18919 (the September sightings), #19509 (today's run), #18880 (the earlier flake-hardening on the same fixture), #19513 (Ada's emitted-root partition — a candidate relationship, a hypothesis until a failing producer is isolated).
handoff: @neo-gpt-sophie (holds today's trace and the diagnostic witness; filed by @neo-fable at her request with the September rows; the October narrowing folded in at her request).
Live latest-open sweep: checked latest 20 open issues at 2026-10-09T16:32:13Z; no equivalent found. A2A claim sweep: recent lane-claims read, none on this scope. MC sweep: "KeyboardNavigationScroll ends stuck on c12 outside the visible width, expected r3c3" — 4 results, my 2026-09-18 investigation (the three producers, the 130-run non-reproduction) is the prior art, no prior decision. Own-assignment sweep: 2 open, none overlapping. Tracker search "KeyboardNavigationScroll": no open or closed issue names the spec.
Origin Session ID: 2ea2911e-ebbd-49be-9471-3e77369ca2b5
Retrieval Hint: "KeyboardNavigationScroll r3c12 stuck ArrowLeft pooled cell selected class scrollByColumns"
Context
test/playwright/e2e/grid/KeyboardNavigationScroll.spec.mjs(the arm #18871 / PR #18873 introduced: arrowing right past the visible columns and back left keeps the selected cell rendered and visible) has now gone red on CI twice with the same shape, with no test or source change that explains it:{inside: false, text: 'r3c15'}; locally under--workers=10 --repeat-each=30the arm ended STUCK on{inside: false, text: 'r3c12'}once in 30; a state-dumping probe over 130 further runs never reproduced it. Unclassified.c3e55797840904a3c644ecc20334ae83779aa452, run 37958019884 attempt 1, job 113913447127 (@neo-gpt-sophie's receipt: https://github.com/neomjs/neo/pull/19509#issuecomment-6084797298){inside: true, text: 'r3c3'}, got{inside: false, text: 'r3c12'}; the trace records twelve ArrowRight then twelve ArrowLeft; the right terminalr3c15was correct, the left terminal stayedr3c12through the whole 5 s poll; the final grid View is active; 69 other tests of the shard passed; the arm is 5/5 locally at that head and at the preceding 828712fc5f (Node 24.19, headless branded Chrome, macOS); the requested rerun went green. Narrowed (Sophie, the retained trace's frame snapshots, https://github.com/neomjs/neo/issues/19515#issuecomment-6085142673): the terminal DOM holds TWO selected cells — pooled slot__row-2__cell-0= c12 (neo-selected,aria-selected="true", left 1650 px; stale since the ArrowLeft at call 1850, where c12 and c11 were both selected) and slot__row-2__cell-2= c3 (selected, left 300 px) — with the body's--grid-scroll-leftended at 300 px (down from 1150 px). The spec's first-match read returned the stale c12: "the navigation never reached c3" is disproved for this occurrence, which points at the second producer below (a stale projection); whether the extra class originates in the row's vdom, its adopted vnode or Main's applied deltas is open — the trace carries no worker-side state.Sophie retains today's trace (artifact 11630205953) and a diagnostic witness. A green rerun is not causal proof; neither occurrence captured worker-side selection state, so the producer is OPEN, not classified. Her bounded probes at
c3e5579784(terminal-state dumps 30/30, CPU throttling ×4 15/15, a controlled late-selection race 8/8, a scroll/recycling crossing 2/2 — receipts in the comment above) did not reproduce the failure and prove nothing harmless.The Problem
Three producers fit both sightings and nothing in the recorded evidence tells them apart:
.neo-selectedclass still sits on a pooled cell: the spec'sselectedCell()readsquerySelectorof the selected-cell selector, the first match in DOM order, and pooled cells are not in column order;scrollByColumnsdeclines becausebody.scrollLefttrails the requested scroll (the unmeasured note on #18873), so the selected cell is correct in the model but outside the visible width.The October occurrence is narrowed toward the second (the two-selected-cells terminal DOM above); the September occurrence remains unclassified against all three. Only the third is a scroll defect; the first is an input-path defect; the second is a projection defect that any keyboard user of a pooled grid would see as a wrong highlight. The arm's assertion is the user-facing contract from #18871 and must stay as it is — no per-key waits to green it, and no read that picks the other selected node.
The Architectural Reality
Neo.selection.grid.CellModel#onNavKeyColumnis purely logical (model and selection only).Neo.grid.Container#scrollByColumns(rewritten geometrically in #18873: visible width = container width minus the scrollbar bands, position bydataField, locked columns skipped) issues the scroll;Neo.grid.Body#afterSetScrollLeftrecomputes the column window; the body pools cells across scrolls, so the DOM order of cells is the pool's, not the columns'. The spec'sselectedCell(page)andscrollLeft(page)helpers read the DOM in the real browser; the trace carries every key event and the final DOM, no worker state.Design authority: not applicable — a red assertion against the arm's own contract, no behavior is called wrong here.
The Fix
--workers=10 --repeat-each=30, the full grid e2e shard beside it) with a worker-state probe aligned to the complete arrow sequence:selectionModel.items, the selected row's vdom, the adopted vnode, and Main's selected-cell classes — collected inside the event path or asynchronously at the terminal, never by awaiting a worker read after each key (that changes the timing under diagnosis); a per-keyscrollLeftread beside it separates producer 3 from the others.scrollByColumns' read of the current scroll position.Acceptance Criteria
KeyboardNavigationScroll.spec.mjsunchanged in its assertions, green 30/30 locally under--workers=10 --repeat-each=30and on the next three CI runs touching the grid shard.Out of Scope
The cell-editing arms and the text-field trailing-space defect (#18910, fixed); any per-key wait or retry inside the arm.
Related
#18871 / #18873 (the arm's origin and the scrollByColumns rewrite), #18910, #18919 (the September sightings), #19509 (today's run), #18880 (the earlier flake-hardening on the same fixture), #19513 (Ada's emitted-root partition — a candidate relationship, a hypothesis until a failing producer is isolated).
handoff: @neo-gpt-sophie (holds today's trace and the diagnostic witness; filed by @neo-fable at her request with the September rows; the October narrowing folded in at her request).
Live latest-open sweep: checked latest 20 open issues at 2026-10-09T16:32:13Z; no equivalent found. A2A claim sweep: recent lane-claims read, none on this scope. MC sweep: "KeyboardNavigationScroll ends stuck on c12 outside the visible width, expected r3c3" — 4 results, my 2026-09-18 investigation (the three producers, the 130-run non-reproduction) is the prior art, no prior decision. Own-assignment sweep: 2 open, none overlapping. Tracker search "KeyboardNavigationScroll": no open or closed issue names the spec.
Origin Session ID: 2ea2911e-ebbd-49be-9471-3e77369ca2b5 Retrieval Hint: "KeyboardNavigationScroll r3c12 stuck ArrowLeft pooled cell selected class scrollByColumns"