LearnNewsExamplesServices
Frontmatter
id18112
titleA native titlebar drop into the MAIN window never parks: the focus gate needs a user activation the OS drag cannot grant
stateClosed
labels
bugaicoredashboard
assigneesneo-fable
createdAtSep 2, 2026, 5:12 PM
updatedAtSep 2, 2026, 5:58 PM
githubUrlhttps://github.com/neomjs/neo/issues/18112
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 5:58 PM

A native titlebar drop into the MAIN window never parks: the focus gate needs a user activation the OS drag cannot grant

Closed v13.2.0/archive-v13-2-0-chunk-18 bugaicoredashboard
neo-opus-vega
neo-opus-vega commented on Sep 2, 2026, 5:12 PM

Context

Reported by the operator 2026-09-02 on the Workstation: pop a pane out, drag the popup by its OS titlebar over the main window — the drop-zone highlight follows correctly — release: nothing happens. The popup stays, the pane never comes home. Popup-over-popup drops (PR #18059's path) work.

Read live through the Neural Link on the operator's Workstation minutes after the attempt (main session 31120d5a…, the app's own receipts):

  • DragCoordinator.claimTrace: every entry outcome: claimed, claimedStableId: workstation-main, the main window intersects: true, accepts: true, the popup skipped as source. The hover half works; each gesture produced a valid candidate.
  • Workspace.lastVesselParkReceipt: authority.source* all true, targetHasHandle: false / targetFocusCapable: false (informational — the main window has no opener-minted route and the check is exempted for a main target), focused: false, refusedAt: 'focus', no moved key. The park returned at the focus step before ever moving the popup.
  • lastCrossWindowTransfer: null, tearOutPanes: {} after the popup was closed by hand.
  • No console errors or warnings in either realm; nativeGestures: [] afterwards — the gesture ended by itself.

This is the same receipt #18054 read on 2026-09-01. PR #18055 made the refused park a bounded retry (nativeWindowDispositionRetryMs 250 ms doubling, capped at 5 s, nativeWindowParkRetryLimit 6 → about 12.75 s in total) on the premise that focus "starts succeeding the moment the window server lets go". For a main-window target it does not.

The Problem

The native-titlebar terminal is geometry-only by design: no pointer event reaches the page while the OS drags a window, so onWindowPositionChange arms a candidate over a claimed target and commits after dwell (450 ms on the same target) + settle (250 ms without a geometry update). commitNativeWindowDrop then asks the source zone to park the popup strictly — parkTearOutVessel({nativeTitlebar: true}) (apps/workstation/view/Workspace.mjs) — and the park's first step is to give the TARGET window focus, so the parked popup falls behind it in z-order.

For a popup target the focus verb is Neo.Main.windowNativeFocus on the opener's handle — the opener focusing a window it opened, which the platform permits. For a main target the root window has no route, so the verb is Neo.Main.windowFocus({windowId: popup}): the popup's main thread calls window.opener.focus(), under the comment "a popup may focus its opener under the user activation carried by the titlebar gesture" (from the original reintegration, PR #16140). An OS titlebar drag delivers no DOM event to the popup's document — that is the whole reason this path is geometry-driven — so the popup carries no transient user activation, and Chrome declines a cross-window focus() without one. #focusWindow (src/Main.mjs) verifies by polling the target document's hasFocus() six times at 50 ms and truthfully answers false. Releasing the titlebar changes nothing: the popup keeps focus after an OS drag, still without activation. Every retry gets the same false; after the seventh attempt retryNativeWindowPark ends the gesture, the highlight disappears, and the only reintegration a human can reach is closing the popup.

The rigs pass because headless Chromium lets every window claim focus (#focusWindow's own comment notes it): WorkstationNativeTitlebarDragNL moves the popup with CDP and the park's focus succeeds without any activation — the freedom the human path never has.

The Architectural Reality

  • src/manager/DragCoordinator.mjs — onWindowPositionChange (candidate + dwell/settle), commitNativeWindowDrop (phase parking → suspendWindowDrag → on suspended !== true → retryNativeWindowPark), retryNativeWindowPark (backoff, nativeWindowParkRetryLimit), settleNativeWindowDisposition (close/restore retries).
  • apps/workstation/view/Workspace.mjs#parkTearOutVessel — the authority table + receipt; targetIsMain selects Neo.Main.windowFocus for the focus verb; the chain is focus → windowNativeMoveTo to the target frame origin → refocus → parked: true. The move and the later close (retireNativeWindowDrag → windowNativeClose) run from the MAIN realm through the popup's owner-granted handle, which needs no activation — the popup-over-popup path proves both work.
  • src/Main.mjs#windowFocus — documented for the keyboard-command return path ("the popup holds the keystroke's user activation"); the titlebar path reuses it without the activation the doc names.
  • src/dashboard/dock/window/DragTarget.mjs — hooks suspendWindowDrag / onRemoteDrop / onRemoteDropOut; a suspendWindowDrag answer of true is the strict park, false a refusal (retry), anything else non-strict.

The Fix

The focus gate exists so the parked popup sits behind the target during the embodiment handoff. For a main target on the native path that goal has another route: the source is retired (closed) right after the semantic commit anyway, and macOS/Windows return focus to the app's remaining window when the focused popup closes. So:

  1. parkTearOutVessel: when targetIsMain && nativeTitlebar, the focus step and the refocus step become best-effort — attempt them, record focused / refocused truthfully in the receipt, but do not return false on their refusal and do not compensate the move on a refused refocus. The move (windowNativeMoveTo to the target frame origin, from the main realm through the handle) stays the strict gate: it is refused while the OS still drags the window and succeeds the moment the user lets go — the release detector #18054 wanted, on a verb the platform actually grants without activation.
  2. The receipt gains parkAttempts and keeps the last refusedAt, so a stalled retry is readable live (the step #18055 named and did not land).
  3. No change to the popup-target branch (windowNativeFocus on the owner's handle keeps working) or to the coordinator's retry shape.
  4. Witness: a unit arm on parkTearOutVessel with a stubbed Neo.Main whose windowFocus answers false and whose windowNativeMoveTo answers false then true (the OS hold, then the release) — red on dev (returns false at focus, never moves), green at head (moves on the second attempt, parked: true, receipt shows focused: false); a control arm for a popup target where a refused windowNativeFocus still refuses the park. The existing NL specs stay green (they grant focus). The human check stays a Post-Merge item with the operator as owner.

Acceptance Criteria

  • AC-1 A native titlebar drop onto the main window commits once the user releases: the popup parks (moves behind the main frame origin), the proxy embodies, transferItem applies, the popup retires — with no click into the main window required.
  • AC-2 parkTearOutVessel on a main target under nativeTitlebar treats focus/refocus as best-effort and the move as the strict gate; the receipt records focused, refocused, parkAttempts, refusedAt truthfully. Red-first unit arm on dev.
  • AC-3 A popup target still refuses the park when windowNativeFocus is refused (control arm).
  • AC-4 WorkstationNativeTitlebarDragNL and WorkstationNativePopupOverPopupNL green under the Brain root; unit/manager green.
  • AC-5 Operator hand check on the Workstation: drag a popup over main by its titlebar, release, no further input — the pane comes home within the retry window. Post-Merge item, operator-owned.

Out of Scope

  • The keyboard-command focus return path (windowFocus under a keystroke's activation) — unchanged.
  • Pointer-path cross-window drags (#17578).
  • Detecting the OS release by any other signal; the move's success remains the detector.
  • The spring-loaded dwell interaction the operator chose on 2026-09-02 (the drop area fills over the dwell, the transfer commits on completion, the popup closes after — no park at all for main targets): its own ticket, layered on top of this fix.

Avoided Traps

  • Reading a green rig as proof of the human path. #focusWindow documents that headless platforms let every window claim focus; the two NL specs pass for exactly that reason. The witness for this ticket stubs the refusal instead of trusting the platform, and the human check stays owned.
  • Widening the retry window. More retries of a verb the platform never grants only delay the silent end; the fix moves the gate to a verb that IS granted on release.
  • Skipping the move gate too. Parking while the OS still holds the window is what #18054 measured as the first failure mode; the move must stay strict so the popup is parked only once it is free.

Related

  • #18054 / PR #18055 — the retry (Mnemosyne); this ticket is the case its premise excluded.
  • PR #16140 — the original reintegration and the windowFocus main-target branch (Euclid).
  • #18059 — popup-over-popup native drop (works: the owner focuses a window it opened).
  • #17578 — the pointer-path coordinator defect (different pipeline).

Live latest-open sweep: checked the latest open issues at 2026-09-02T15:14Z; keyword sweeps native park focus main window handle, titlebar drop opener focus activation, popup main window drop native — no equivalent open ticket (#18054 is closed by #18055).

Origin Session ID: 87c91db1-7fcd-4987-9932-2bf78d3dda25

Retrieval Hint: "native titlebar drop main window focus gate user activation park refused opener.focus"

tobiu referenced in commit 99b5be2 - "fix(workstation): a native titlebar drop onto the main window parks on the move, not on a focus the platform never grants (#18112) (#18114) on Sep 2, 2026, 5:58 PM
tobiu closed this issue on Sep 2, 2026, 5:58 PM