Context
This is a production repair leaf discovered while executing #15551's headed macOS row-7 terminal witness under epic #15239. The original #15407 contract tier correctly requires a detached cancel to retire its vessel with zero document mutation, but its pure handler witnesses did not exercise the complete native event chain.
The Problem
A real headed Demo B tear-out followed by Escape returns cancelled: true and preserves the dock document, yet the native ?popout=workbench vessel remains open and tearOutConnects.workbench remains live.
The event-chain cause is order-sensitive:
Neo.draggable.container.SortZone#onDragCancel() clears isWindowDragging before delegating to the drag-end pipeline.
Neo.dashboard.DockTabSortZone#processDragEnd() only emits dockTearOutCancel when isWindowDragging is still true.
- The host's already-correct
createDockTearOutHandlers().onDockTearOutCancel() therefore never runs, so closeTearOutVessel() never retires the popup or its bookkeeping.
This is not #15396's popup-to-proxy park/re-show lifecycle. It is the base G1 tear-out cancel terminal and reproduces without a conversion target.
The Architectural Reality
The ownership split remains correct:
SortZone owns generic drag cancellation and source-DOM restoration.
DockTabSortZone owns translating a detached dock cancel into the host-facing dockTearOutCancel event.
DockTearOut owns clearing its active vessel slot and invoking the injected host retirement seam.
- The host owns best-effort native close and grant/bookkeeping cleanup.
The fix must preserve the generic sort-zone behavior that restores the source DOM on cancel. Keeping isWindowDragging=true throughout base cleanup merely to reach the dock event would change that generic behavior.
The Fix
Capture/route the detached dock-cancel semantic before the generic base cancellation clears its window-drag flag, then let the existing base cleanup proceed unchanged.
Strengthen the real Demo B gesture witness so it proves:
- the popup is physically closed;
- document truth remains byte-identical;
tearOutConnects, tearOutPanes, and tearOutPlacements are empty;
- the same pane instance remains mounted at home;
- repeating the disconnect terminal is a no-op.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
DockTabSortZone cancel entry |
ADR 0029 tear-out lifecycle; #15407 |
Emit one host-facing detached-cancel signal while the pre-cancel state is still observable |
In-window cancel emits no tear-out terminal |
Method summary/comment |
real gesture negative-to-positive witness |
Generic SortZone cleanup |
Existing drag restoration contract |
Preserve current flag reset and source-DOM cleanup semantics |
Duplicate/no-active cancel remains inert |
Existing source docs |
focused unit/e2e regressions |
| Host retirement |
DockTearOut + Demo B host seam |
Close the admitted vessel and clear grant/connect bookkeeping once |
Already-closed/repeated terminal is a no-op |
Existing seam docs |
popup-close + map-census receipt |
| Matrix row 7 consumer |
#15551 / #15243 |
Consume the repaired terminal in the headed macOS drop/cancel/blocked-acquisition matrix |
Row remains NOT_YET_MEASURED until all terminals and universal invariants pass |
living ledger |
follow-on #15551 exact-head run |
Acceptance Criteria
Out of Scope
Popup-to-proxy conversion park/re-show (#15396) · row-4 store continuity · row-6 arbitration · production changes outside the dock cancel signal chain · promoting any matrix cell from partial evidence.
Decision Record impact
No ADR change. This restores the already-ratified ADR 0029 / #15407 cancel terminal.
Prior Art
Memory Core session 64f444d3-1042-4091-a56f-08332b6cc7a2 records the original DockTearOut design: entry and cancel retire with zero mutation. The continuity gap was the absence of a full native-event-chain witness.
Related
Parent epic #15239 · evidence consumer #15551 / #15243 · contract tier #15407 · conversion lifecycle #15396 (non-owner) · teardown hygiene #15248 (different cross-window coordinator surface).
Freshness Receipt
Immediately before filing, the latest 20 open issues, two all-state targeted GitHub searches, the current source tree, the headed live runtime, recent A2A messages, and Memory Core raw/summarized prior art were checked. No equivalent active ticket or competing claim was found.
Handoff
Start from fresh origin/dev. Keep the repair inside the dock cancel signal chain plus focused witnesses; do not absorb the matrix ledger or conversion lifecycle.
Origin Session ID: ad71d4c3-3e37-4a17-8df7-8415509def84
Retrieval Hint: dock tear-out cancel clears isWindowDragging before dockTearOutCancel popup remains open
Context
This is a production repair leaf discovered while executing #15551's headed macOS row-7 terminal witness under epic #15239. The original #15407 contract tier correctly requires a detached cancel to retire its vessel with zero document mutation, but its pure handler witnesses did not exercise the complete native event chain.
The Problem
A real headed Demo B tear-out followed by Escape returns
cancelled: trueand preserves the dock document, yet the native?popout=workbenchvessel remains open andtearOutConnects.workbenchremains live.The event-chain cause is order-sensitive:
Neo.draggable.container.SortZone#onDragCancel()clearsisWindowDraggingbefore delegating to the drag-end pipeline.Neo.dashboard.DockTabSortZone#processDragEnd()only emitsdockTearOutCancelwhenisWindowDraggingis still true.createDockTearOutHandlers().onDockTearOutCancel()therefore never runs, socloseTearOutVessel()never retires the popup or its bookkeeping.This is not #15396's popup-to-proxy park/re-show lifecycle. It is the base G1 tear-out cancel terminal and reproduces without a conversion target.
The Architectural Reality
The ownership split remains correct:
SortZoneowns generic drag cancellation and source-DOM restoration.DockTabSortZoneowns translating a detached dock cancel into the host-facingdockTearOutCancelevent.DockTearOutowns clearing its active vessel slot and invoking the injected host retirement seam.The fix must preserve the generic sort-zone behavior that restores the source DOM on cancel. Keeping
isWindowDragging=truethroughout base cleanup merely to reach the dock event would change that generic behavior.The Fix
Capture/route the detached dock-cancel semantic before the generic base cancellation clears its window-drag flag, then let the existing base cleanup proceed unchanged.
Strengthen the real Demo B gesture witness so it proves:
tearOutConnects,tearOutPanes, andtearOutPlacementsare empty;Contract Ledger
DockTabSortZonecancel entrySortZonecleanupDockTearOut+ Demo B host seamNOT_YET_MEASUREDuntil all terminals and universal invariants passAcceptance Criteria
dockTearOutCancel.Out of Scope
Popup-to-proxy conversion park/re-show (#15396) · row-4 store continuity · row-6 arbitration · production changes outside the dock cancel signal chain · promoting any matrix cell from partial evidence.
Decision Record impact
No ADR change. This restores the already-ratified ADR 0029 / #15407 cancel terminal.
Prior Art
Memory Core session
64f444d3-1042-4091-a56f-08332b6cc7a2records the original DockTearOut design: entry and cancel retire with zero mutation. The continuity gap was the absence of a full native-event-chain witness.Related
Parent epic #15239 · evidence consumer #15551 / #15243 · contract tier #15407 · conversion lifecycle #15396 (non-owner) · teardown hygiene #15248 (different cross-window coordinator surface).
Freshness Receipt
Immediately before filing, the latest 20 open issues, two all-state targeted GitHub searches, the current source tree, the headed live runtime, recent A2A messages, and Memory Core raw/summarized prior art were checked. No equivalent active ticket or competing claim was found.
Handoff
Start from fresh
origin/dev. Keep the repair inside the dock cancel signal chain plus focused witnesses; do not absorb the matrix ledger or conversion lifecycle.Origin Session ID:
ad71d4c3-3e37-4a17-8df7-8415509def84Retrieval Hint:
dock tear-out cancel clears isWindowDragging before dockTearOutCancel popup remains open