LearnNewsExamplesServices
Frontmatter
id19372
titleA parked vessel's terminal re-show lands one title bar too high
stateClosed
labels
bugairegressiondashboard
assigneesneo-opus-vega
createdAtOct 2, 2026, 10:28 PM
updatedAtOct 3, 2026, 8:50 AM
githubUrlhttps://github.com/neomjs/neo/issues/19372
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 18532 Native vessel re-show has no runtime witness that the outer footprint is preserved (mechanism mapped, preconditions measured)
closedAtOct 3, 2026, 8:50 AM

A parked vessel's terminal re-show lands one title bar too high

Closed v13.2.0/archive-v13-2-0-chunk-24 bugairegressiondashboard
neo-opus-vega
neo-opus-vega commented on Oct 2, 2026, 10:28 PM

Context

Found by #18532's witness arm (the fourth test of test/playwright/e2e/workstation/WorkstationNativePopupOverPopupNL.spec.mjs, on its branch) on dev 42b80fee81, 2026-10-02, headless Chrome on a real display with 67 px of window chrome. The source vessel is parked by the real native-titlebar gesture (dwell → commitNativeWindowDrop → the host's parkTearOutVessel, receipt parked: true); the coordinator then refuses the handoff (clearNativeWindowDropCandidate — the path a declined drop, a failed embodiment and a cancelled gesture all take); the restore receipt reads:

rect  : {x: 796, y: 123, width: 320, height: 240}   // the frame the park took the window from
frame : {x: 796, y: 56}                              // what moveTo was handed
moved : true, admitted: true, terminal: true, addonRestored: false

The window re-shows at y 56 — exactly chrome.top (67) above where the user left it. The arm's assertion against the parked-from frame fails by 67 on every cycle.

The Problem

A refused native handoff, and every non-commit terminal of a pointer-path conversion while the vessel is parked (cancel, reject, a host-routed disconnect — VesselPark's compensating arm), restores the user's window one title bar above where it was. On the native path the user never sees the park, so what they see is the window they were holding jump upward when the drop is declined. The engine reports success (admitted: true), so nothing downstream learns the restore was wrong.

The Architectural Reality

Four lines carry one rect through two coordinate planes — the hazard #18532 named:

step source plane
sample apps/workstation/view/VesselWorkspace.mjs resolveVesselConversionSourceRect → record.outerRect ?? record.innerRect frame, since 48591f31b6 (#19225, PR #19232, 2026-09-26: the conversion admission must read the frame the pointer rides)
record the same value reaches VesselPark#onConversionIn as sourceRect — pointer path VesselWorkspace.mjs:504 (data.record?.sourceRect), native path :558 (resolveVesselConversionSourceRect directly) — and is kept as preConversionRect frame
restore VesselPark#finishTerminal → restore(vessel, vessel.preConversionRect, true) → NativeVesselTransaction reshowVessel({rect, terminal: true}) handed on as rect
convert src/dashboard/dock/window/NativeVesselTransaction.mjs:710 toFrameOrigin(rect, windowId) = rect − chrome, documented at :238–244 as converting a content rect into the frame origin moveTo consumes; pinned by test/playwright/unit/apps/workstation/Workspace.spec.mjs ("large-over-small park … restores the same exact popup": rect {420, 240} → frame {420, 173} with chrome.top 67) content → frame

Before #19225 the recorded rect was the window's innerRect (content), so the conversion was right, and #18515 had fixed the terminal path for exactly that content contract. #19225 moved the sample to the frame for the admission — correctly: ADR 0029 §2.8.6 reads the park against the source's outer frame — and the restore anchor moved with it, uncorrected. The same section's witness states the restore's obligation: "restored the exact original frame".

toFrameOrigin cannot tell which plane it was handed: a frame rect and a content rect are both {x, y}.

The Fix

The park RECORD is a content rect; the admission SAMPLE stays the frame.

  • VesselWorkspace.mjs: one host helper (name at implementation) that snapshots Neo.manager.Window.get(windowId).innerRect for a tear-out vessel, used as sourceRect by both onConversionIn call sites (:504, :558). resolveVesselConversionSourceRect is untouched — the sensor keeps the frame #19225 gave it.
  • VesselPark#onConversionIn's docblock names the plane of sourceRect (content), so the next author does not re-derive it.
  • Unit arm beside the existing park/restore arms in Workspace.spec.mjs: with a source window publishing chrome.top 67, a park record taken from it and a terminal restore hand moveTo the frame the park took the window from — red on dev (frame 67 px high), green with the fix.
  • The #18532 e2e arm is this ticket's end-to-end witness: red by 67 on dev, green with the fix; it lands under #18532 once this merges.

Contract Ledger

Surface Authority Behavior Edge case Docs Evidence
VesselPark#onConversionIn sourceRect — the restore anchor the transaction's content-rect restore contract (toFrameOrigin); ADR 0029 §2.8.6 ("restored the exact original frame") the host records the vessel's inner rect; a terminal restore re-shows the frame where the park took it from a window publishing no chrome: inner = outer, zero offset, behaviour unchanged onConversionIn + helper JSDoc unit arm red-first; the #18532 e2e arm
conversion admission sample — resolveVesselConversionSourceRect #19225 / ADR 0029 §2.8.6 unchanged: the frame — existing #19225's unit arm stays green

Decision Record impact: aligned-with ADR 0029 §2.8.6 — no amendment; the record already states both the admission plane and the restore's obligation.

Acceptance Criteria

  • AC-1: on a real-chrome window, a terminal restore after a refused native handoff re-shows the source at the outer frame the park took it from — OS screenX/Y + outerWidth/Height and manager.Window outerRect, within 2 px, three cycles: the #18532 arm, red by chrome.top on dev 42b80fee81, green with the fix.
  • AC-2: the Workspace.spec.mjs unit arm above is red on dev and green with the fix; the existing park/restore arms and #19225's sampling arm stay green.
  • AC-3: the pointer path's conversion-out rect (data.logicalRect ?? record.sourceRect, VesselWorkspace.mjs:520) is #19373's question — split out because its origin is the coordinator's pointer-minus-grab logicalSourceRect (DragCoordinator.mjs:1040–1044), whose plane only a headed pointer-drag measurement settles; this ticket leaves that site byte-identical and states the finding there, not here.
  • AC-4: resolveVesselConversionSourceRect and the conversion admission are byte-identical to dev.

Out of Scope

The admission plane (#19225 stands). The addon's drag follow and the non-terminal resumeWindowDrag route. Making toFrameOrigin plane-aware (Avoided Traps). The witness arm itself — #18532 lands it.

Avoided Traps

  • Teaching the transaction the plane (rectPlane: 'outer' ⇒ skip toFrameOrigin): the same reshowVessel receives content rects from conversion-out and from the host's own unit contract; a plane switch there fixes one caller by breaking the others. The record site is the one place that knows what it recorded.
  • Subtracting the chrome back at the record site: equivalent to reading innerRect, but it re-derives what manager.Window already publishes (outerRect.x = innerRect.x − chrome.left, src/manager/Window.mjs:113).

Related

#18532 (the witness; blocked by this) · #19373 (the conversion-out half, split from AC-3) · #19225 / PR #19232 (the plane switch) · #18514 / #18515 (the content-rect terminal fix this restores) · #15396 / #15436 (the park-never-close contract) · #19278 (the clear park) · ADR 0029 §2.8.6

Live latest-open sweep: latest 20 open issues read 2026-10-02T20:25Z — no equivalent; the only hits for re-show / toFrameOrigin / preConversionRect are #18532 and closed predecessors. A2A in-flight sweep: last 30 messages, no claim on the restore plane. Memory sweep: the 2026-09-09 chain recorded sourceRect = innerRect as the restore anchor — this ticket is its regression.

Origin Session ID: 31c0406b-2b5c-42e7-89af-35cbecea62ac Retrieval Hint: query_raw_memories("parked vessel terminal re-show chrome too high preConversionRect frame plane toFrameOrigin regression")

tobiu referenced in commit 184af1b - "fix(workstation): the park records the vessel's content rect, so a terminal re-show lands on the frame it was parked from (#19372) (#19374) on Oct 3, 2026, 8:50 AM
tobiu closed this issue on Oct 3, 2026, 8:50 AM