Split from #19372 (its AC-3). #19372 fixes the TERMINAL restore: the park now records the vessel's content rect, so a refused handoff or a cancelled conversion re-shows the frame the park took the window from. The NON-terminal path — the pointer leaves the target while the vessel is parked — re-shows through a different rect, and its plane is not settled by source reading alone.
The Problem
apps/workstation/view/VesselWorkspace.mjs:520 resumes at data.logicalRect ?? data.record?.sourceRect. logicalRect is the coordinator's logicalSourceRect (src/manager/DragCoordinator.mjs:1040–1044): {x: screenX − offsetX, y: screenY − offsetY, width/height: proxyRect} — the pointer minus the grab offset. Two readings of that origin disagree:
as the proxy's content origin in screen coordinates — then toFrameOrigin (src/dashboard/dock/window/NativeVesselTransaction.mjs:710) converts it correctly;
as the frame origin the window follow uses — #19225's own observation: the dragged vessel follows the pointer with its OUTER origin at pointer minus the grab offset, its content one title bar lower — then the resume hands resumeWindowDrag an origin one chrome too high, and whether the addon's follow corrects it on the next pointer frame is unmeasured.
The fallback, record.sourceRect, is the FRAME sample since #19225 (48591f31b6) and is converted wrongly for certain; it is reached only when logicalRect is absent.
The Architectural Reality
Terminal restores read preConversionRect — content since #19372. Non-terminal re-shows read the out-event's live rect (the design record's resumeWindowDrag(widgetName, proxyRect) semantics: the popup resumes under the pointer).
The resume route is the addon's (Neo.main.addon.DragDrop.resumeWindowDrag, NativeVesselTransaction.mjs:756–758): the drag is live, the addon owns the gesture, and a direct route move beside it would be a second writer — so the final position is the addon's to settle, which is why this needs a measurement, not a derivation.
Stage: a pointer-driven vessel-over-vessel drag on real chrome. DemoBCrossWindowDragNL has been 2 of 3 red since 2026-09-09; WorkstationNativeTitlebarDragNL / WorkstationNativePopupOverPopupNL drive the native path only. The measurement needs a torn-out tab dragged by pointer across windows on a real display (headed).
The Fix — a measurement first
On real chrome, headed: tear a tab out, drag it by pointer over a second vessel until the conversion parks the source, drag out again, read the restore receipt (rect, frame, terminal: false), the addon's resume data and the window's final screenX/Y against the pointer position. Post the receipt here.
If the resumed window sits one chrome above where the pointer holds it: hand the content plane in at the conversion-out site (the logical rect shifted by the source window's chrome, or the window's live inner rect) — the rule #19372 applied to the record — with a red-first unit arm beside #19372's.
If it sits where the pointer holds it: record that here and close the branch question.
Either way, the fallback record.sourceRect moves to the content anchor (resolveVesselRestoreAnchor, #19372): it is a frame rect by construction.
Contract Ledger
Surface
Authority
Behavior
Edge case
Docs
Evidence
conversion-out resume rect (VesselWorkspace.mjs:520)
the transaction's content-rect contract; the design record's resume-under-the-pointer semantics
the vessel resumes under the pointer with its frame where the follow holds it
logicalRect absent → the content anchor, never the frame sample
site comment names the plane
the headed measurement; a unit arm if step 2 fires
Decision Record impact: none — aligned-with ADR 0029 §2.8.6 (the resume semantics are already its text).
Acceptance Criteria
AC-1: one measured headed run on real chrome, receipt posted here: restore.rect, restore.frame, the final screenX/Y, the pointer position and the window's chrome.
AC-2: with no live origin on the out-event, the re-show uses the content anchor the park already captured through resolveVesselRestoreAnchor, never record.sourceRect (a frame sample) and never a second read after parking, which would read the parking corner (Sophie's refinement, 6048871689); unit arm red-first.
AC-3: if AC-1 shows a chrome-sized displacement, the logicalRect branch hands the content plane in, with a red-first unit arm and the measurement repeated green; if it shows none, the measurement is the closing evidence and no further code lands.
Out of Scope
Terminal restores (#19372). The conversion admission plane (#19225). The large-over-small conversion window (#16358, #16472).
Ownership: unowned, with rationale — the measurement needs a headed seat on a real display and a pointer-driven cross-window stage that is red today; I take it after #18532 lands unless a headed seat claims it first.
Live latest-open sweep: latest 20 open issues read 2026-10-02T20:37Z — no equivalent; keyword hits (conversion-out · logicalRect · resumeWindowDrag) are #19372, #18532 and closed predecessors. A2A in-flight sweep: last 30 messages, no claim.
Origin Session ID: 31c0406b-2b5c-42e7-89af-35cbecea62ac
Retrieval Hint: query_raw_memories("conversion-out resume rect plane logicalSourceRect pointer minus grab offset chrome real window measurement")
tobiu referenced in commit 994fa3e - "fix(workstation): keep converted popups under the pointer (#19373) (#19458)" on Oct 8, 2026, 1:50 AM
Context
Split from #19372 (its AC-3). #19372 fixes the TERMINAL restore: the park now records the vessel's content rect, so a refused handoff or a cancelled conversion re-shows the frame the park took the window from. The NON-terminal path — the pointer leaves the target while the vessel is parked — re-shows through a different rect, and its plane is not settled by source reading alone.
The Problem
apps/workstation/view/VesselWorkspace.mjs:520resumes atdata.logicalRect ?? data.record?.sourceRect.logicalRectis the coordinator'slogicalSourceRect(src/manager/DragCoordinator.mjs:1040–1044):{x: screenX − offsetX, y: screenY − offsetY, width/height: proxyRect}— the pointer minus the grab offset. Two readings of that origin disagree:toFrameOrigin(src/dashboard/dock/window/NativeVesselTransaction.mjs:710) converts it correctly;resumeWindowDragan origin one chrome too high, and whether the addon's follow corrects it on the next pointer frame is unmeasured.The fallback,
record.sourceRect, is the FRAME sample since #19225 (48591f31b6) and is converted wrongly for certain; it is reached only whenlogicalRectis absent.The Architectural Reality
preConversionRect— content since #19372. Non-terminal re-shows read the out-event's live rect (the design record'sresumeWindowDrag(widgetName, proxyRect)semantics: the popup resumes under the pointer).Neo.main.addon.DragDrop.resumeWindowDrag,NativeVesselTransaction.mjs:756–758): the drag is live, the addon owns the gesture, and a direct route move beside it would be a second writer — so the final position is the addon's to settle, which is why this needs a measurement, not a derivation.DemoBCrossWindowDragNLhas been 2 of 3 red since 2026-09-09;WorkstationNativeTitlebarDragNL/WorkstationNativePopupOverPopupNLdrive the native path only. The measurement needs a torn-out tab dragged by pointer across windows on a real display (headed).The Fix — a measurement first
restorereceipt (rect,frame,terminal: false), the addon's resume data and the window's finalscreenX/Yagainst the pointer position. Post the receipt here.record.sourceRectmoves to the content anchor (resolveVesselRestoreAnchor, #19372): it is a frame rect by construction.Contract Ledger
VesselWorkspace.mjs:520)logicalRectabsent → the content anchor, never the frame sampleDecision Record impact: none — aligned-with ADR 0029 §2.8.6 (the resume semantics are already its text).
Acceptance Criteria
restore.rect,restore.frame, the finalscreenX/Y, the pointer position and the window's chrome.resolveVesselRestoreAnchor, neverrecord.sourceRect(a frame sample) and never a second read after parking, which would read the parking corner (Sophie's refinement, 6048871689); unit arm red-first.logicalRectbranch hands the content plane in, with a red-first unit arm and the measurement repeated green; if it shows none, the measurement is the closing evidence and no further code lands.Out of Scope
Terminal restores (#19372). The conversion admission plane (#19225). The large-over-small conversion window (#16358, #16472).
Related
#19372 (the terminal half; parent of this split) · #18532 (the native-path witness) · #19225 / PR #19232 · #18514 / #18515 · #16358 · #16472
Ownership: unowned, with rationale — the measurement needs a headed seat on a real display and a pointer-driven cross-window stage that is red today; I take it after #18532 lands unless a headed seat claims it first.
Live latest-open sweep: latest 20 open issues read 2026-10-02T20:37Z — no equivalent; keyword hits (conversion-out · logicalRect · resumeWindowDrag) are #19372, #18532 and closed predecessors. A2A in-flight sweep: last 30 messages, no claim.
Origin Session ID: 31c0406b-2b5c-42e7-89af-35cbecea62ac Retrieval Hint:
query_raw_memories("conversion-out resume rect plane logicalSourceRect pointer minus grab offset chrome real window measurement")