LearnNewsExamplesServices
Frontmatter
id18047
titleWitness the native titlebar drag of a popup onto another popup
stateClosed
labels
enhancementaitesting
assigneesneo-opus-vega
createdAtSep 1, 2026, 11:38 PM
updatedAtSep 2, 2026, 12:16 AM
githubUrlhttps://github.com/neomjs/neo/issues/18047
authorneo-opus-vega
commentsCount0
parentIssue15239
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 12:16 AM

Witness the native titlebar drag of a popup onto another popup

Closed v13.2.0/archive-v13-2-0-chunk-18 enhancementaitesting
neo-opus-vega
neo-opus-vega commented on Sep 1, 2026, 11:38 PM

Context

#18029 (PR #18040) repaired the native-titlebar popup drag against the main window and left one e2e arm behind, WorkstationNativeTitlebarDragNL. The multi-window continuation — a torn-out popup moved by its OS titlebar onto another torn-out popup — has no witness at all: every popup-over-popup spec (WorkstationHumanPopupOverlapNL) drives the pointer path and calls publishGeometry() by hand. A ten-run diagnostic probe on dev@ff4ccd512b proved the gesture works end to end; the receipts and the rig facts are on Epic #15239 (comment). The operator asked for more e2e coverage of exactly this surface (2026-09-01).

Live latest-open sweep at 2026-09-01T21:23Z (newest #18044) plus keyword sweeps (native titlebar vessel, popup over popup native, titlebar drag transfer vessel): no equivalent. Nearest: #17578 (open, pointer-path coordinator gap), #16123 / #16497 (closed native-return tickets), #18029 (closed, main-window target only).

The Problem

The class of defect #18029 exposed — a working feature silently broken because nothing exercises its trigger — applies with more force to the multi-window path, which has three more moving parts than the main-window path: the vessel participation's claim (workstation-vessel:<item> stable ids), the vessel-target park (parkTearOutVessel with a non-main target route), and the cross-window transferItem commit into the vessel document. None is witnessed on the native path today, so a regression in any of them would ship green.

The Architectural Reality

  • Trigger: Neo.main.addon.WindowPosition publishes on each movement of the source popup (poll owned by observeMovement, Workstation opt-in since #18040); no pointer event exists on this path.
  • Claim: DragCoordinator#onWindowPositionChange (src/manager/DragCoordinator.mjs:1034) → resolveClaimedTarget over the workstation-cross-window sort group; GestureClaimArbiter keeps hover seniority across moves and settles ties lexicographically, so a popup that first claims main keeps main while it still intersects main.
  • Preview: Workspace#renderCrossWindowPreview for a vessel workspace; ensureCrossWindowPreviewGeometry measures asynchronously and nothing re-renders on resolve — the first update over an unmeasured target renders nothing, the next update renders.
  • Terminal: dwell 450 ms / settle 250 ms → commitNativeWindowDrop → suspendNativeWindowDrag → parkTearOutVessel({nativeTitlebar: true}) (non-main target requires targetRoute.capabilities.focus, granted by Main.nativeWindowCapabilities defaults) → onRemoteDrop → commitCrossWindowTransfer → transferItem into workstation-vessel-tabs:<item> → retireNativeWindowDrag closes the source vessel.
  • Rig facts that decide whether a run is evidence: Playwright emulates window.screen at the viewport size (a popup outside the main window is then off the emulated screen and the app's window.moveTo cannot park it — viewport: null restores the real display); Chrome will not shrink the main window below ~500 px wide, so on an 800×600 stage the target sits below a 300 px-tall main window; the pop-outs must happen before the shrink or the tab headers collapse into the overflow menu; the source must reach the target without pausing over main.

The Fix

Add test/playwright/e2e/workstation/WorkstationNativePopupOverPopupNL.spec.mjs (Neural Link fixture, viewport: null), sibling of WorkstationNativeTitlebarDragNL:

  1. Stage guard: test.skip when the real display cannot hold a 760 px main window plus a 240 px target below it, with the reason stated.
  2. Pop out the target (metrics) then the source (commits) via the header action; after each, wait for the detach projection's refresh (main Participation id changes) and for all three participations to be registered.
  3. Shrink the main window to 300 px tall (wait for manager.Window to publish it), place the target vessel fully on-screen below main, outside the main rect.
  4. Assert the source realm's poll is armed by config and no mouseout reaches it; move the source straight onto the target's centre, then nudge twice inside the dwell window.
  5. Assert, in order: the last claim is workstation-vessel:metrics; .neo-dock-preview-affordance count in the target popup ≥ 1 while the main window shows 0; lastVesselParkReceipt.parked === true; lastCrossWindowTransfer is transferItem commits → workstation-vessel:metrics with applied: true; the source vessel retires (tearOutPanes.commits null, popup closed); the target vessel document lists both items; the main document no longer lists commits; no page errors.
  6. Header comment carries the trigger discipline (CDP bounds = window-server stand-in only; no synthetic input; no runtime patching) and the four rig facts.

Decision Record impact

aligned-with ADR 0029 §2.8 (claim protocol, outcome machine, vessel lifecycle) — a witness of landed behaviour, no authority mutation.

Acceptance Criteria

  • The spec exists at the path above, is green three consecutive runs at the PR head on an 800×600-or-larger real display, and skips with a stated reason on a smaller one.
  • It asserts the claim, the target-realm preview, the park receipt, the transferItem commit, and the source retirement — each on a production receipt, none on a synthetic input.
  • No pointer event and no publishGeometry() call reaches either popup realm during the gesture (a mouseout counter in the source realm is asserted at zero).
  • WorkstationNativeTitlebarDragNL and WorkstationHumanPopupOverlapNL are untouched and stay green where they run.
  • The spec header documents the four rig facts so the next author does not re-derive them.

Out of Scope

  • Wiring the Neural Link tier into CI (playwright.config.e2e.mjs gates it on the external Brain runtime; #17844 owns battery runs).
  • A preview on the first hover over an unmeasured target (a design gap in ensureCrossWindowPreviewGeometry, benign under continuous movement; would be its own leaf).
  • Multi-display behaviour (window.moveTo is unpermissioned across displays, per the D#15204 audit).

Avoided Traps

  • Emulating the screen with test.use({screen}) — it did not take effect in this rig and would hide the window.moveTo clamp; viewport: null is the honest stage.
  • Walking the source through the main window in steps — the arbiter's seniority makes main win and a pause there reintegrates into main (the feature, not a bug); the witness moves once, directly.
  • Reading the preview after a single update — the measurement is asynchronous; the witness reads after the second update and inside the dwell window.
  • Any synthetic mouseout or CDP mouse input — it would test the trigger the human gesture never produces (#18029's lesson).

Related

Refs #15239 (multi-window choreography epic, receipts comment) · #18029 / PR #18040 (main-window native path) · #17836 (DockLayouts rewrite) · #15252 (five-beat film) · #17578 (pointer-path coordinator gap)

Origin Session ID: 93fd8de9-7231-4979-b906-c050bcfc25a4

Retrieval Hint: query_raw_memories("native titlebar popup over popup vessel target transferItem probe viewport null 800x600 stage arbiter seniority")