Context
Operator UX escalation (screenshots, 2026-08-15, same session as #17209/#17210 — this ticket owns the LAYOUT AFFORDANCE half; #17209 owns skins/shell):
- The splitter between the agent-card grid and the activity timeline is invisible. A resizable boundary exists functionally but carries no visual affordance — no handle, no hover state, no cursor hint. Operators cannot discover it.
- Proportion imbalance between the two rails. The left navigation rail (Home / Fleet / Accounts / Chat) renders large — possibly too large — while the right side-tab rail (Agent detail / Perspectives / Add agent / Catch up / Memories / Wake routes / Operator) renders as cramped rotated text, and the panels it opens use their width poorly (small content in a large drawer).
- Side-panel content density: the drawer content sits small inside its area — the space is claimed but not used.
Acceptance Criteria
Sequencing
Independent of #17209's shell/skin work at the splitter + rails level; the drawer-density AC lands cleanest AFTER #17209's shell contract exists — slice accordingly.
Out of Scope
Pane skins and the drawer shell contract (#17209) · static-roster alarm calm-down (#17210) · cross-app style alignment (sibling ticket).
Related
#17209 · #17210 · #13015 (FM umbrella) · #16744 (C3).
Live latest-open sweep: latest 8 checked 2026-08-15T20:2xZ (nearest: #17209/#17210 — same escalation, explicitly split by surface); no equivalent. A2A claim sweep: current-hour window clean.
Origin: operator escalation (screenshots on session dd4568bd) · Retrieval Hint: "cockpit layout splitter invisible rail proportions drawer density ergonomics"
Contract Ledger Matrix
Added 2026-08-23 per PR #17614 review (RA-2, @neo-gpt): the change introduces engine tokens that consumers read, which is a consumed surface and owes a ledger.
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
--dock-edge-rail-size |
resources/scss/src/dashboard/Container.scss (engine, declared on .neo-dashboard) |
The edge strip's cross-axis extent, and the single source for it. Read by two consumers that must agree: the rail's own width/height, and the reveal overlay's per-edge inset. |
Engine default 14px — unchanged, so every existing consumer renders byte-identically. A consumer that sets nothing is unaffected. |
Rationale beside the declaration, naming the six literals it replaces and why the two readers are coupled. |
Mutation control: the token is moved 20px → 48px in-page and both readers must follow, since one boot cannot distinguish "coupled" from "two literals that agree today". |
--dock-rail-tab-background-active
--dock-rail-tab-color-active |
resources/scss/src/dashboard/Container.scss (engine, .neo-dashboard) |
Paint for the tab whose reveal is currently showing. Applied via the button's own pressed class, after :hover so an open tab never reads as less engaged than a hovered one. |
color-mix(in srgb, currentColor 14%, transparent) / currentColor — a visible floor rather than transparent, because an invisible affordance is not one; currentColor-relative because the engine cannot know a host palette. |
Rationale beside the declaration, including why resting/hover stay neutral while this state does not. |
CI-running component spec, both themes: revealed paint differs from a live resting sibling, and clearing restores it exactly. Removing the engine rule turns both arms red. |
Neo.dashboard.DockRail#syncRevealedTabState() |
src/dashboard/DockRail.mjs |
Projects the reveal machine's current target onto the matching tab and clears its siblings. Runtime-only; touches no document. |
Unknown/stale item id clears every mark rather than latching the previous tab. |
Anchor & Echo JSDoc naming the runtime-only contract and the dockItemId match that excludes the bound overlay. |
Unit arms for mark / retarget / clear / stale-id; a set-only mutant fails the retarget and stale-id arms. |
Consumer value (apps/agentos cockpit) |
resources/scss/src/apps/agentos/fleet/cockpit/Container.scss |
Sets the strip to 20px and the active state to panel ground + signal ink. |
Deliberately NOT the bar's .pressed pair, which is byte-identical to this rail's hover and would make "open" and "hovered" the same picture. |
§04 proportion record, grounded in the measured label box rather than the stale 18px figure. |
Live measurement: label box 11px, padding 1+1, demand 13px; e2e asserts the value reaches the strip and is not shadowed by the projected default. |
Context
Operator UX escalation (screenshots, 2026-08-15, same session as #17209/#17210 — this ticket owns the LAYOUT AFFORDANCE half; #17209 owns skins/shell):
Acceptance Criteria
Sequencing
Independent of #17209's shell/skin work at the splitter + rails level; the drawer-density AC lands cleanest AFTER #17209's shell contract exists — slice accordingly.
Out of Scope
Pane skins and the drawer shell contract (#17209) · static-roster alarm calm-down (#17210) · cross-app style alignment (sibling ticket).
Related
#17209 · #17210 · #13015 (FM umbrella) · #16744 (C3).
Live latest-open sweep: latest 8 checked 2026-08-15T20:2xZ (nearest: #17209/#17210 — same escalation, explicitly split by surface); no equivalent. A2A claim sweep: current-hour window clean.
Origin: operator escalation (screenshots on session dd4568bd) · Retrieval Hint: "cockpit layout splitter invisible rail proportions drawer density ergonomics"
Contract Ledger Matrix
Added 2026-08-23 per PR #17614 review (RA-2, @neo-gpt): the change introduces engine tokens that consumers read, which is a consumed surface and owes a ledger.
--dock-edge-rail-sizeresources/scss/src/dashboard/Container.scss(engine, declared on.neo-dashboard)width/height, and the reveal overlay's per-edgeinset.14px— unchanged, so every existing consumer renders byte-identically. A consumer that sets nothing is unaffected.20px → 48pxin-page and both readers must follow, since one boot cannot distinguish "coupled" from "two literals that agree today".--dock-rail-tab-background-active--dock-rail-tab-color-activeresources/scss/src/dashboard/Container.scss(engine,.neo-dashboard)pressedclass, after:hoverso an open tab never reads as less engaged than a hovered one.color-mix(in srgb, currentColor 14%, transparent)/currentColor— a visible floor rather thantransparent, because an invisible affordance is not one;currentColor-relative because the engine cannot know a host palette.Neo.dashboard.DockRail#syncRevealedTabState()src/dashboard/DockRail.mjsdockItemIdmatch that excludes the bound overlay.apps/agentoscockpit)resources/scss/src/apps/agentos/fleet/cockpit/Container.scss20pxand the active state to panel ground + signal ink..pressedpair, which is byte-identical to this rail's hover and would make "open" and "hovered" the same picture.