The second leaf of #17241, split out for the same reason its sibling #17522 was: the parent covers the dock's whole visual language moving from apps/ into the engine, and that is more than one PR can honestly close.
#17522 took the rail tabs — both apps declare the same structure and differ only in values, so it was mechanical. The splitter was deliberately left behind, because the engine's neutral default there was an open design decision rather than a refactor. That decision is now made and recorded on #17241 (@neo-fable, 2026-08-22): option 3 — the engine ships a discoverable affordance; an app opts out by token. This ticket implements it.
Filed on @neo-gpt's RA-1 against PR #17531, which correctly found that Resolves #17241 over-closes the parent: the head delivers the splitter half only, while #17241 additionally retains the edge/band/rail chrome, the dark-theme home decision, and the cross-cutting net-LOC criterion. #17241 stays open as the two-leaf parent.
Live latest-open sweep: latest 20 open issues checked 2026-08-22T12:19:54Z; no equivalent — #17522 is the rail sibling, #17211 is the cockpit-ergonomics escalation that consumes this. A2A in-flight claim sweep: 30 most recent messages, newest 2026-08-22T01:24:41Z; no claim on the splitter scope.
The Problem
resources/scss/src/dashboard/Container.scss is the engine home for Neo.dashboard.Container. Before this work it gave .neo-dashboard-dock-splitterflex-shrink, position, touch-action, z-index, a hit-target ::before and a cursor — no background, no border, no handle, no hover, no active. Line 159 said so in place: "Demo-consumable minimal hooks only — the real visual language lands with the F-tranche."
resources/scss/src/apps/workstation/Workspace.scss:31-82 — an application stylesheet — carried the entire affordance: band background, inset ring, a 36×2px ::after grip, hover glow, active state.
So a consumer that adopts the dock inherits an invisible drag target. That is not a neutral default; it is the reported defect #17211 (finding 1) was filed about, and it is why the splitter is discoverable in the workstation and invisible in the FleetCockpit — the two are not styled by the same layer at all.
The Architectural Reality
resources/scss/src/dashboard/Container.scss:113 — the engine splitter rule, structure only
Container.scss:14-27 — the existing --dock-* token precedent, motion only; paint is the gap
resources/scss/src/apps/workstation/Workspace.scss:31-82 — the app-owned affordance, already token-driven but reading --workstation-*, so its content is the promotion candidate almost verbatim and only its token names are app-specific
The workstation's active state carried opacity: 1 !important. An !important means a competing rule is being fought rather than resolved; the promotion must fix the specificity cause instead of carrying the escape hatch into the engine.
The Fix
Three layers, and the middle one is the whole design:
layer
owner
content
structure
engine, untokened
flex-shrink, position, touch-action, z-index, the hit-target ::before
affordance floor
engine, token defaults
visible band, the 36×2 centred handle, hover raise, functional active — without the pill ring and outer glow
identity
app, token values
workstation re-adds ring + glow; FM declares --dock-splitter-handle-size: 0
Ring and glow are workstation signal-language — they were born reading --workstation-signal — so shipping them as the engine default would push one consumer's identity onto every other. FM's flat splitter becomes an explicit --dock-splitter-handle-size: 0: a design statement that greps, rather than an absence that reads as an oversight.
Neutral defaults mix from currentColor rather than a literal grey — the engine cannot know the host palette, and a guess reads wrong on half of them.
SCSS token/skin layers only; zero CSS-in-JS.
Contract Ledger Matrix
The consumed token surface. Source of authority for every row is resources/scss/src/dashboard/Container.scss (engine defaults) — each verified present in that file at 450ef52b68 except the -active handle row, which is the gap this ticket closes.
computed style in the active state; workstation sets the value only
--dock-splitter-ring / -hover / -active
identity slot
none — engine never fills it
guard asserts the none default
--dock-splitter-handle-glow-hover
identity slot
none — engine never fills it
guard asserts the none default
Decision Record impact
aligned-with ADR 0029 (learn/agentos/decisions/0029-docking-design.md — renamed 2026-08-21 from 0029-harness-docking-design.md by #17503; the harness misnomer is retired, persisted schema strings stay frozen). Presentation only — behavior and state ownership are unchanged.
Acceptance Criteria
.neo-dashboard-dock-splitter renders a discoverable affordance from the engine layer alone — asserted against examples/dashboard/dock, the consumer #17211 was filed about, with no app tokens set
The engine's affordance floor is token defaults; ring and glow remain empty identity slots the engine never fills
No application stylesheet paints a splitter, including in its own nested state and pseudo blocks.&:active::after is a state of the splitter itself, not a descendant, and must stay inside the census
The census rejects every visual property except an explicit allowlist of documented structural deviations — a fixed five-property denylist lets filter, outline or color re-enter undetected
The active handle is an engine token consumed by the engine rule; Workspace.scss sets a value and declares no background
FM's flat splitter is an explicit --dock-splitter-handle-size: 0, not an omission
No !important in the promoted engine rule; if one seems required, the specificity cause is fixed instead
Resting, hover and active computed styles are asserted in both apps and both themes, including parity for the values meant to stay unchanged and FM's flat opt-out — rendered effect, not SCSS source shape
Mutation: reintroducing nested app-side active paint reddens the census
Mutation: removing the engine token block makes the engine-only consumer fail, so the test observes the promotion rather than an app override
Out of Scope
The rail tabs — #17522's leaf, delivered by PR #17524.
The dock edge zone / row / band / rail chrome, the dark-theme home decision, and the cross-cutting net-LOC criterion. Those stay on #17241; this leaf does not close the parent.
Dock behavior, drag mechanics, and the DockSplitter component API — presentation only.
Whether FM keeps a flat splitter now that a real handle exists. That is @neo-fable-clio's call on FM's design language, and the engine answer does not depend on it.
Avoided Traps
Fixing FM by copying the workstation's SCSS into FleetCockpit.scss. That is how the current state arose; a third copy makes the next consumer's bug certain.
Promoting the workstation's tokens as-is.--workstation-signal in an engine stylesheet inverts the dependency this work exists to fix.
Asserting the promotion on SCSS source alone. A source guard proves declarations exist and some properties are absent; it cannot prove token resolution, visible output, hover/active parity, selector load order, or consumer adoption. The close-target criterion is rendered effect.
Verifying by screenshot. The visual suite runs in no workflow and 8/8 goldens fail on an unmodified tree (#17518), so it cannot discriminate this change. Computed-style equality asserts the property rather than a rendering of it.
Related
#17241 — parent; retains the edge/band/rail chrome, the dark-theme home, and net LOC. Stays open. Whether it should convert from enhancement to epic now that it has two leaves is a taxonomy call left to its closeout, not decided here
Context
The second leaf of #17241, split out for the same reason its sibling #17522 was: the parent covers the dock's whole visual language moving from
apps/into the engine, and that is more than one PR can honestly close.#17522 took the rail tabs — both apps declare the same structure and differ only in values, so it was mechanical. The splitter was deliberately left behind, because the engine's neutral default there was an open design decision rather than a refactor. That decision is now made and recorded on #17241 (@neo-fable, 2026-08-22): option 3 — the engine ships a discoverable affordance; an app opts out by token. This ticket implements it.
Filed on @neo-gpt's RA-1 against PR #17531, which correctly found that
Resolves #17241over-closes the parent: the head delivers the splitter half only, while #17241 additionally retains the edge/band/rail chrome, the dark-theme home decision, and the cross-cutting net-LOC criterion. #17241 stays open as the two-leaf parent.Live latest-open sweep: latest 20 open issues checked 2026-08-22T12:19:54Z; no equivalent — #17522 is the rail sibling, #17211 is the cockpit-ergonomics escalation that consumes this. A2A in-flight claim sweep: 30 most recent messages, newest 2026-08-22T01:24:41Z; no claim on the splitter scope.
The Problem
resources/scss/src/dashboard/Container.scssis the engine home forNeo.dashboard.Container. Before this work it gave.neo-dashboard-dock-splitterflex-shrink,position,touch-action,z-index, a hit-target::beforeand a cursor — no background, no border, no handle, no hover, no active. Line 159 said so in place: "Demo-consumable minimal hooks only — the real visual language lands with the F-tranche."resources/scss/src/apps/workstation/Workspace.scss:31-82— an application stylesheet — carried the entire affordance: band background, inset ring, a 36×2px::aftergrip, hover glow, active state.So a consumer that adopts the dock inherits an invisible drag target. That is not a neutral default; it is the reported defect #17211 (finding 1) was filed about, and it is why the splitter is discoverable in the workstation and invisible in the FleetCockpit — the two are not styled by the same layer at all.
The Architectural Reality
resources/scss/src/dashboard/Container.scss:113— the engine splitter rule, structure onlyContainer.scss:14-27— the existing--dock-*token precedent, motion only; paint is the gapresources/scss/src/apps/workstation/Workspace.scss:31-82— the app-owned affordance, already token-driven but reading--workstation-*, so its content is the promotion candidate almost verbatim and only its token names are app-specificresources/scss/src/apps/agentos/fleet/FleetCockpit.scss:134— FM's partial horizontal-splitter overrideThe workstation's active state carried
opacity: 1 !important. An!importantmeans a competing rule is being fought rather than resolved; the promotion must fix the specificity cause instead of carrying the escape hatch into the engine.The Fix
Three layers, and the middle one is the whole design:
flex-shrink,position,touch-action,z-index, the hit-target::before--dock-splitter-handle-size: 0Ring and glow are workstation signal-language — they were born reading
--workstation-signal— so shipping them as the engine default would push one consumer's identity onto every other. FM's flat splitter becomes an explicit--dock-splitter-handle-size: 0: a design statement that greps, rather than an absence that reads as an oversight.Neutral defaults mix from
currentColorrather than a literal grey — the engine cannot know the host palette, and a guess reads wrong on half of them.SCSS token/skin layers only; zero CSS-in-JS.
Contract Ledger Matrix
The consumed token surface. Source of authority for every row is
resources/scss/src/dashboard/Container.scss(engine defaults) — each verified present in that file at450ef52b68except the-activehandle row, which is the gap this ticket closes.--dock-splitter-background/-hover/-activecolor-mix(currentColor 9% / 16% / 26%, transparent)--dock-splitter-radius999px--dock-splitter-handle-size/-thicknesssize: 0is the documented opt-out36px/2px--dock-splitter-handle-color/-hovercolor-mix(currentColor 45%, transparent)/currentColor--dock-splitter-handle-color-activevar(--dock-splitter-handle-color-hover)--dock-splitter-ring/-hover/-activenone— engine never fills itnonedefault--dock-splitter-handle-glow-hovernone— engine never fills itnonedefaultDecision Record impact
aligned-with ADR 0029(learn/agentos/decisions/0029-docking-design.md— renamed 2026-08-21 from0029-harness-docking-design.mdby #17503; the harness misnomer is retired, persisted schema strings stay frozen). Presentation only — behavior and state ownership are unchanged.Acceptance Criteria
.neo-dashboard-dock-splitterrenders a discoverable affordance from the engine layer alone — asserted againstexamples/dashboard/dock, the consumer #17211 was filed about, with no app tokens set&:active::afteris a state of the splitter itself, not a descendant, and must stay inside the censusfilter,outlineorcolorre-enter undetectedWorkspace.scsssets a value and declares nobackground--dock-splitter-handle-size: 0, not an omission!importantin the promoted engine rule; if one seems required, the specificity cause is fixed insteadOut of Scope
DockSplittercomponent API — presentation only.Avoided Traps
Fixing FM by copying the workstation's SCSS into
FleetCockpit.scss. That is how the current state arose; a third copy makes the next consumer's bug certain.Promoting the workstation's tokens as-is.
--workstation-signalin an engine stylesheet inverts the dependency this work exists to fix.Asserting the promotion on SCSS source alone. A source guard proves declarations exist and some properties are absent; it cannot prove token resolution, visible output, hover/active parity, selector load order, or consumer adoption. The close-target criterion is rendered effect.
Verifying by screenshot. The visual suite runs in no workflow and 8/8 goldens fail on an unmodified tree (#17518), so it cannot discriminate this change. Computed-style equality asserts the property rather than a rendering of it.
Related
enhancementtoepicnow that it has two leaves is a taxonomy call left to its closeout, not decided hereRetrieval Hint:
dock splitter affordance floor engine token promotion Container.scss Workspace.scss active handleOrigin Session ID: cb3eb9c7-875a-4eac-a716-02878fa535c5