LearnNewsExamplesServices
Frontmatter
id18863
titleReprojection focus, pointer order and invalid drafts in grid editing
stateClosed
labels
bugaigrid
assigneesneo-opus-vega
createdAtSep 18, 2026, 1:36 PM
updatedAtSep 18, 2026, 7:17 PM
githubUrlhttps://github.com/neomjs/neo/issues/18863
authorneo-opus-grace
commentsCount0
parentIssue18762
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 18866 The Grids guide teaches none of the cell editing contract
closedAtSep 18, 2026, 7:17 PM

Reprojection focus, pointer order and invalid drafts in grid editing

neo-opus-grace
neo-opus-grace commented on Sep 18, 2026, 1:36 PM

Context

Leaf of #18762, slice 2 (interaction contract). The slice's remaining terminals: where focus goes on reprojection, the order of events when the pointer activates a cell, and what an invalid draft does.

terminal state today
focus on reprojection red, measured by @neo-fable in #18843: a record sorted or filtered out of the row pool and back reprojects with its draft, but focus is on grid.View, not the editor
pointer ordering the simple case works (the #18762 evaluation round: a click on another cell commits, then selects it). The plugin does not consume GestureClaimArbiter, which D#17832 names as the ordering primitive
invalid draft unmeasured
double-click activation measured by @neo-fable after filing: the two clicks toggle the CellModel selection on and off (no selected cell, and no selected row under the row models), so after the commit focus is on grid.View with no anchor cell for the arrow keys. Enter and F2 activation keep the selection

The Problem

D#17832 OQ5 settled them:

  • DOM focus returns on reprojection only while the session's generation still owns it.
  • Activating another cell first resolves the current session's terminal, then processes the new target, through GestureClaimArbiter rather than a second gesture-claim protocol.
  • An invalid draft stays: no leave-edit gesture ends it.

The Architectural Reality

  • The session is {recordId, dataField, editor} (src/grid/plugin/CellEditing.mjs). Reprojection is the next Row render of the proving cell, and isEmbodied reads the row pool and the column window.
  • src/manager/GestureClaimArbiter.mjs is the engine's per-gesture claim arbiter.
  • #18843's store driver moves a record out of the pool and back without taking focus. Its recipe is reusable here.

The Fix

  • On reprojection, focus the editor when focus was in it at suspension and no newer intent has moved it.
  • Route the pointer's activate-another-cell sequence through GestureClaimArbiter, or record in the PR why the current order is already the arbiter's.
  • Witness the invalid-draft terminal. Fix it if it does not hold.

Acceptance Criteria

Split 2026-09-18 (author): the reprojection-focus and click-away-order ACs moved, word for word, to #18927. PR #18912 delivers the two below, so it resolves this ticket as narrowed. Same shape as #18861 / #18883.

  • After a double-click edit commits, the edited cell is selected, so the arrow keys have an anchor.
  • An invalid draft keeps the editor through Enter, Tab, a click away and focus leaving the grid.
  • Each arm is killed by a named mutation recorded in the PR.

Out of Scope

  • Tab traversal and select-on-activation (the leaves beside this one).
  • The picker island (#18838).
  • A removed record ending the session at removal (noted in #18843, undecided).

Related

#18762 · D#17832 (OQ5) · #18843 (the reprojection-focus measurement and the store driver) · #18838

Live latest-open sweep: latest 20 open issues read at 2026-09-18T11:32:01Z, plus an all-state title search (grid editor Tab / select-all / aria-readonly / IME / Grids guide / locked cell editing): no equivalent; the nearest are #18838, #18843 and the closed #18764 / #18837. A2A sweep: latest 30 messages, no claim on this scope. Memory Core sweep, on the problem's nouns (Tab dead key, select on activation, aria-readonly, IME, locked columns): the evaluation-round and slice-1 trails only, no prior decision against it. Own-assignment sweep: none on grid editing.

unowned-rationale: filed by the epic's steward (me) from the Stage 3 remainders of @neo-opus-ada's epic review and @neo-fable's evaluation round, so any seat can claim it. I am on the menu lane (#18846–#18848) and am not taking it now.

Origin Session ID: 0a997d38-ccdb-4352-ac21-566606a6ab2d