Context
PR #13209 renders stable dock splitter affordances and exposes DockLayoutAdapter.createResizeSplitOperation() so a later drag handler can emit the existing semantic resizeSplit operation. Ada approved that PR with no required changes, but named one durable follow-up: the Harness view still needs live pointer/drag wiring so the affordance becomes interactive instead of only projected metadata.
Release classification: post-release (Approve+Follow-Up from PR #13209; non-blocking for the #13206 merge gate).
Live duplicate sweep: checked the latest 30 open issues at 2026-06-14T09:50:50Z; exact live search for splitter drag resizeSplit Harness view returned no duplicate; labels enhancement, ai, and architecture exist. A2A latest-30 all-status sweep at the same boundary found no overlapping splitter-drag lane claim. Local and archived search found only the new #13206 adapter seam, the existing Neo.component.Splitter, and unrelated historical dashboard/splitter work.
The Problem
The dock-zone split model now has three separate pieces:
- persisted semantic state owned by
DockZoneModel
- projected splitter affordances emitted by
DockLayoutAdapter
- future pointer/drag input that calculates a new child-size vector
The missing piece is the live Harness-view handler that turns a user drag into a committed resizeSplit operation. Leaving that work only as a PR post-merge checkbox makes it easy to lose after PR #13209 is merged, even though the user-facing resize affordance depends on it.
The Architectural Reality
learn/agentos/HarnessDockZoneModel.md defines resizeSplit as the semantic operation that updates a split node's normalized child sizes. The same contract says the dock model does not own pointer events, preview-only state must stay runtime-only, and the adapter must not read DOM rectangles, pointer coordinates, or drag-zone internals while projecting committed layout.
src/dashboard/DockZoneModel.mjs is the persisted-operation authority. Its class summary states operations are pure static functions over a dockZone.v1 document, return {document, errors}, fail closed, and keep runtime pixels, DOMRects, and preview state out of the persisted model.
src/dashboard/DockLayoutAdapter.mjs now projects splitter metadata and exposes createResizeSplitOperation(splitter, sizes). That helper intentionally accepts resolved positive sizes, clones size arrays, and emits {operation: 'resizeSplit', splitNodeId, sizes} without doing pointer math itself.
src/component/Splitter.mjs is not the correct authority for this model. It uses drag:start / drag:end and mutates sibling inline style.width / style.height after resolving DOM geometry. That existing component can inform UI affordance behavior, but the Harness dock path must commit through DockZoneModel.applyOperation() rather than letting a splitter component mutate persisted split sizes or sibling styles as the source of truth.
The Fix
Wire the Harness/dashboard view layer that owns rendered dock splitter components so pointer or drag completion:
- reads the projected splitter metadata from the affordance rendered by
DockLayoutAdapter
- resolves the affected split children into a positive size vector in model child order
- calls
DockLayoutAdapter.createResizeSplitOperation(splitter, sizes)
- commits that descriptor through
DockZoneModel.applyOperation() or the owning Harness state reducer
- re-renders from the committed dock-zone document
The implementation may wrap or adapt Neo.component.Splitter only if it prevents direct sibling style mutation from becoming the persistence authority. A Harness-specific handler is acceptable when it stays inside the existing drag lifecycle boundary and does not create a parallel docking pointer system.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
| Harness dock splitter drag completion handler |
This issue plus learn/agentos/HarnessDockZoneModel.md drag integration boundary |
On drag completion, compute positive split sizes in child order and emit the semantic resizeSplit descriptor. |
Invalid metadata, non-finite sizes, missing split node, or rejected model operation leaves the previous dock-zone document unchanged and surfaces recoverable UI state. |
JSDoc on the handler or controller method. |
Focused unit or whitebox coverage proving handler-to-model commit and reject path. |
DockLayoutAdapter.createResizeSplitOperation(splitter, sizes) |
PR #13209 / issue #13206 adapter seam |
Convert projected splitter metadata and resolved sizes into {operation: 'resizeSplit', splitNodeId, sizes} without pointer math or model mutation. |
Missing metadata still returns the descriptor shape; downstream model application rejects invalid split ids. |
Existing JSDoc is sufficient unless the handler adds stricter input rules. |
Unit coverage that the handler calls this helper and the descriptor commits through DockZoneModel.applyOperation(). |
DockZoneModel.applyOperation({operation: 'resizeSplit', ...}) |
learn/agentos/HarnessDockZoneModel.md operations contract and src/dashboard/DockZoneModel.mjs |
Apply valid split-size changes to a cloned document and return {document, errors}. |
Invalid references or invalid sizes fail closed with original document plus errors. |
No new docs unless behavior changes. |
Existing model tests plus handler-level commit/rejection tests. |
Decision Record impact
aligned-with ADR 0020. The ticket implements a leaf under the Harness docking contract pointer in learn/agentos/decisions/0020-agent-harness-concept.md; it does not amend the ADR. It also aligns with learn/agentos/HarnessDockZoneModel.md, especially the operation table, drag integration boundary, and split projection rule that resizable splitters write back through resizeSplit rather than mutating persisted sizes directly.
Acceptance Criteria
Out of Scope
- Generic drag-to-dock preview/drop wiring for tab, split, or edge targets.
- Auto-hide, pin/unpin, perspective storage, grouped drag, or detached-window restore.
- Replacing
Neo.component.Splitter globally.
- Changing the persisted dock-zone schema or adding another resize operation.
Avoided Traps
- Do not make
Neo.component.Splitter the persistence authority. Its current behavior mutates sibling inline styles, while dock layout persistence is model-owned.
- Do not put pointer math into
DockLayoutAdapter.project(). Projection consumes committed model state only.
- Do not persist hover rectangles, DOMRects, window ids, pointer coordinates, or drag preview payloads.
- Do not add a docking-specific pointer manager that bypasses the existing drag lifecycle unless a separate architectural decision explicitly changes the drag integration boundary.
Related
Parent: #13158
Related: #13206, #13209, #13012, #13030
Origin Session ID: 019ec50d-5a01-7b11-9490-50f3241e8cfc
Handoff Retrieval Hints: Harness dock splitter drag resizeSplit DockLayoutAdapter createResizeSplitOperation DockZoneModel applyOperation PR 13209
Context
PR
#13209renders stable dock splitter affordances and exposesDockLayoutAdapter.createResizeSplitOperation()so a later drag handler can emit the existing semanticresizeSplitoperation. Ada approved that PR with no required changes, but named one durable follow-up: the Harness view still needs live pointer/drag wiring so the affordance becomes interactive instead of only projected metadata.Release classification: post-release (Approve+Follow-Up from PR
#13209; non-blocking for the#13206merge gate).Live duplicate sweep: checked the latest 30 open issues at 2026-06-14T09:50:50Z; exact live search for
splitter drag resizeSplit Harness viewreturned no duplicate; labelsenhancement,ai, andarchitectureexist. A2A latest-30 all-status sweep at the same boundary found no overlapping splitter-drag lane claim. Local and archived search found only the new#13206adapter seam, the existingNeo.component.Splitter, and unrelated historical dashboard/splitter work.The Problem
The dock-zone split model now has three separate pieces:
DockZoneModelDockLayoutAdapterThe missing piece is the live Harness-view handler that turns a user drag into a committed
resizeSplitoperation. Leaving that work only as a PR post-merge checkbox makes it easy to lose after PR#13209is merged, even though the user-facing resize affordance depends on it.The Architectural Reality
learn/agentos/HarnessDockZoneModel.mddefinesresizeSplitas the semantic operation that updates a split node's normalized child sizes. The same contract says the dock model does not own pointer events, preview-only state must stay runtime-only, and the adapter must not read DOM rectangles, pointer coordinates, or drag-zone internals while projecting committed layout.src/dashboard/DockZoneModel.mjsis the persisted-operation authority. Its class summary states operations are pure static functions over adockZone.v1document, return{document, errors}, fail closed, and keep runtime pixels, DOMRects, and preview state out of the persisted model.src/dashboard/DockLayoutAdapter.mjsnow projects splitter metadata and exposescreateResizeSplitOperation(splitter, sizes). That helper intentionally accepts resolved positive sizes, clones size arrays, and emits{operation: 'resizeSplit', splitNodeId, sizes}without doing pointer math itself.src/component/Splitter.mjsis not the correct authority for this model. It usesdrag:start/drag:endand mutates sibling inlinestyle.width/style.heightafter resolving DOM geometry. That existing component can inform UI affordance behavior, but the Harness dock path must commit throughDockZoneModel.applyOperation()rather than letting a splitter component mutate persisted split sizes or sibling styles as the source of truth.The Fix
Wire the Harness/dashboard view layer that owns rendered dock splitter components so pointer or drag completion:
DockLayoutAdapterDockLayoutAdapter.createResizeSplitOperation(splitter, sizes)DockZoneModel.applyOperation()or the owning Harness state reducerThe implementation may wrap or adapt
Neo.component.Splitteronly if it prevents direct sibling style mutation from becoming the persistence authority. A Harness-specific handler is acceptable when it stays inside the existing drag lifecycle boundary and does not create a parallel docking pointer system.Contract Ledger Matrix
learn/agentos/HarnessDockZoneModel.mddrag integration boundaryresizeSplitdescriptor.DockLayoutAdapter.createResizeSplitOperation(splitter, sizes){operation: 'resizeSplit', splitNodeId, sizes}without pointer math or model mutation.DockZoneModel.applyOperation().DockZoneModel.applyOperation({operation: 'resizeSplit', ...})learn/agentos/HarnessDockZoneModel.mdoperations contract andsrc/dashboard/DockZoneModel.mjs{document, errors}.Decision Record impact
aligned-with ADR 0020. The ticket implements a leaf under the Harness docking contract pointer in
learn/agentos/decisions/0020-agent-harness-concept.md; it does not amend the ADR. It also aligns withlearn/agentos/HarnessDockZoneModel.md, especially the operation table, drag integration boundary, and split projection rule that resizable splitters write back throughresizeSplitrather than mutating persistedsizesdirectly.Acceptance Criteria
resizeSplitoperation against the owning dock-zone document.splitNodeId, orientation, boundary index) rather than DOM position as persistent identity.DockLayoutAdapter.createResizeSplitOperation().Out of Scope
Neo.component.Splitterglobally.Avoided Traps
Neo.component.Splitterthe persistence authority. Its current behavior mutates sibling inline styles, while dock layout persistence is model-owned.DockLayoutAdapter.project(). Projection consumes committed model state only.Related
Parent: #13158
Related: #13206, #13209, #13012, #13030
Origin Session ID: 019ec50d-5a01-7b11-9490-50f3241e8cfc
Handoff Retrieval Hints:
Harness dock splitter drag resizeSplit DockLayoutAdapter createResizeSplitOperation DockZoneModel applyOperation PR 13209