Context
#14850 (merged) wired the dock's first gesture: a tab reorders within its own tabs node. The next gesture — the one the operator has explicitly asked to SEE — is dragging a tab header and releasing it into a different drop zone (another tabs node, or an edge/split of the workspace). That gesture is blocked on one missing piece: the drop-zone producer.
The Problem
The dock's drag pipeline is executor-complete but producer-empty:
- Built:
DockZoneModel semantic ops (moveItem, splitNode, addTab); AgentOS.view.DockPreview (previewToOperation descriptor + the transient visual affordance renderer); the example's applyDockZoneOperation / onDockZoneDocumentChange commit loop.
- Missing (this leaf): the producer — raw drag geometry → a
neo.harness.dockPreview.v1 object. DockPreview's own JSDoc names it out of scope ("The producer (raw drag geometry → dockPreview) … a separate docking leaf"), and it is wired nowhere — a grep for any producer of a dockPreview.v1 / placement kind returns zero hits.
V-B-A correction to #14769's premise: #14769 maps its previewFor owner-seam to "the landed hover→dockPreview compute path." That path is not landed. src/dashboard/CrossWindowDragTarget.mjs exposes hitTest (line 71) and previewFor (line 79) as null owner-seams — fail-closed shells awaiting an owner, not implementations. So #14769 (cross-window) depends-on this producer to fill those seams; it cannot bind them to a compute path that does not exist yet.
The Architectural Reality
- Owner tier:
src/dashboard/DockLayoutAdapter.mjs (projection SSOT) + apps/agentos/view/DockPreview.mjs (the render seam the app passes in). src/dashboard/ imports no app module — the app-side workspace composition passes the preview seam.
- Reuse, not rebuild (no parallel drag system): the producer hooks the EXISTING drag lifecycle. The tab-header drag already fires via the tab-header SortZone (
#14850); the container SortZone fires dragBoundaryEntry / dragBoundaryExit (src/draggable/container/SortZone.mjs:290,323); the dashboard SortZone + DragCoordinator own cross-container/cross-window remote drag. The producer is a hit-test + preview-compute layered on those events — it never adds a second drag system.
- Placement vocabulary is already fixed by
DockPreview.VALID_PLACEMENT_KINDS: edge-{top,right,bottom,left}, split-{before,after}, tab-{before,after,into}, rejected.
- On drop:
DockPreview.previewToOperation(preview) → a semantic descriptor → applyDockZoneOperation → commit + re-project. Already landed; the producer just has to feed it a valid preview.
The Fix
Scaffold the producer + land the tab-into placement first (drop a tab into another tabs node → moveItem(itemId, targetNodeId)): a workspace hit-test that maps a pointer (x,y) over the projected dock rects to a placement kind, produces a neo.harness.dockPreview.v1, feeds DockPreview (renders the affordance during the drag), and on drop routes through previewToOperation → applyDockZoneOperation. Wire it into the dock example's drag lifecycle. edge-* / split-* placements (drop-to-split) are follow-up slices on the same scaffold.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
producer: (pointer, projected rects) → dockPreview.v1 |
learn/agentos/HarnessDockZoneModel.md schema; DockPreview.VALID_PLACEMENT_KINDS |
Resolves the zone + placement kind under the pointer; emits a valid preview (tab-into first) |
Off any zone → placement.kind: 'rejected' → affordance clears, no mutation |
inline + DockPreview JSDoc updated (producer now landed) |
whitebox-e2e drops a tab cross-zone; unit test for the hit-test → placement mapping |
CrossWindowDragTarget.hitTest / previewFor seams |
CrossWindowDragTarget.mjs:71,79 (null seams) |
Producer is the implementation #14769 binds these to |
fail-closed already enforced by the shell |
— |
#14769 can cite this leaf as the landed producer |
Decision Record impact
aligned-with ADR 0029 (§"no parallel drag system"; the producer rides the existing SortZone/DragCoordinator lifecycle). Corrects a stale premise in #14769 (its previewFor compute path is unbuilt) — #14769 becomes blocked-by this leaf.
Acceptance Criteria
Out of Scope
edge-* / split-* drop-to-split placements — follow-up slices on this scaffold.
- Cross-window / foreign-window transfer (
#14769, now depends-on this) and its two-window demo (#14772).
- Grouped-node drag (
#14770), transition animation (#14779).
Related
Parent #13158 · authority ADR 0029 §2.3/§2.6 · unblocks #14769 (fills its hitTest/previewFor seams) → #14772 · renderer apps/agentos/view/DockPreview.mjs · builds on #14850.
Live latest-open sweep: checked latest open ai/enhancement issues on 2026-07-05; no equivalent producer/drop-zone leaf (#14769 is cross-window, #14771 overflow, #14772 demo). A2A in-flight sweep: mailbox scanned same session, no competing claim.
Origin Session ID: 9e42a8de-4291-46fc-944e-92ceb0db1748
Retrieval Hint: "dock drop-zone producer hit-test dockPreview.v1 previewToOperation cross-zone tab-into CrossWindowDragTarget seams"
Context
#14850(merged) wired the dock's first gesture: a tab reorders within its own tabs node. The next gesture — the one the operator has explicitly asked to SEE — is dragging a tab header and releasing it into a different drop zone (another tabs node, or an edge/split of the workspace). That gesture is blocked on one missing piece: the drop-zone producer.The Problem
The dock's drag pipeline is executor-complete but producer-empty:
DockZoneModelsemantic ops (moveItem,splitNode,addTab);AgentOS.view.DockPreview(previewToOperationdescriptor + the transient visual affordance renderer); the example'sapplyDockZoneOperation/onDockZoneDocumentChangecommit loop.neo.harness.dockPreview.v1object.DockPreview's own JSDoc names it out of scope ("The producer (raw drag geometry →dockPreview) … a separate docking leaf"), and it is wired nowhere — agrepfor any producer of adockPreview.v1/ placement kind returns zero hits.V-B-A correction to
#14769's premise:#14769maps itspreviewForowner-seam to "the landed hover→dockPreviewcompute path." That path is not landed.src/dashboard/CrossWindowDragTarget.mjsexposeshitTest(line 71) andpreviewFor(line 79) asnullowner-seams — fail-closed shells awaiting an owner, not implementations. So#14769(cross-window) depends-on this producer to fill those seams; it cannot bind them to a compute path that does not exist yet.The Architectural Reality
src/dashboard/DockLayoutAdapter.mjs(projection SSOT) +apps/agentos/view/DockPreview.mjs(the render seam the app passes in).src/dashboard/imports no app module — the app-side workspace composition passes the preview seam.#14850); the container SortZone firesdragBoundaryEntry/dragBoundaryExit(src/draggable/container/SortZone.mjs:290,323); the dashboard SortZone +DragCoordinatorown cross-container/cross-window remote drag. The producer is a hit-test + preview-compute layered on those events — it never adds a second drag system.DockPreview.VALID_PLACEMENT_KINDS:edge-{top,right,bottom,left},split-{before,after},tab-{before,after,into},rejected.DockPreview.previewToOperation(preview)→ a semantic descriptor →applyDockZoneOperation→ commit + re-project. Already landed; the producer just has to feed it a valid preview.The Fix
Scaffold the producer + land the
tab-intoplacement first (drop a tab into another tabs node →moveItem(itemId, targetNodeId)): a workspace hit-test that maps a pointer(x,y)over the projected dock rects to a placement kind, produces aneo.harness.dockPreview.v1, feedsDockPreview(renders the affordance during the drag), and on drop routes throughpreviewToOperation→applyDockZoneOperation. Wire it into the dock example's drag lifecycle.edge-*/split-*placements (drop-to-split) are follow-up slices on the same scaffold.Contract Ledger Matrix
(pointer, projected rects) → dockPreview.v1learn/agentos/HarnessDockZoneModel.mdschema;DockPreview.VALID_PLACEMENT_KINDStab-intofirst)placement.kind: 'rejected'→ affordance clears, no mutationDockPreviewJSDoc updated (producer now landed)CrossWindowDragTarget.hitTest/previewForseamsCrossWindowDragTarget.mjs:71,79(null seams)#14769binds these to#14769can cite this leaf as the landed producerDecision Record impact
aligned-with ADR 0029(§"no parallel drag system"; the producer rides the existing SortZone/DragCoordinator lifecycle). Corrects a stale premise in#14769(itspreviewForcompute path is unbuilt) —#14769becomesblocked-bythis leaf.Acceptance Criteria
tab-intoat minimum; off-workspace →rejected) — unit-tested against known rects.neo.harness.dockPreview.v1;DockPreviewrenders the affordance during a live drag.moveItemto that node (App-Worker truth: the item leaves its source node and lands in the target) — fail-closed onrejected/errors[].Out of Scope
edge-*/split-*drop-to-split placements — follow-up slices on this scaffold.#14769, now depends-on this) and its two-window demo (#14772).#14770), transition animation (#14779).Related
Parent
#13158· authority ADR 0029 §2.3/§2.6 · unblocks#14769(fills itshitTest/previewForseams) →#14772· rendererapps/agentos/view/DockPreview.mjs· builds on#14850.Live latest-open sweep: checked latest open
ai/enhancementissues on 2026-07-05; no equivalent producer/drop-zone leaf (#14769is cross-window,#14771overflow,#14772demo). A2A in-flight sweep: mailbox scanned same session, no competing claim.Origin Session ID: 9e42a8de-4291-46fc-944e-92ceb0db1748 Retrieval Hint: "dock drop-zone producer hit-test dockPreview.v1 previewToOperation cross-zone tab-into CrossWindowDragTarget seams"