Epic #17539 lifted the shared reducer/projection loop into Neo.dashboard.DockWorkspace, then migrated Workstation and the showcase examples. Its reviewed closeout matrix still names O-3 as a blocker: the tear-out/window-lifecycle half has no engine owner and FleetCockpit—the zero-grant host selected as the first consumer—still extends plain Neo.container.Base.
The dependency is explicit rather than inferred. Epic #17335’s formal review stops its FleetCockpit decomposition until this O-3 leaf lands, then requires a fresh census; its native subgraph is still empty.
At origin/dev2e1a63cf5c, apps/agentos/view/fleet/cockpit/Container.mjs is 4,085 physical lines / 1,795 code LOC. It repeats the class-owned dock holder loop (applyDockZoneOperation, getDockZoneDocument, onDockZoneDocumentChange, projectDockModel, refreshDockWorkspace, onDockCrossZoneDrop) and keeps the common tear-out mechanics beside product-specific popup and Fleet data behavior.
The full structure map currently fails before output with Cannot create a string longer than 0x1fffffe8 characters. Scoped maps succeeded: src/dashboard contains 30 files with DockWorkspace.mjs at 305 code LOC; the Fleet cockpit folder contains two files, with Container.mjs at 1,795 code LOC. No new file or directory is proposed.
The Problem
The base class owns the in-window reducer and projection transaction, but it stops immediately before the second repeated half: detach placement capture, admitted vessel ownership, exact semantic return, and worker window connect/disconnect routing. Workstation, Demo B, and FleetCockpit each implement those mechanics again.
The duplication is already behaviorally expensive. FleetCockpit cannot adopt the engine holder loop without threading its tear-out state through the existing hooks, so the flagship FM consumer remains on the retired host pattern. Conversely, lifting the whole app methods would be wrong: those methods also contain product policy that the engine must not know—Workstation/Demo B owner grants and staged embodiment, Demo B cross-window targets, and Fleet click-popouts plus owner-held return panes.
The reviewed epic boundary is therefore load-bearing: the engine owns admission, document mutation, and window lifecycle; the app owns embodiment and grant policy.
The Architectural Reality
A current-head three-host body comparison confirms the boundary rather than merely repeating the epic:
All three capture {tabsNodeId, index} before detachItem, delete refused captures, and reintegrate through semantic addTab with exact-home/fallback behavior.
All three maintain pre-terminal connections versus post-commit tearOutPanes, then reparent the same live pane into the admitted vessel.
Workstation and Demo B consume owner grants, staged embodiment, native routes, and workspace-target registration; FleetCockpit deliberately has zero grant references and uses tearOutPaneHandles / returningTearOutPanes.
Each top-level window handler also owns unrelated product flows. Those branches cannot move into the engine under a generic name.
DockWorkspace already provides the required holder hooks: getDockProjectionOptions, getPreservedItemIds, beforeRefreshDockWorkspace, resolvePane, and the serialized refresh transaction. The missing addition is a generic tear-out lifecycle with explicit hooks at the policy and live-instance boundaries, not another app helper or a generic DragZone change.
The Fix
Extend Neo.dashboard.DockWorkspace with the common tear-out state and lifecycle: placement capture/refusal cleanup, admitted connection versus committed ownership, semantic exact-position reintegration, and worker connect/disconnect registration for vesselFlow=tear-out.
Expose narrow template hooks for app-owned policy and embodiment:
optional grant admission/consumption (default no-op for Fleet);
retain Fleet click-popout, data, UI, and same-instance returningTearOutPanes policy app-side.
Remove only the duplicated imports, fields, and methods whose ownership moved. Workstation and Demo B migrations onto the new tear-out half remain separate reversible leaves.
Contract Ledger Matrix
Target Surface
Source of Authority
Proposed Behavior
Fallback
Docs
Evidence
DockWorkspace tear-out lifecycle
ADR 0029 + Epic #17539 O-3 + current three-host common bodies
One engine implementation owns detach placement, tear-out admission state, exact semantic return, and worker lifecycle routing
Engine units over terminal-first/connect-first/refusal/disconnect matrices
Grant-policy hook
Workstation/Demo B exact owner-grant checks; Fleet zero-grant specimen
Consumer may admit/consume its product grant without the engine naming a grant format
Default no-op admits only after engine-owned host/flow/admission-token checks
Hook JSDoc
Fleet no-grant green plus wrong-host/flow/token negatives
Adopt/return hooks
#16415 live-instance law + Fleet returningTearOutPanes
Engine exposes awaited/observable lifecycle moments; app moves or parks the live pane and can return the same instance
Hook failure fails the ownership transition loudly or settles through the documented no-home terminal; never silently destroys a live owner
Hook JSDoc
Same-instance Fleet tear-out/return control and late-write guard
FleetCockpit inheritance
DockWorkspace holder contract + current Fleet loop
Fleet extends DockWorkspace, inherits holder/projection/cross-zone methods, and contributes only existing hooks/configs
No new engine hook for Fleet-only data/UI sequences
Fleet class JSDoc
Method-origin census + owning unit/E2E suites
Theme/root classes
DockWorkspace.baseCls contract + Fleet SCSS
neo-dock-workspace remains the override anchor; fm-fleet-cockpit becomes consumer cls, while Neo.dashboard.Container remains loaded
Engine defaults continue on projected .neo-dashboard zones
Config JSDoc
Both root classes and computed token continuity
Fleet DockService holder
src/ai/client/DockService.mjs
Existing service reads and commits through inherited holder methods before and after a tear-out
Same fail-closed reducer errors
Existing holder docs
NL/unit operation before first commit and after reintegration
Decision Record impact
aligned-with ADR 0029 and the reviewed O-3 boundary on Epic #17539. No persisted dockZone.v1, wire vocabulary, or service topology changes.
Acceptance Criteria
DockWorkspace owns the common tear-out placement/admission/return/window-lifecycle path without importing any app module or naming a product grant format.
The engine path covers terminal-first and connect-first admission, refused detach, wrong host/flow/admission token, pre-terminal disconnect, committed disconnect, and exact-once semantic reintegration.
Grant policy, vessel opening/closing, live-pane embodiment, and non-tear-out window branches remain explicit consumer hooks with inert defaults.
Adopt and return lifecycle moments are observable, and FleetCockpit proves #16415's same-live-instance return through returningTearOutPanes.
FleetCockpit extends DockWorkspace; the six duplicated holder/projection methods and app-owned preview producer are removed rather than shadowing the engine implementation.
Fleet maps its persistent toolbar, shell index, pane resolver, control-bar sync, and detached/torn preservation onto existing DockWorkspace hooks with no Fleet-only hook added to the engine.
The rendered root keeps both neo-dock-workspace and fm-fleet-cockpit; the projected .neo-dashboard token floor and current light/dark Fleet values remain unchanged.
Existing click-popout, gesture tear-out, splitter resize, perspective switch, cross-zone drop, and DockService/NL journeys stay green; the tear-out witness crosses the real gesture threshold.
A source mutation that bypasses the engine lifecycle or shadows an inherited holder method turns the focused matrix red.
Workstation and Demo B remain behaviorally unchanged and keep their app implementations until their own migration leaves.
Out of Scope
Migrating Workstation or Demo B onto the new tear-out lifecycle.
Cross-window workspace transfer, vessel conversion/parking, click-popout product flows, or a new grant schema.
Fleet information design, data feeds, SCSS redesign, or pane decomposition.
Closing Epic #17539; this leaf discharges O-3 only.
Avoided Traps
Core-only Fleet parent swap. Leaves O-3's tear-out/window lifecycle owner unresolved and certifies the wrong blocker.
Moving whole app window handlers into the engine. Imports product flows and grant policy into a generic class.
One hook per Fleet sequence. Hook admission requires a named engine lifecycle moment, an inert minimal-consumer default, and product policy left app-side.
Recreating panes on return. Violates the owner-held live-instance law and loses in-flight state.
Migrating all three rich hosts in one PR. Hides behavior changes and destroys independent rollback.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-24T07:00:57.607Z; exact all-state and local-history searches found no equivalent. A2A in-flight sweep: latest 30 messages across read states at 2026-08-24T07:00:57.607Z; no competing claim. Semantic ticket sweep found no equivalent.
Context
Epic
#17539lifted the shared reducer/projection loop intoNeo.dashboard.DockWorkspace, then migrated Workstation and the showcase examples. Its reviewed closeout matrix still names O-3 as a blocker: the tear-out/window-lifecycle half has no engine owner and FleetCockpit—the zero-grant host selected as the first consumer—still extends plainNeo.container.Base.The dependency is explicit rather than inferred. Epic #17335’s formal review stops its FleetCockpit decomposition until this O-3 leaf lands, then requires a fresh census; its native subgraph is still empty.
At
origin/dev2e1a63cf5c,apps/agentos/view/fleet/cockpit/Container.mjsis 4,085 physical lines / 1,795 code LOC. It repeats the class-owned dock holder loop (applyDockZoneOperation,getDockZoneDocument,onDockZoneDocumentChange,projectDockModel,refreshDockWorkspace,onDockCrossZoneDrop) and keeps the common tear-out mechanics beside product-specific popup and Fleet data behavior.The full structure map currently fails before output with
Cannot create a string longer than 0x1fffffe8 characters. Scoped maps succeeded:src/dashboardcontains 30 files withDockWorkspace.mjsat 305 code LOC; the Fleet cockpit folder contains two files, withContainer.mjsat 1,795 code LOC. No new file or directory is proposed.The Problem
The base class owns the in-window reducer and projection transaction, but it stops immediately before the second repeated half: detach placement capture, admitted vessel ownership, exact semantic return, and worker window connect/disconnect routing. Workstation, Demo B, and FleetCockpit each implement those mechanics again.
The duplication is already behaviorally expensive. FleetCockpit cannot adopt the engine holder loop without threading its tear-out state through the existing hooks, so the flagship FM consumer remains on the retired host pattern. Conversely, lifting the whole app methods would be wrong: those methods also contain product policy that the engine must not know—Workstation/Demo B owner grants and staged embodiment, Demo B cross-window targets, and Fleet click-popouts plus owner-held return panes.
The reviewed epic boundary is therefore load-bearing: the engine owns admission, document mutation, and window lifecycle; the app owns embodiment and grant policy.
The Architectural Reality
A current-head three-host body comparison confirms the boundary rather than merely repeating the epic:
{tabsNodeId, index}beforedetachItem, delete refused captures, and reintegrate through semanticaddTabwith exact-home/fallback behavior.tearOutPanes, then reparent the same live pane into the admitted vessel.tearOutPaneHandles/returningTearOutPanes.DockWorkspacealready provides the required holder hooks:getDockProjectionOptions,getPreservedItemIds,beforeRefreshDockWorkspace,resolvePane, and the serialized refresh transaction. The missing addition is a generic tear-out lifecycle with explicit hooks at the policy and live-instance boundaries, not another app helper or a genericDragZonechange.The Fix
Neo.dashboard.DockWorkspacewith the common tear-out state and lifecycle: placement capture/refusal cleanup, admitted connection versus committed ownership, semantic exact-position reintegration, and worker connect/disconnect registration forvesselFlow=tear-out.openTearOutVessel/closeTearOutVessel;getDockProjectionOptions.DockWorkspaceas the first consumer:flex: 1;syncControlBar, pane resolution, and preserved detached/torn ids onto existing hooks;returningTearOutPanespolicy app-side.Contract Ledger Matrix
DockWorkspacetear-out lifecycle#16415live-instance law + FleetreturningTearOutPanesDockWorkspaceholder contract + current Fleet loopDockWorkspace, inherits holder/projection/cross-zone methods, and contributes only existing hooks/configsDockWorkspace.baseClscontract + Fleet SCSSneo-dock-workspaceremains the override anchor;fm-fleet-cockpitbecomes consumercls, whileNeo.dashboard.Containerremains loaded.neo-dashboardzonesDockServiceholdersrc/ai/client/DockService.mjsDecision Record impact
aligned-with ADR 0029and the reviewed O-3 boundary on Epic #17539. No persisteddockZone.v1, wire vocabulary, or service topology changes.Acceptance Criteria
DockWorkspaceowns the common tear-out placement/admission/return/window-lifecycle path without importing any app module or naming a product grant format.#16415's same-live-instance return throughreturningTearOutPanes.DockWorkspace; the six duplicated holder/projection methods and app-owned preview producer are removed rather than shadowing the engine implementation.DockWorkspacehooks with no Fleet-only hook added to the engine.neo-dock-workspaceandfm-fleet-cockpit; the projected.neo-dashboardtoken floor and current light/dark Fleet values remain unchanged.Out of Scope
DragZone.proxyParentId,DockTearOutgesture grammar, persisted documents, orneo.harness.*wire identifiers.Avoided Traps
Related
Parent: #17539
Blocking predecessor for: #17335
Related: #17541 Related: #17546 Related: #17630 Related: #16415 Related: #16322
Live latest-open sweep: checked the latest 20 open issues at 2026-08-24T07:00:57.607Z; exact all-state and local-history searches found no equivalent. A2A in-flight sweep: latest 30 messages across read states at 2026-08-24T07:00:57.607Z; no competing claim. Semantic ticket sweep found no equivalent.
Origin Session ID: 0dc1379e-5329-4fba-80ca-f6466822f7c9
Retrieval Hint:
FleetCockpit DockWorkspace O-3 tear-out lifecycle engine admission document mutation window connect adopt return hooks same live pane