LearnNewsExamplesServices
Frontmatter
id19841
titleA popped-out vessel docks back into main when the pointer enters it
stateClosed
labels
bugaidashboard
assigneesneo-opus-grace
createdAtOct 17, 2026, 10:51 PM
updatedAtOct 17, 2026, 11:58 PM
githubUrlhttps://github.com/neomjs/neo/issues/19841
authorneo-opus-grace
commentsCount0
parentIssue15239
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 17, 2026, 11:57 PM

A popped-out vessel docks back into main when the pointer enters it

Closed Backlog/active-chunk-9 bugaidashboard
neo-opus-grace
neo-opus-grace commented on Oct 17, 2026, 10:51 PM

Context

This surfaced while I was clearing the one Brain-gated red on #19840. On a hosted runner, WorkstationPopOutHoverSyncNL.spec.mjs:90 failed both attempts with page.evaluate: Target page, context or browser has been closed: the popup closed under the test about 2.4 s after it connected. The spec passes on dev's runs and locally because it finishes in about 1.3 s. Six arms in BRAIN_QUARANTINES (buildScripts/util/e2eCiSelection.mjs) are marked "hosted only" or "hosted, intermittent". One of them, WorkstationPopOutSparklinesNL, names the same symptom: "the popup closes while a sparkline cell is being captured".

Observed. Setup: headless chrome channel, local, Engine dev 44534e3128, Brain dev afae1ef3. I used an untracked probe copy of the HoverSync arm that holds the popup open instead of closing it.

  • Pointer moved into the popup: 10 of 10 runs closed it. The popup's window.close() ran about 2.0 s after the pop-out click unthrottled (6 runs) and about 3 s at 4× CPU throttle (4 runs).
    • Main-thread stack: Main.windowNativeClose ← RemoteMethodAccess.onRemoteMethod.
    • App Worker stack, from a hot-patched NativeLifecycle#retire: Workspace.retireReturnedVessel ← Workspace.commitCrossWindowTransfer ← DragCoordinator.commitNativeWindowDrop.
    • Receipt: lastTearOutClose {itemId: 'feed', stage: 'acknowledged', dispatch: 'native', closed: true}. A wrapped TearOut#retireVessel was never called.
  • No pointer in the popup: 2 of 2 runs kept it open for 4 s, and nothing retired.
  • The popup never moved. From its first sample until the close, its own screenX/screenY read (34, 749) at 1576×240. Main sits at (22, 22), 1600×900, so the vessel opens over main where the pane was.

Inference: the engine committed a native-titlebar drop that nobody performed.

The Problem

The vessel's first pointermove is not a move, but it publishes geometry:

  1. WindowPosition#armViewportProbe takes one pointer sample to measure where the viewport starts inside the frame.
  2. neoViewportOffset starts unset, so that first sample calls publishGeometry().
  3. The publication carries no nativeEffect.

DragCoordinator#onWindowPositionChange reads any untagged publication from a vessel the workspace owns as a title-bar move:

  1. defaultResolveNativeWindowDrag resolves the vessel as a drag source on ownership alone.
  2. The candidate finds main's dock under the vessel, because the vessel opened at the pane's own rect.
  3. Dwell (1200 ms), settle (250 ms) and handoff (180 ms) run out.
  4. commitNativeWindowDrop returns the pane to main and closes the vessel.

So a pane popped out with the header action docks back into main about 1.6 s after the pointer first enters its window. The e2e tier reads this as hosted flake, because an arm passes whenever it finishes inside that window. The behavior is witnessed headless. Headed should behave the same by construction (same probe, same coordinator, and the vessel opens over main by design), but nobody has witnessed it headed yet.

The Architectural Reality

  • src/main/addon/WindowPosition.mjs:
    • armViewportProbe takes one pointermove sample into win.neoViewportOffset, then calls publishGeometry() when the offset is new.
    • onResize publishes a fixed-origin resize the same way.
  • src/worker/App.mjs onWindowPositionChange passes one payload to Neo.manager.Window, then to Neo.manager.DragCoordinator.
  • src/manager/DragCoordinator.mjs:
    • onWindowPositionChange returns early on nativeEffect. Otherwise it resolves a source through getNativeWindowDragSource and starts or re-arms a candidate.
    • The timers are nativeWindowDropDwellMs: 1200, nativeWindowDropSettleMs: 250 and nativeWindowDropHandoffMs: 180, followed by commitNativeWindowDrop.
  • src/dashboard/dock/window/Participation.mjs defaultResolveNativeWindowDrag: any owned vessel window is a drag source. Nothing asks whether the window moved.
  • apps/workstation/view/VesselWorkspace.mjs: commitCrossWindowTransfer → retireReturnedVessel → NativeLifecycle#retire → closeTearOutVessel → NativeVesselTransaction.closeVessel → Main.windowNativeClose.

Design authority:

  • DragCoordinator#onWindowPositionChange: "Consumes high-frequency window geometry updates for native OS-titlebar popup drags, where the browser does not emit pointer move/up events."
  • WindowPosition#publishGeometry: "Advancing this baseline prevents the poll from echoing that same position as an untagged user move."
  • ADR-0029's native title-bar path (#13028) is for a popup dragged by its title bar.

A publication that does not move the window is none of these.

Prior art: Mnemosyne's 2026-09-26 probes (Memory Core session 9f1b8628-238d-4ab9-b47e-9c08c5df5781) saw CDP placements of a committed popup over main dock into main after the dwell, and read them as instrument artifacts. A CDP placement is a move. This case has no move at all.

The Fix

In DragCoordinator#onWindowPositionChange, read a title-bar move as a change of the window's origin:

  • Keep each window's last published origin (screenLeft/screenTop), including nativeEffect publications, so an engine move becomes the baseline.
  • Start no native drop gesture from a publication that repeats that origin.
  • A candidate that is already running keeps receiving publications as it does today.

The method's JSDoc states the rule. WindowPosition stays as it is, so the measured offset still reaches Neo.manager.Window.

Contract ledger: n/a. No config, signature or payload changes. Decision Record impact: aligned-with ADR-0029 (the native title-bar path).

Acceptance Criteria

  • A unit arm in test/playwright/unit/manager/DragCoordinator.spec.mjs, red on dev: a vessel publication that repeats its last origin starts no native drop candidate, and one that moves the origin still does.
  • A whitebox e2e arm, red on dev, where the vessel closes through commitNativeWindowDrop at about 2 s. It pops out the Feed pane with the header action, moves the pointer into the vessel and holds past dwell + settle (≥ 3 s). Pass: the vessel is still open and still holds the pane.
  • The native title-bar witnesses stay green at the fix head: WorkstationNativeTitlebarDragNL, WorkstationNativePopupOverPopupNL.
  • The WorkstationPopOutSparklinesNL entry leaves BRAIN_QUARANTINES, with the receipt that ties its symptom ("the popup closes while a sparkline cell is being captured") to this mechanism. (Narrowed 2026-10-17 from "each entry": the other five get their own hosted run, below.)

Evidence per AC: PR #19842's AC table at the head the operator merged (e7ac348959, 2026-10-17). It was approved by Euclid (review 5560266267), and its checks passed at that head.

Post-Merge Validation

  • (operator's hand) Pop out a pane in the Workstation, move the mouse into its window and wait 3 s: the pane stays popped out.
  • The other five BRAIN_QUARANTINES entries run at the merged head, each lifted in its own burn-in under #19655. An entry whose arm then passes leaves the list. One that still fails keeps its row, with the finding that this mechanism does not explain it.

Out of Scope

  • Where the vessel opens. Lifting the pane at its own rect is the pop-out's design (HeaderActions#measureDockPaneRect).
  • The dwell, settle and handoff timings.
  • WindowPosition's probe and its movement poll.

Avoided Traps

  • Opening the vessel beside main: this changes product placement and leaves the misread in place for any vessel over main.
  • Tagging only the probe's publication: a fixed-origin resize (onResize) would still read as a drag.
  • A longer dwell, or quarantining HoverSync too: both move the race and call a product bug flake.

Related

  • Parent: #15239
  • #19655: the hosted Brain-gated tier, whose quarantine list holds the arms above
  • #19840: where this surfaced
  • #18559: the measured viewport origin

Sweeps

  • Live latest-open sweep: checked the latest 20 open issues at 2026-10-17T20:49:42Z; no equivalent (nearest: #19537, refused popup opens).
  • A2A claim sweep: the last 30 messages, all read-states, back to 19:41Z; no overlapping claim.
  • MC sweep: "popped-out pane returns to main by itself …" and "WindowPosition viewportProbe publishGeometry … untagged user move", 12 results. The prior art above; no decision that a non-moving publication should count.
  • Own-assignment sweep: 5 open, none on the dock.

Origin Session ID: caeb11ca-daae-42a2-8924-95229e329df2 Retrieval Hint: "popped-out vessel docks back into main, first pointermove, viewport probe, native window drop"

🖖 Grace (Opus 5.5, Claude Code) · session caeb11ca-daae-42a2-8924-95229e329df2

tobiu referenced in commit e7ac348 - "fix(dock): a native title-bar drop starts only when the window's origin moves (#19841) (#19842) on Oct 17, 2026, 11:57 PM
tobiu closed this issue on Oct 17, 2026, 11:57 PM