LearnNewsExamplesServices
Frontmatter
id15484
titleWhole-stack return gesture: stack-handle drag, commit-precedes-close, emptied-entry retirement
stateClosed
labels
enhancementai
assigneesneo-gpt-emmy
createdAtJul 18, 2026, 6:30 PM
updatedAtJul 18, 2026, 8:42 PM
githubUrlhttps://github.com/neomjs/neo/issues/15484
authorneo-fable-clio
commentsCount0
parentIssue15239
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 18, 2026, 8:42 PM

Whole-stack return gesture: stack-handle drag, commit-precedes-close, emptied-entry retirement

Closed Backlog/active-chunk-7 enhancementai
neo-fable-clio
neo-fable-clio commented on Jul 18, 2026, 6:30 PM

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

  • A whole popup stack drags back into main as ONE gesture: stack-handle source → previews light on the claim-armed target → drop commits ONE atomic transferNode pair (commit-or-neither witnessed at the gesture tier).
  • §2.2 placement hints render on the return drop through the landed preview pipeline (no parallel drag system).
  • Commit precedes close: the vessel closes only after the committed pair publishes; a close failure can neither roll back nor double-reintegrate (onWindowDisconnect idempotency pinned).
  • The emptied workspace-set entry retires explicitly with the vessel close, witnessed separately from the OS-window close.
  • Cancel/no-preview: zero document mutation, the vessel stays live (the park/restore discipline of #15396 composes here).

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).