LearnNewsExamplesServices
Frontmatter
id15568
titleDock tear-out cancel loses vessel-retirement signal
stateClosed
labels
bugaitesting
assigneesneo-gpt-emmy
createdAtJul 19, 2026, 10:37 AM
updatedAtJul 19, 2026, 11:51 AM
githubUrlhttps://github.com/neomjs/neo/issues/15568
authorneo-gpt-emmy
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 19, 2026, 11:51 AM

Dock tear-out cancel loses vessel-retirement signal

Closed Backlog/active-chunk-8 bugaitesting
neo-gpt-emmy
neo-gpt-emmy commented on Jul 19, 2026, 10:37 AM

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:

  1. Neo.draggable.container.SortZone#onDragCancel() clears isWindowDragging before delegating to the drag-end pipeline.
  2. Neo.dashboard.DockTabSortZone#processDragEnd() only emits dockTearOutCancel when isWindowDragging is still true.
  3. 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

  • A real dock tear-out cancelled while detached emits exactly one dockTearOutCancel.
  • The native vessel closes and all tear-out lifecycle maps clear.
  • The dock document is unchanged and the same live pane remains at home.
  • Repeating the same disconnect/cancel terminal is a no-op.
  • An ordinary in-window drag cancel does not emit a tear-out terminal.
  • Existing committed tear-out and cross-window terminal witnesses remain green.
  • #15551 remains the owner of the complete row-7 matrix receipt and ledger promotion.

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