Context
Found while building the binding §06 gesture witness for PR #14974 (2026-07-10). Two related engine-tier gaps in the drag layer's lifecycle, both bisected against the live dockdemo surface with a scripted-pointer reproducer.
The Problem
Gap 1 — the early-gesture deafness race. A real pointer drag started within ~1s of app mount renders a drag proxy and the neo-is-dragging state (main-thread DragDrop addon — instant), but no worker-side sort zone ever hears the gesture: the zones' delegated drag:start / drag:move DOM-listener registration round-trip (worker → main) has not landed when the gesture begins. Consequences on the dock surfaces: no dockCrossZoneDragMove / dockCrossZoneDrop fire, so the drop-indicator menu never lights and the release commits nothing — the drag LOOKS alive and is functionally dead.
Bisected cleanly (fixture probe, identical flow): pointer-down at +0ms after readiness selectors → deaf (subclass onDragMove never runs — verified via an instance-field trail read over the Neural Link, since SharedWorker consoles don't bubble to the page); pointer-down at +1000ms → everything fires. The .neo-draggable cls is NOT a sufficient readiness witness — it lands before the listener registration completes.
Gap 2 — no gesture-time key capture. During a header drag, the base sort zone placeholder-swaps the dragged button out of the DOM; browser focus falls to <body>, so element-scoped keydown listeners (the only kind a workspace can register) can never receive a real mid-drag Escape. The §06 Escape-cancel seam (PR #14974) is therefore drivable and unit/e2e-proven at the seam tier, but a REAL keystroke has no path to it. The same family as the known no-abort-API residual (a cancelled drag's proxy visual rides until release).
The Architectural Reality
- Main-thread
DragDrop addon owns proxy/visuals and works from frame one; worker zones subscribe via addDomListeners (async round-trip) — the two halves have no shared readiness contract.
DockTabSortZone fires the dock seams from onDragStart/Move/End — all downstream of the deaf registration.
- No
drag:cancel primitive exists anywhere in the stack (grep-verified), and no window-scoped key listener surface is available to worker components during a gesture.
The Fix
Investigation-first, two candidate shapes:
- Readiness: either the main-thread addon defers gesture-start until the worker's drag listeners for the pressed element's path are registered (a registration-complete handshake), or a queryable/awaitable readiness signal is exposed so surfaces (and specs) can gate honestly. Today's only workaround is a blind settle (documented in
test/playwright/e2e/dashboard/DemoADragMenuNL.spec.mjs).
- Gesture-time input capture: while a drag session is active, the main-thread drag owner captures
keydown (at minimum Escape) and routes it into the drag event stream (drag:cancel or a keyed field on drag:move/drag:end) — giving every drop consumer one honest cancel primitive and retiring the per-workspace Escape workarounds.
Acceptance Criteria
Out of Scope
- The dock surfaces' consumer logic (PR
#14974) — correct once events arrive.
- The
#14970 overlay cascade defect (separate, PR #14975 in flight).
Decision Record impact
none — engine defect investigation; no ADR authority touched.
Related: #14974 (the witness that found both) · #13158 · #14923 (drag-lifecycle destroy family) · #14970 (sibling finding from the same gate lineage).
Live latest-open sweep: latest 12 checked at 2026-07-10T18:12Z; no equivalent (nearest #14970 = CSS cascade, disjoint). A2A herd window checked: no competing claim.
Origin Session ID: 183ac080-2f24-4837-8f63-69bfc536af0d
Retrieval Hint: "early gesture drag listener registration race worker deaf escape key capture drag cancel"
Context
Found while building the binding §06 gesture witness for PR
#14974(2026-07-10). Two related engine-tier gaps in the drag layer's lifecycle, both bisected against the live dockdemo surface with a scripted-pointer reproducer.The Problem
Gap 1 — the early-gesture deafness race. A real pointer drag started within ~1s of app mount renders a drag proxy and the
neo-is-draggingstate (main-threadDragDropaddon — instant), but no worker-side sort zone ever hears the gesture: the zones' delegateddrag:start/drag:moveDOM-listener registration round-trip (worker → main) has not landed when the gesture begins. Consequences on the dock surfaces: nodockCrossZoneDragMove/dockCrossZoneDropfire, so the drop-indicator menu never lights and the release commits nothing — the drag LOOKS alive and is functionally dead.Bisected cleanly (fixture probe, identical flow): pointer-down at +0ms after readiness selectors → deaf (subclass
onDragMovenever runs — verified via an instance-field trail read over the Neural Link, since SharedWorker consoles don't bubble to the page); pointer-down at +1000ms → everything fires. The.neo-draggablecls is NOT a sufficient readiness witness — it lands before the listener registration completes.Gap 2 — no gesture-time key capture. During a header drag, the base sort zone placeholder-swaps the dragged button out of the DOM; browser focus falls to
<body>, so element-scopedkeydownlisteners (the only kind a workspace can register) can never receive a real mid-drag Escape. The §06 Escape-cancel seam (PR#14974) is therefore drivable and unit/e2e-proven at the seam tier, but a REAL keystroke has no path to it. The same family as the known no-abort-API residual (a cancelled drag's proxy visual rides until release).The Architectural Reality
DragDropaddon owns proxy/visuals and works from frame one; worker zones subscribe viaaddDomListeners(async round-trip) — the two halves have no shared readiness contract.DockTabSortZonefires the dock seams fromonDragStart/Move/End— all downstream of the deaf registration.drag:cancelprimitive exists anywhere in the stack (grep-verified), and no window-scoped key listener surface is available to worker components during a gesture.The Fix
Investigation-first, two candidate shapes:
test/playwright/e2e/dashboard/DemoADragMenuNL.spec.mjs).keydown(at minimum Escape) and routes it into the drag event stream (drag:cancelor a keyed field ondrag:move/drag:end) — giving every drop consumer one honest cancel primitive and retiring the per-workspace Escape workarounds.Acceptance Criteria
DemoADragMenuNL.spec.mjsis removed in favor of the readiness contract.Out of Scope
#14974) — correct once events arrive.#14970overlay cascade defect (separate, PR#14975in flight).Decision Record impact
none— engine defect investigation; no ADR authority touched.Related:
#14974(the witness that found both) ·#13158·#14923(drag-lifecycle destroy family) ·#14970(sibling finding from the same gate lineage).Live latest-open sweep: latest 12 checked at 2026-07-10T18:12Z; no equivalent (nearest
#14970= CSS cascade, disjoint). A2A herd window checked: no competing claim.Origin Session ID: 183ac080-2f24-4837-8f63-69bfc536af0d Retrieval Hint: "early gesture drag listener registration race worker deaf escape key capture drag cancel"