Epic #13158 identifies interactive splitter resize as the first QT-parity docking polish gap under the Agent Harness line. The current source is ahead of the epic body's oldest wording: DockZoneModel.resizeSplit() and descriptor dispatch already exist, and DockLayoutAdapter.projectSplitNode() already projects persisted split sizes as child flex values.
The remaining near-term gap for this leaf is the adapter/UI seam named in learn/agentos/HarnessDockZoneModel.md Split Projection: resizable splitters should sit between projected children and write back semantic size changes through resizeSplit, not mutate persisted sizes directly from pointer handlers.
Scope correction, 2026-06-14: cross-family review on PR #13209 surfaced that this ticket's original AC wording mixed the adapter/seam proof with the later live Harness-view pointer/drag handler. This ticket is now scoped to the delivered splitter affordance, semantic descriptor helper, and descriptor-to-model commit proof. Live pointer/drag wiring is tracked separately in #13213 under the same parent epic.
Release classification: ON Project 13 Agent Harness board - child leaf under #13158 / #13012, M3 dashboard composition polish.
Live latest-open sweep: checked the latest 20 open issues at 2026-06-14T09:21:19Z; no equivalent leaf found. Exact live searches for DockZoneModel splitter resize, resizeSplit splitter, and dock splitter resize resizeSplit returned no open duplicate beyond parent #13158. A2A in-flight sweep checked the latest 30 messages via a fresh Memory Core client bound as @neo-gpt; no recent [lane-claim] / [lane-intent] overlapped this scope. Follow-up #13213 now owns the live Harness-view pointer/drag interaction.
The Problem
The model contract is ready for persisted split ratios, but the rendered dock layout still needs a stable splitter affordance and semantic helper before the live Harness-view handler can safely exist. Without a scoped adapter seam, a future interaction implementation could cut around the contract by wiring pointer handlers straight to node.sizes, storing transient geometry, or introducing a second docking-specific drag pipeline.
no DOMRect, pointer coordinates, windowId, preview payloads, functions, or live component references in serialized layout data;
no parallel drag system.
The leaf should make the first affordance and semantic commit seam real while preserving the already-landed model boundary. The later user-facing drag gesture is a separate Harness-view integration leaf, now #13213.
The Architectural Reality
learn/agentos/HarnessDockZoneModel.md defines resizeSplit(splitNodeId, sizes) and says future resizable splitters sit between projected children and write through that operation.
src/dashboard/DockZoneModel.mjs implements resizeSplit(), normalizeSplitSizes(), and applyOperation({operation: 'resizeSplit', ...}) with fail-closed validation.
src/dashboard/DockLayoutAdapter.mjs projects split nodes as ordinary containers with child flex values; this ticket extends that projection with splitter affordance metadata and a pure descriptor helper.
This ticket therefore does not add the model operation or the live pointer/drag handler. It adds the adapter/UI seam and focused commit proof that consumes the existing operation. #13213 owns the later Harness-view interaction path.
The Fix
Add the minimal dashboard-layer splitter affordance seam needed for split resize:
Extend the split projection / harness dock rendering path so each adjacent pair of projected split children has a stable splitter affordance with the owning splitNodeId, child boundary index, and orientation available to a later interaction handler.
Add a semantic helper that converts resolved child sizes into {operation: 'resizeSplit', splitNodeId, sizes}.
Keep transient pointer geometry runtime-only. Pixel deltas may be used by a later handler to compute ratios for the event frame, but only normalized positive sizes enter the dock-zone document.
Add focused tests proving the projected affordance metadata exists and that a simulated resolved-size resize commits through resizeSplit without direct sizes mutation.
Render/emit one splitter affordance between each adjacent pair of split children, carrying splitNodeId, boundary index, and orientation.
If a split has fewer than two children, render no splitter affordance.
Inline JSDoc / adapter comments only if the seam is not self-explanatory.
Focused adapter/unit test verifies affordance metadata for horizontal and vertical splits.
Resize commit descriptor helper
DockZoneModel.resizeSplit() + applyOperation()
Resolved splitter sizes produce {operation: 'resizeSplit', splitNodeId, sizes} and the focused proof commits that descriptor through the existing semantic operation.
Invalid target or invalid ratios leave the original document unchanged and surface the existing errors.
PR body evidence.
Unit test asserts descriptor shape, descriptor path, final normalized sizes, and source-model non-mutation.
Runtime geometry boundary
HarnessDockZoneModel.md Serializable vs Runtime State
Pointer pixels / DOMRects / drag-frame state stay outside persisted dock-zone and saved-layout documents.
Fail closed if any runtime-only field is about to enter committed model input.
Existing contract doc remains authoritative.
Test includes a persisted document assertion with no pointer / DOMRect / window fields.
Decision Record impact
Aligned-with ADR 0020. This is an H1/Harness dashboard composition leaf under the Agent Harness concept; it preserves the ADR guardrails around JSON-first state, benchmark-before-perf-claims, restart-affordance awareness, and dashboard-layer ownership.
Acceptance Criteria
Split projections expose/render a stable splitter affordance between adjacent split children for both horizontal and vertical split nodes.
Projected splitter affordances carry the metadata needed by a later handler: splitNodeId, boundary index, and orientation.
A resolved-size resize can be converted into a resizeSplit semantic descriptor and committed through the existing DockZoneModel.resizeSplit() / applyOperation() path.
Invalid resize descriptor input fails closed with the original document unchanged.
Persisted dock-zone / saved-layout data remains JSON-only and contains no pointer coordinates, DOMRect, windowId, preview payloads, functions, or live component references.
Focused verification covers adapter/affordance metadata, descriptor-to-model commit, source-model non-mutation, and runtime-geometry rejection at the narrowest practical level.
Out of Scope
Live Harness-view pointer/drag/keyboard interaction wiring for the rendered splitter; tracked in #13213.
Adding or changing the resizeSplit model operation itself; it already exists.
Auto-hide / pin sidebars, named perspectives, grouped tab drag, and tab overflow.
Cross-window choreography, popup terminal drop, or OS-window drag reintegration.
A core src/layout/Dock primitive or new generic splitter framework.
Public performance claims before #13032 provides benchmark evidence.
Avoided Traps
Direct pointer-handler mutation of node.sizes is rejected; the operation seam owns persistence.
Persisting runtime geometry is rejected; only semantic normalized ratios survive.
Adding a new docking-specific drag substrate is rejected; use the existing dashboard/drag interaction path or keep the interaction local to the rendered splitter affordance until it commits a semantic operation.
Re-filing a model-operation ticket is rejected because current source already ships DockZoneModel.resizeSplit().
Collapsing the live interaction into this seam leaf is rejected after PR #13209 review; the durable follow-up is #13213.
Context
Epic #13158 identifies interactive splitter resize as the first QT-parity docking polish gap under the Agent Harness line. The current source is ahead of the epic body's oldest wording:
DockZoneModel.resizeSplit()and descriptor dispatch already exist, andDockLayoutAdapter.projectSplitNode()already projects persisted splitsizesas childflexvalues.The remaining near-term gap for this leaf is the adapter/UI seam named in
learn/agentos/HarnessDockZoneModel.mdSplit Projection: resizable splitters should sit between projected children and write back semantic size changes throughresizeSplit, not mutate persistedsizesdirectly from pointer handlers.Scope correction, 2026-06-14: cross-family review on PR
#13209surfaced that this ticket's original AC wording mixed the adapter/seam proof with the later live Harness-view pointer/drag handler. This ticket is now scoped to the delivered splitter affordance, semantic descriptor helper, and descriptor-to-model commit proof. Live pointer/drag wiring is tracked separately in #13213 under the same parent epic.Release classification: ON Project 13 Agent Harness board - child leaf under #13158 / #13012, M3 dashboard composition polish.
Live latest-open sweep: checked the latest 20 open issues at 2026-06-14T09:21:19Z; no equivalent leaf found. Exact live searches for
DockZoneModel splitter resize,resizeSplit splitter, anddock splitter resize resizeSplitreturned no open duplicate beyond parent #13158. A2A in-flight sweep checked the latest 30 messages via a fresh Memory Core client bound as@neo-gpt; no recent[lane-claim]/[lane-intent]overlapped this scope. Follow-up #13213 now owns the live Harness-view pointer/drag interaction.The Problem
The model contract is ready for persisted split ratios, but the rendered dock layout still needs a stable splitter affordance and semantic helper before the live Harness-view handler can safely exist. Without a scoped adapter seam, a future interaction implementation could cut around the contract by wiring pointer handlers straight to
node.sizes, storing transient geometry, or introducing a second docking-specific drag pipeline.That would violate the #13158 guardrails:
DOMRect, pointer coordinates,windowId, preview payloads, functions, or live component references in serialized layout data;The leaf should make the first affordance and semantic commit seam real while preserving the already-landed model boundary. The later user-facing drag gesture is a separate Harness-view integration leaf, now #13213.
The Architectural Reality
learn/agentos/HarnessDockZoneModel.mddefinesresizeSplit(splitNodeId, sizes)and says future resizable splitters sit between projected children and write through that operation.src/dashboard/DockZoneModel.mjsimplementsresizeSplit(),normalizeSplitSizes(), andapplyOperation({operation: 'resizeSplit', ...})with fail-closed validation.test/playwright/unit/dashboard/DockZoneModel.spec.mjsalready covers valid resize normalization, invalid resize fail-closed behavior, and descriptor dispatch.src/dashboard/DockLayoutAdapter.mjsprojects split nodes as ordinary containers with childflexvalues; this ticket extends that projection with splitter affordance metadata and a pure descriptor helper.This ticket therefore does not add the model operation or the live pointer/drag handler. It adds the adapter/UI seam and focused commit proof that consumes the existing operation. #13213 owns the later Harness-view interaction path.
The Fix
Add the minimal dashboard-layer splitter affordance seam needed for split resize:
splitNodeId, child boundary index, and orientation available to a later interaction handler.{operation: 'resizeSplit', splitNodeId, sizes}.sizesenter the dock-zone document.resizeSplitwithout directsizesmutation.Contract Ledger Matrix
HarnessDockZoneModel.mdSplit Projection +DockLayoutAdapter.projectSplitNode()splitNodeId, boundary index, and orientation.DockZoneModel.resizeSplit()+applyOperation(){operation: 'resizeSplit', splitNodeId, sizes}and the focused proof commits that descriptor through the existing semantic operation.sizes, and source-model non-mutation.HarnessDockZoneModel.mdSerializable vs Runtime StateDecision Record impact
Aligned-with ADR 0020. This is an H1/Harness dashboard composition leaf under the Agent Harness concept; it preserves the ADR guardrails around JSON-first state, benchmark-before-perf-claims, restart-affordance awareness, and dashboard-layer ownership.
Acceptance Criteria
splitNodeId, boundary index, and orientation.resizeSplitsemantic descriptor and committed through the existingDockZoneModel.resizeSplit()/applyOperation()path.DOMRect,windowId, preview payloads, functions, or live component references.Out of Scope
resizeSplitmodel operation itself; it already exists.src/layout/Dockprimitive or new generic splitter framework.Avoided Traps
node.sizesis rejected; the operation seam owns persistence.DockZoneModel.resizeSplit().#13209review; the durable follow-up is #13213.Related
Parent: #13158. Follow-up: #13213. Related: #13012, #13030, #13032. Contract:
learn/agentos/HarnessDockZoneModel.md. Substrate:src/dashboard/DockZoneModel.mjs,src/dashboard/DockLayoutAdapter.mjs.Origin Session ID: 650bf204-359f-41ff-bc35-19c15da38f54
Handoff Retrieval Hints:
query_summaries("DockZoneModel resizeSplit splitter affordance #13158")ask_knowledge_base("DockZoneModel splitter resize sizes semantic operation HarnessDockZoneModel")rg -n "resizeSplit|projectSplitNode|Split Projection" learn/agentos/HarnessDockZoneModel.md src/dashboard test/playwright/unit/dashboard