Context
Leaf of #18762, split out of #18964 by its own author before that PR opened, on the #18863 → #18927 pattern: the ACs a PR cannot honestly tick move to a new leaf rather than being softened to fit it.
#18964 fixes grid.Container#cellEditing so the config governs its plugin in both directions — off disables, on reuses the same instance. Its unit arms pin the plugin lifecycle and the refusal (startEdit returns false while disabled). They cannot pin what a user sees, and that is this ticket.
The Problem
Two of #18964's acceptance criteria describe a live edit session, not a plugin's lifecycle:
- a double-click after an off→on cycle must open exactly one editor;
- an edit that is already open when
cellEditing goes false must be cancelled, not committed — draft discarded, record unchanged.
Both need an editor that actually mounts. In the single-thread unit harness it cannot: creating a session drives form.field.Text into registerPreventDefaultKeys / unregisterPreventDefaultKeys, which are Neo.main.DomEvents calls with no main thread behind them. Measured — the arm's own assertions passed and teardown then threw TypeError: Cannot read properties of undefined (reading 'registerPreventDefaultKeys') from plugin.destroy.
That is a statement about the tier, not about the product. The behaviour is reachable; only the unit harness cannot reach it.
The Architectural Reality
test/playwright/component/apps/grid-cell-editing is the fixture the epic's other leaves drive — real editors mount there, and #18959/#18960 have just made its editor-drag arm deterministic. src/grid/Container.mjs#afterSetCellEditing (post-#18964) routes cellEditing to plugin.disabled, and grid.plugin.CellEditing#afterSetDisabled (:65-71) cancels an open edit with reason 'disabled'. So the production path for AC-2 below already exists and is unwitnessed.
The fixture's grid is constructed with editing already on, so the arm has to reach cellEditing at runtime — the same surface #18964 fixed.
The Fix
Two arms on the existing grid-cell-editing fixture. No source change is expected; if one turns out to be needed, that is a finding for this ticket rather than a reason to widen #18964.
Acceptance Criteria
- AC-1 On the fixture, toggling
cellEditing off and on again and then double-clicking an editable cell opens exactly one editor — asserted by counting mounted editors in the cell, not by counting plugin instances (#18964's unit arms already own the instance count).
- AC-2 An edit open when
cellEditing goes false is cancelled: the editor leaves, the record keeps its pre-edit value, and a cellEditCancel with reason 'disabled' is observed. A commit would write the draft, so the arm must distinguish the two rather than assert only that the editor is gone.
- AC-3 Both arms are mutation-proven against #18964's fix: reverting
afterSetCellEditing's off branch reddens AC-2; reverting its reuse branch reddens AC-1.
Out of Scope
afterSetCellEditing itself — delivered by #18964. The plugin's disabled semantics, which are existing behaviour this ticket only witnesses. Anything about a first runtime enable, which is @neo-fable's still-open defect-note of 2026-09-19 13:22Z.
Avoided Traps
- Do not re-assert the plugin instance count here. #18964's unit arms own it; this ticket exists precisely because a count is not what a user feels.
- Do not assert "no editor" for AC-2 and stop. A commit also removes the editor. The record's value is what separates cancel from commit.
- Do not move these arms back to the unit tier. The
registerPreventDefaultKeys failure above is why they are here; a unit arm would have to stub the main thread and would then be testing the stub.
Related
- #18964 — the fix these arms witness; this ticket carries the two ACs its PR could not honestly tick.
- #18762 — parent epic; its "one editor at a time" predicate is what AC-1 protects.
- #18959 / #18960 — recent determinism work on the same fixture.
Handoff Retrieval Hints
Retrieval Hint: "grid cellEditing runtime toggle fixture arms one editor cancel on disable" · fixture test/playwright/component/apps/grid-cell-editing · source anchors src/grid/Container.mjs#afterSetCellEditing, src/grid/plugin/CellEditing.mjs:65-71.
Context
Leaf of #18762, split out of #18964 by its own author before that PR opened, on the #18863 → #18927 pattern: the ACs a PR cannot honestly tick move to a new leaf rather than being softened to fit it.
#18964 fixes
grid.Container#cellEditingso the config governs its plugin in both directions — off disables, on reuses the same instance. Its unit arms pin the plugin lifecycle and the refusal (startEditreturnsfalsewhile disabled). They cannot pin what a user sees, and that is this ticket.The Problem
Two of #18964's acceptance criteria describe a live edit session, not a plugin's lifecycle:
cellEditinggoesfalsemust be cancelled, not committed — draft discarded, record unchanged.Both need an editor that actually mounts. In the single-thread unit harness it cannot: creating a session drives
form.field.TextintoregisterPreventDefaultKeys/unregisterPreventDefaultKeys, which areNeo.main.DomEventscalls with no main thread behind them. Measured — the arm's own assertions passed and teardown then threwTypeError: Cannot read properties of undefined (reading 'registerPreventDefaultKeys')fromplugin.destroy.That is a statement about the tier, not about the product. The behaviour is reachable; only the unit harness cannot reach it.
The Architectural Reality
test/playwright/component/apps/grid-cell-editingis the fixture the epic's other leaves drive — real editors mount there, and #18959/#18960 have just made its editor-drag arm deterministic.src/grid/Container.mjs#afterSetCellEditing(post-#18964) routescellEditingtoplugin.disabled, andgrid.plugin.CellEditing#afterSetDisabled(:65-71) cancels an open edit with reason'disabled'. So the production path for AC-2 below already exists and is unwitnessed.The fixture's grid is constructed with editing already on, so the arm has to reach
cellEditingat runtime — the same surface #18964 fixed.The Fix
Two arms on the existing
grid-cell-editingfixture. No source change is expected; if one turns out to be needed, that is a finding for this ticket rather than a reason to widen #18964.Acceptance Criteria
cellEditingoff and on again and then double-clicking an editable cell opens exactly one editor — asserted by counting mounted editors in the cell, not by counting plugin instances (#18964's unit arms already own the instance count).cellEditinggoesfalseis cancelled: the editor leaves, the record keeps its pre-edit value, and acellEditCancelwith reason'disabled'is observed. A commit would write the draft, so the arm must distinguish the two rather than assert only that the editor is gone.afterSetCellEditing's off branch reddens AC-2; reverting its reuse branch reddens AC-1.Out of Scope
afterSetCellEditingitself — delivered by #18964. The plugin'sdisabledsemantics, which are existing behaviour this ticket only witnesses. Anything about a first runtime enable, which is @neo-fable's still-open defect-note of 2026-09-19 13:22Z.Avoided Traps
registerPreventDefaultKeysfailure above is why they are here; a unit arm would have to stub the main thread and would then be testing the stub.Related
Handoff Retrieval Hints
Retrieval Hint: "grid cellEditing runtime toggle fixture arms one editor cancel on disable"· fixturetest/playwright/component/apps/grid-cell-editing· source anchorssrc/grid/Container.mjs#afterSetCellEditing,src/grid/plugin/CellEditing.mjs:65-71.