Merged PR #19292 (91fd23336d20cbe17f95d8e357c9f9a15be34181) makes Workstation return a physically closed popup's panes to the main layout in one undoable Group transaction. The operator selected that behavior and then requested this follow-up: why does reusable multi-window transaction behavior live in custom app code, and which part belongs in manager.Transaction versus dashboard/dock/*?
This is a placement and reuse correction to the shipped behavior, not a new close-policy decision.
The Problem
Workstation now assembles an all-pane document transfer, guards it against queued writes and successor bindings, correlates close versus reload, routes reconnect-lease expiry, and implements native undo/redo effects. Another Dock consumer must reproduce that orchestration to offer the same policy.
Moving the whole method into the generic transaction manager would put dock item, home and tab semantics below their existing domain boundary. Moving it verbatim behind an app-shaped engine wrapper would preserve the duplication in another directory.
The Architectural Reality
Read at the merged implementation's source:
Existing owner
Responsibility and current seam
src/manager/Transaction.mjs class contract; write, undo, redo
Logical Groups, serialized transactions, opaque participants, binding generations and one history cursor. Explicitly knows no dock document or app class.
src/manager/transaction/Commit.mjs#run
Captures participant values at queue head, prepares/adopts/compensates atomically, publishes a snapshot, then reports projection/effect failures separately. Replay supplies replayRow.
src/manager/transaction/NativeLifecycle.mjs
Group-owned admission, connection and retirement records; source-contributed effects. Its existing onLeaseExpired handles provisional admissions, not the app's formerly-bound recovery path.
Adapts dock reducers to the participant protocol; a transfer already reduces from both captured documents. It owns no parallel membership registry.
src/dashboard/dock/window/NativeVesselTransaction.mjs and Workspace#bindNativeWindowSource
Shared native effect routines and Dock composition seams. The closed predecessor #18651 established a callable reuse path rather than requiring all hosts to inherit the same view.
Current app-side orchestration to retire or reduce.
src/Main.mjs#windowNativeIsClosed
Existing exact-handle physical observation, including unknown outcomes. Reuse it; disconnected is not synonymous with closed.
The Fix
Deliver the extraction and Workstation migration together in one PR.
Keep the transaction authority generic. Reuse Transaction / Commit for the queue, captured state, atomic adoption, cursor and effect receipts. Any necessary generic lifecycle hook belongs with the existing Group owner and carries opaque identities; it must not import Dock or interpret pane-return descriptors.
Put Dock semantics in the Dock adapter. Extend the existing WorkspaceSet preparation path for an atomic all-pane return using model/Operations. Prefer preparation from queue-head captures over transplanting Workstation's precompute/version/retry loop. Compose correlated native replay through the existing Dock window/Workspace seams; evaluate NativeVesselTransaction for the shared effect routine. Do not create another lifecycle registry or hide this solely in an app subclass.
Expose policy, not orchestration. A consumer selects retained headless layout versus return, and supplies the destination workspace and landing policy. Workstation selects return-to-main and preserves its current recorded-home/index and fallback behavior. Unconfigured consumers retain their existing behavior. Keep product names, workspace prefixes, pane factories and user-facing receipts in the app.
Delete the replaced app machinery. Reduce returnClosedPopupWorkspace to policy/receipt glue or remove it; remove the app's replay-specific projectDockCommit override and bound-close lease subscription once shared composition owns them. Report the removed code and remaining app responsibilities.
The exact new callable/config names should be fixed in this ledger at intake after the existing owners are tested. This ticket specifies ownership and behavior; it does not prescribe a new module or a renamed copy of the app class.
Contract Ledger Matrix
Target Surface
Source of Authority
Proposed Behavior
Fallback
Docs
Evidence
Proposed Dock return preparation on the existing WorkspaceSet participant path
ADR 0029 §§2.1, 2.3; current Commit#run / WorkspaceSet#register
All source panes return through one Group transaction against current captured documents; one cursor row; identity and ordering preserved
Invalid target, reducer refusal, stale binding or failed adoption produces no partial document change; retain recovery authority
Callable JSDoc and Dock adoption guide
Real Group tests with arbitrary workspace keys; intervening write, successor binding and refusal controls
Proposed opt-in close policy on Dock composition
Operator's choice on #19273; ADR 0029 §2.8; current Workspace#bindNativeWindowSource
Return or retain, with consumer-supplied destination/landing choice; Workstation uses return-to-main
Default preserves other consumers; unknown physical result does not assert closure; lease recovery is separately identified
Dock window guide: close/reload/lease behavior and timing
Return and retain cases, reload within lease, formerly bound expiry, never-bound admission
Shared close-return replay through Dock window effects
Existing Group history/replayRow; ADR 0029 §2.8.3
Undo restores documents and attempts popup embodiment; redo returns documents then closes the exact current render target
Popup denial/close refusal remains an observable native outcome; does not roll back semantic truth, duplicate a return or close a successor
Effect contract and recovery behavior
Undo/redo with native refusal and stale-generation controls; same pane instances
Keeps app policy and result presentation; consumes the shared mechanism
Existing safe behavior remains on rejected return; app does not regain a second lifecycle owner
App README plus concise composition example
Existing close, reload, multi-pane and Group-cursor browser witnesses at the PR head
Acceptance Criteria
The PR explains the manager.Transaction / manager.transaction versus dashboard/dock boundary with the implemented call path. Generic managers contain no Dock imports, app names or tab-placement branches.
One shared Dock operation returns all panes atomically and records one Group row; it preserves earlier queued writes, rejects stale/successor bindings and leaves both documents intact on refusal.
Close, warm reload, unknown observation, formerly-bound lease expiry and never-bound admission stay distinct. Timing comes from documented policy/current authorities; no second window registry or per-popup worker-wide lifecycle owner is added.
Undo/redo preserves pane/component identity and reports failed native effects separately. A failed or duplicate native notification cannot reintegrate twice or close a replacement window.
Workstation's orchestration is deleted as described above. The PR gives a before/after deletion account; additional engine lines alone do not satisfy the extraction.
An independent minimal Dock host/fixture, without Workstation classes or identifiers, exercises both return and retain policies through the public composition seam. Existing Workstation single-/multi-pane close, popup/main reload and undo/redo browser witnesses pass at the head; render boundaries are observed before identity assertions.
Public seam names and failure behavior are finalized in the ledger, documented with a small consumer example, and covered by executable tests. No unchanged consumer silently adopts Workstation's product policy.
Decision Record impact
aligned-with ADR 0029: worker-owned documents, reducer-based changes, and native close as a separate effect. The reusable facility preserves those boundaries. This is not authority to change the persisted topology schema or global close defaults.
Out of Scope
Video/tour work; whole Demo B migration; new persistence formats; another transaction/history subsystem; changing the product's chosen return behavior; the separately owned popup parking/dropzone lane.
Avoided Traps
Putting tab/home rules in manager.Transaction because the operation is transactional.
A new lifecycle owner alongside NativeLifecycle, or a wrapper that still requires every app to implement the algorithm.
Moving the existing retry loop untouched when queue-head participant preparation can remove it.
Equating a native close with deletion of a logical workspace, or an unknown observation with confirmed death.
Claiming reuse from tests that call the Workstation method through a facade.
Related
#19273 · PR #19292 · #18314 · #18651 · #15247. The latter two concern commanded vessel close/gesture return; this follow-up concerns physical-close-triggered all-pane return and replay. #19101 is a release-note survey, not an implementation duplicate.
unowned-rationale: operator-requested follow-up recorded for the Dock engine queue; no implementation claim is made by filing it. A claimant should confirm the shared seam at intake before editing.
Live latest-open sweep: latest 20 open Engine issues in created-descending order at 2026-09-26 immediately before creation at 20:08 UTC (newest #19295); no equivalent extraction. A2A: latest 30 messages across read states immediately before filing; no competing claim. Exact GitHub searches for popup close/return and WorkspaceSet surfaced #18651, #15247 and #19101; their bodies were read and scopes distinguished above. Own-assignment sweep: two open issues (#16151, #15526), neither on this surface.
MC rationale sweep: three-result searches on popup-close return, NativeLifecycle/opaque Group ownership and the current app method names, plus the final problem-noun sweep. Recovered the earlier Group-owned lifecycle and shared-close decisions; no retrieved decision rejects this extraction. KB source/ticket queries were incomplete and used only as discovery hints; current source and ADR 0029 supplied the ownership evidence. Surface names in the ledger were checked directly. No new module placement is prescribed; the structural-pre-flight trigger must run if intake chooses one.
Claimer contract — @neo-gpt, 2026-09-29
Selected discipline: ticket-intake + tech-debt-radar; medium, reversible extraction under ADR 0029. The runtime-owner and finite-JSON probes in the intake comment pin the existing owners. No new persisted format or global close policy is introduced.
Carry the exact released binding/generation through expiry; Transaction#getBinding exposes generationToken, and Dock capture combines lineage plus counter to reject replacement during preparation; Group-owned awaitingClosure and the full pending release-work promise live on that binding, clear on accepted rebind; Dock code queries expiry through the owner and returns a disposition, while only the owner updates closure state; source bindingExpired callback distinguishes formerly bound expiry
Old/foreign/successor generations do no work; never-bound admission keeps its existing expiry path
Actual manager release/rebind/expiry and pending-work controls; no Dock imports
WorkspaceSet#register optional resolveReturnDescriptor / runtime nativeWindow effect seams, and #returnWorkspace
Existing registered Dock participants and context.valuesBefore
Reduce all current source items against both queue-head captures; registered target calls the existing landing hook; plain placements and history metadata carry no functions; one Group row
No partial adoption on missing/read-only target, reducer/adoption refusal or failed binding guard; duplicate empty return records no extra row
Arbitrary workspace keys, earlier queued writes, binding successor and refusal controls
WorkspaceSet#getParticipantForBinding(bindingKey)
Existing Group participant membership and registered bindingKey / runtime nativeWindow seams
Resolve the unique writable native-window document owner as {workspaceId, participant}, independently of semantic document-key spelling
Return null for invalid/missing binding keys, missing/ineligible owners, or multiple eligible owners; no parallel registry
Callable JSDoc, independent host and lookup/refusal controls
Opt-in return or retain; an injected WorkspaceSet allows close-policy-only native sources without enabling tear-out gestures; view hooks provide route/presentation and product policy, not transaction algorithms
Default null preserves the existing consumer path; unknown observation retains until the separately identified lease boundary
Independent return/retain host and the existing Workstation close/reload witnesses
Existing Dock native effects and Group history/projection
Observe exact native closure; return through WorkspaceSet; reserve/open/revoke retained workspace embodiment; correlated undo/redo wraps ordinary projection and emits native outcomes on the existing effect-receipt channel
Open/close refusal remains an effect receipt; semantic truth stays committed; generation checks cannot close a successor
Native refusal, duplicate notification and replay controls; same pane instances
Workstation migration
Existing return-to-main policy and landing hook
Main registration supplies its current landing hook; popup opts in to return-to-main; retain only product URL/name/features, interaction cleanup and receipt glue
Remove snapshot/version/retry loop, popup replay override and controller lease subscription
Before/after source deletion account and rendered identity battery
The nativeWindow registration value is a runtime effect/accessor descriptor, not a new landing-policy object; resolveDockReturnDescriptor remains the landing policy. No callback enters a semantic descriptor. The history operation label remains returnPopupWorkspace for replay compatibility, while the public all-pane operation is returnWorkspace. Its result exposes ordered itemIds even with history depth zero. Binding keys resolve native slots independently of semantic participant keys. Unknown physical observations wait for the manager's configured reconnect lease; expiry of the same formerly-bound record is a separate recovery cause, including when observation was still pending.
Current source at dev@ede64fd051 still has the same affected owner blobs as the intake baseline; the only intervening merge retires the unrelated conversion exit signal. Created 2026-09-26 / updated 2026-09-26: pre-stale under the 90+14-day workflow, no stale/exemption label. No assignee, parent, blocker or matching open PR exists. ADR successor-risk: adr-aligned. Classification: valid-as-written with this intake-derived ledger. Positive ROI: delete consumer transaction/lifecycle orchestration while giving independent Dock hosts the same operation.
Context
Merged PR #19292 (
91fd23336d20cbe17f95d8e357c9f9a15be34181) makes Workstation return a physically closed popup's panes to the main layout in one undoable Group transaction. The operator selected that behavior and then requested this follow-up: why does reusable multi-window transaction behavior live in custom app code, and which part belongs inmanager.Transactionversusdashboard/dock/*?This is a placement and reuse correction to the shipped behavior, not a new close-policy decision.
The Problem
Workstation now assembles an all-pane document transfer, guards it against queued writes and successor bindings, correlates close versus reload, routes reconnect-lease expiry, and implements native undo/redo effects. Another Dock consumer must reproduce that orchestration to offer the same policy.
Moving the whole method into the generic transaction manager would put dock item, home and tab semantics below their existing domain boundary. Moving it verbatim behind an app-shaped engine wrapper would preserve the duplication in another directory.
The Architectural Reality
Read at the merged implementation's source:
src/manager/Transaction.mjsclass contract;write,undo,redosrc/manager/transaction/Commit.mjs#runreplayRow.src/manager/transaction/NativeLifecycle.mjsonLeaseExpiredhandles provisional admissions, not the app's formerly-bound recovery path.src/dashboard/dock/window/WorkspaceSet.mjs#register/transfersrc/dashboard/dock/window/NativeVesselTransaction.mjsandWorkspace#bindNativeWindowSourceapps/workstation/view/VesselWorkspace.mjs#onNativeWindowRelease/returnClosedPopupWorkspace;PopupWorkspace#projectDockCommit;WorkspaceController#onPopupLeaseExpiredsrc/Main.mjs#windowNativeIsClosedThe Fix
Deliver the extraction and Workstation migration together in one PR.
Transaction/Commitfor the queue, captured state, atomic adoption, cursor and effect receipts. Any necessary generic lifecycle hook belongs with the existing Group owner and carries opaque identities; it must not import Dock or interpret pane-return descriptors.WorkspaceSetpreparation path for an atomic all-pane return usingmodel/Operations. Prefer preparation from queue-head captures over transplanting Workstation's precompute/version/retry loop. Compose correlated native replay through the existing Dock window/Workspace seams; evaluateNativeVesselTransactionfor the shared effect routine. Do not create another lifecycle registry or hide this solely in an app subclass.returnClosedPopupWorkspaceto policy/receipt glue or remove it; remove the app's replay-specificprojectDockCommitoverride and bound-close lease subscription once shared composition owns them. Report the removed code and remaining app responsibilities.The exact new callable/config names should be fixed in this ledger at intake after the existing owners are tested. This ticket specifies ownership and behavior; it does not prescribe a new module or a renamed copy of the app class.
Contract Ledger Matrix
WorkspaceSetparticipant pathCommit#run/WorkspaceSet#registerWorkspace#bindNativeWindowSourcereplayRow; ADR 0029 §2.8.3Acceptance Criteria
manager.Transaction/manager.transactionversusdashboard/dockboundary with the implemented call path. Generic managers contain no Dock imports, app names or tab-placement branches.Decision Record impact
aligned-with ADR 0029: worker-owned documents, reducer-based changes, and native close as a separate effect. The reusable facility preserves those boundaries. This is not authority to change the persisted topology schema or global close defaults.Out of Scope
Video/tour work; whole Demo B migration; new persistence formats; another transaction/history subsystem; changing the product's chosen return behavior; the separately owned popup parking/dropzone lane.
Avoided Traps
manager.Transactionbecause the operation is transactional.NativeLifecycle, or a wrapper that still requires every app to implement the algorithm.Related
#19273 · PR #19292 · #18314 · #18651 · #15247. The latter two concern commanded vessel close/gesture return; this follow-up concerns physical-close-triggered all-pane return and replay. #19101 is a release-note survey, not an implementation duplicate.
unowned-rationale: operator-requested follow-up recorded for the Dock engine queue; no implementation claim is made by filing it. A claimant should confirm the shared seam at intake before editing.
Origin Session ID: 01a0deee-3f9b-7180-ac35-f90129ccaa40
Retrieval Hint:
returnClosedPopupWorkspace onPopupLeaseExpired PopupWorkspace projectDockCommit manager.Transaction WorkspaceSet NativeLifecycle; source baseline PR #19292 atab7671628a5a1446fdeead7676d5a4731c45d301.Freshness and duplicate checks
Live latest-open sweep: latest 20 open Engine issues in created-descending order at 2026-09-26 immediately before creation at 20:08 UTC (newest #19295); no equivalent extraction. A2A: latest 30 messages across read states immediately before filing; no competing claim. Exact GitHub searches for popup close/return and WorkspaceSet surfaced #18651, #15247 and #19101; their bodies were read and scopes distinguished above. Own-assignment sweep: two open issues (#16151, #15526), neither on this surface.
MC rationale sweep: three-result searches on popup-close return,
NativeLifecycle/opaque Group ownership and the current app method names, plus the final problem-noun sweep. Recovered the earlier Group-owned lifecycle and shared-close decisions; no retrieved decision rejects this extraction. KB source/ticket queries were incomplete and used only as discovery hints; current source and ADR 0029 supplied the ownership evidence. Surface names in the ledger were checked directly. No new module placement is prescribed; the structural-pre-flight trigger must run if intake chooses one.Claimer contract — @neo-gpt, 2026-09-29
Selected discipline: ticket-intake + tech-debt-radar; medium, reversible extraction under ADR 0029. The runtime-owner and finite-JSON probes in the intake comment pin the existing owners. No new persisted format or global close policy is introduced.
Transactionrelease/lease envelopes;NativeLifecycle#isCurrentRelease/#isLeaseExpired/#withReleasedBindingTransaction#getBindingexposesgenerationToken, and Dock capture combines lineage plus counter to reject replacement during preparation; Group-ownedawaitingClosureand the full pending release-work promise live on that binding, clear on accepted rebind; Dock code queries expiry through the owner and returns a disposition, while only the owner updates closure state; sourcebindingExpiredcallback distinguishes formerly bound expiryWorkspaceSet#registeroptionalresolveReturnDescriptor/ runtimenativeWindoweffect seams, and#returnWorkspacecontext.valuesBeforeWorkspaceSet#getParticipantForBinding(bindingKey)bindingKey/ runtimenativeWindowseams{workspaceId, participant}, independently of semantic document-key spellingnullfor invalid/missing binding keys, missing/ineligible owners, or multiple eligible owners; no parallel registryWorkspace.nativeWindowClosePolicy/nativeWindowReturnTargetKeyplus#getDockParticipantSeams,#getNativeWindowReturnPlacements,#getNativeWindowRoute,#getNativeWindowOwnerWindowId,#getNativeWindowOpenConfig,#onNativeWindowDetached,#onDockWorkspaceReturnreturnorretain; an injected WorkspaceSet allows close-policy-only native sources without enabling tear-out gestures; view hooks provide route/presentation and product policy, not transaction algorithmsnullpreserves the existing consumer path; unknown observation retains until the separately identified lease boundaryNativeVesselTransaction#releaseWorkspace,#embodyWorkspace,#replayWorkspaceReturn(static callables)The
nativeWindowregistration value is a runtime effect/accessor descriptor, not a new landing-policy object;resolveDockReturnDescriptorremains the landing policy. No callback enters a semantic descriptor. The history operation label remainsreturnPopupWorkspacefor replay compatibility, while the public all-pane operation isreturnWorkspace. Its result exposes ordereditemIdseven with history depth zero. Binding keys resolve native slots independently of semantic participant keys. Unknown physical observations wait for the manager's configured reconnect lease; expiry of the same formerly-bound record is a separate recovery cause, including when observation was still pending.Current source at
dev@ede64fd051still has the same affected owner blobs as the intake baseline; the only intervening merge retires the unrelated conversion exit signal. Created 2026-09-26 / updated 2026-09-26: pre-stale under the 90+14-day workflow, no stale/exemption label. No assignee, parent, blocker or matching open PR exists. ADR successor-risk: adr-aligned. Classification: valid-as-written with this intake-derived ledger. Positive ROI: delete consumer transaction/lifecycle orchestration while giving independent Dock hosts the same operation.Core idioms loaded:
src/Neo.mjs,src/core/Base.mjs,src/state/Provider.mjs,src/data/Model.mjs,src/data/Store.mjs; window ids remain runtime render targets. Prior-art anchor: memory14b20212-63f9-4070-a0ba-f1665601d03a, session01a0deee-3f9b-7180-ac35-f90129ccaa40.Origin Session ID: 01a0ee37-7eaa-7d52-9869-ba5d0de51b43