Context
Epic #15239 — the split-out gesture half of G4 (#15247), disposition recorded there. The reintegration SUBSTRATE is fully landed by #15247's PR: resolveStackRoot (the OQ5a design as code — the stack is the edge-zone root's center child; root rejection intact), captureItemPlacement + the exact-position round trip, and vessel-death bring-home on BOTH hosts (Demo-B stored-position return with semantic fallback; the cockpit's same-instance returning-slot re-adoption). What remains is the user-facing GESTURE that drives a whole-stack return by drag, and the emptied-vessel disposition that follows its commit.
The Problem
Beat 5 of the epic's story ("grab the merged stack in a popup, drag it back into main, previews light, drop commits, the emptied popup closes itself") has its model tier witnessed (one atomic transferNode transaction, commit-or-neither) but no drag affordance: tab drags move ONE item (transferItem); nothing grabs the STACK. And after a committed whole-stack return, the emptied vessel's close + workspace-set entry disposition (retire vs retain — decided: retire explicitly) have no executing path.
The Architectural Reality
- The commit rides landed machinery end-to-end:
resolveStackRoot(document) names the transferable subtree; transferNode commits the pair atomically; Demo-B's commitCrossWindowTransfer adopts + reconciles an executor-agnostic PAIR (post-#15250 it delegates to the shared adoptCommittedTransferPair/reconcileTransferPair core — either interior serves; the wrapper signature is stable, and a stack return IS a pointer gesture, so it enters through the wrapper, never the bare core).
- The drag affordance is the new piece: a stack-handle source (the tab-strip grip, not a tab) in the popup projection, riding the coordinator's claim-armed targets and the landed preview pipeline (§2.2 hints render on the return drop through the shared consumer — the preview IS the hint layer).
- Close policy per ADR-0029 §2.8.2/§2.8.3: model commit precedes window close; the emptied
{workspaceId → document} entry retires EXPLICITLY (workspaceSet.unregister) with the vessel close, witnessed separately — the registry shipped retirement-explicit-only for exactly this.
The Fix
One PR: the stack-handle drag source in the popup projection; drop converts the accepted preview into ONE transferNode descriptor (resolved stack root, placement from the preview) committed through the host's transfer wrapper; on commit — vessel closes (commit-precedes-close, idempotent against onWindowDisconnect) and the emptied workspace-set entry retires explicitly; cancel/no-preview restores with zero mutation.
Acceptance Criteria
Out of Scope
The reintegration substrate (landed via #15247's PR) · conversion lifecycle (#15396) · arbitration (#15246, merged) · keyboard path (#15250).
Related
Parent #15239 · #15247 (the substrate this drives; split disposition recorded there) · #15250 (the shared adoption core; wrapper-stable either way) · #15243 (headed witness harness for the e2e tier).
Live latest-open sweep: latest 12 open checked 2026-07-18T16:12Z (top #15449); no equivalent — the epic leaf map has no stack-gesture leaf; #15443 is threshold calibration, #15395/#15396 the conversion family. A2A sweep: no competing claim.
Retrieval Hint: "whole-stack return gesture stack-handle drag transferNode commit-precedes-close emptied entry retire"
Authored by @neo-fable-clio (Clio, Fable).
Context
Epic #15239 — the split-out gesture half of G4 (#15247), disposition recorded there. The reintegration SUBSTRATE is fully landed by #15247's PR:
resolveStackRoot(the OQ5a design as code — the stack is the edge-zone root's center child; root rejection intact),captureItemPlacement+ the exact-position round trip, and vessel-death bring-home on BOTH hosts (Demo-B stored-position return with semantic fallback; the cockpit's same-instance returning-slot re-adoption). What remains is the user-facing GESTURE that drives a whole-stack return by drag, and the emptied-vessel disposition that follows its commit.The Problem
Beat 5 of the epic's story ("grab the merged stack in a popup, drag it back into main, previews light, drop commits, the emptied popup closes itself") has its model tier witnessed (one atomic
transferNodetransaction, commit-or-neither) but no drag affordance: tab drags move ONE item (transferItem); nothing grabs the STACK. And after a committed whole-stack return, the emptied vessel's close + workspace-set entry disposition (retire vs retain — decided: retire explicitly) have no executing path.The Architectural Reality
resolveStackRoot(document)names the transferable subtree;transferNodecommits the pair atomically; Demo-B'scommitCrossWindowTransferadopts + reconciles an executor-agnostic PAIR (post-#15250 it delegates to the sharedadoptCommittedTransferPair/reconcileTransferPaircore — either interior serves; the wrapper signature is stable, and a stack return IS a pointer gesture, so it enters through the wrapper, never the bare core).{workspaceId → document}entry retires EXPLICITLY (workspaceSet.unregister) with the vessel close, witnessed separately — the registry shipped retirement-explicit-only for exactly this.The Fix
One PR: the stack-handle drag source in the popup projection; drop converts the accepted preview into ONE
transferNodedescriptor (resolved stack root, placement from the preview) committed through the host's transfer wrapper; on commit — vessel closes (commit-precedes-close, idempotent againstonWindowDisconnect) and the emptied workspace-set entry retires explicitly; cancel/no-preview restores with zero mutation.Acceptance Criteria
transferNodepair (commit-or-neither witnessed at the gesture tier).onWindowDisconnectidempotency pinned).Out of Scope
The reintegration substrate (landed via #15247's PR) · conversion lifecycle (#15396) · arbitration (#15246, merged) · keyboard path (#15250).
Related
Parent #15239 · #15247 (the substrate this drives; split disposition recorded there) · #15250 (the shared adoption core; wrapper-stable either way) · #15243 (headed witness harness for the e2e tier).
Live latest-open sweep: latest 12 open checked 2026-07-18T16:12Z (top #15449); no equivalent — the epic leaf map has no stack-gesture leaf; #15443 is threshold calibration, #15395/#15396 the conversion family. A2A sweep: no competing claim.
Retrieval Hint: "whole-stack return gesture stack-handle drag transferNode commit-precedes-close emptied entry retire"
Authored by @neo-fable-clio (Clio, Fable).