Context
Found while implementing #15657 (FM cockpit vessel-narrow layout). After removing the cockpit's min-content width floors (inline-size containment per the landed own-width discipline), the ~314px vessel window still rendered an empty cockpit: the primary split computed width 0 while its zones' heights split correctly per the committed proportions. Live-DOM probe receipts (2026-07-24, this session):
edgeRow x51 w262 (row: [split, band, rail])
split x51 w0 flex:1 1 0% ← starved to zero
band x51 w280 flex:0 0 auto position:static ← the occupant
rail x331 w14 flex:0 0 auto ← pushed past the viewport edge
The Problem
DockLayoutAdapter.projectEdgeBand (src/dashboard/DockLayoutAdapter.mjs:663) projects an edge zone's band unconditionally — even when projectTabsNode (:783-785) has railed away every one of its items as autoHidden. The FM cockpit's default document (cockpitDockDocument.mjs) does exactly that: all five right-zone items are auto-hidden, so the band is a tabs container with zero live tab items — and it still occupies inline-size: 17.5rem (280px, resources/scss/src/dashboard/Container.scss .neo-dashboard-dock-edge-band-right) of normal flow at every width:
- Desktop: a permanently empty 280px gutter between the fleet grid and the right rail — dead space the center owns by the adapter's own philosophy.
- Narrow (~314px vessel): the band (280) + rail (14) exceed the row (262); the split (
flex-basis: 0) receives zero width, both zones vanish, and the rail is pushed to x=331 — outside its own window. This is the "the right-edge rail keeps its share" observation recorded in #15657's measurements, now root-caused.
The adapter's JSDoc states the sizing intent: "bands keep a fixed cross-extent … instead of flexing against the center — an unsized band silently eats workspace geometry the center owns." An empty band eats that geometry too — 280px for nothing.
The Architectural Reality
projectEdgeZoneNode (:580-650) pushes the band whenever zones.right (etc.) exists (:603/:611/:619/:635) — existence, not liveness, is the condition.
projectTabsNode already computes the live tab flow: items minus context.railedItemIds, activeIndex: null when empty. Emptiness is knowable at projection time from the projected config.
- Reveals do NOT use the band: a rail-tab click renders through
DockRevealOverlay (position: absolute, z 20 — ADR 0029 "overlay, never push"). The band's function is hosting live (pinned / non-auto-hidden) edge panes.
- Non-empty bands are load-bearing for other consumers:
apps/workstation/tour/denseWorkstation.mjs:41 commits left: 'left-tabs', right: 'split-right' with live panes (and a band may be a split node, not tabs). The fix must not touch populated bands.
- Document mutations re-run projection, so a skipped band re-appears the moment a pin/un-autohide operation makes it non-empty — no dynamic gap.
The Fix
In projectEdgeZoneNode (or a guard inside projectEdgeBand): when the projected band config is a tabs projection whose live item set is empty (all items railed), do not push the band into the row/rows. Split-node bands and any band with ≥1 live tab item project exactly as today.
Witness: extend test/playwright/unit/dashboard/DockLayoutAdapter.spec.mjs — (a) an edge zone whose band items are all autoHidden projects rail-without-band; (b) a partially-railed zone keeps its band; (c) a split-node band is untouched.
Golden impact (reviewed design delta, named up front): removing the desktop dead gutter widens the FM center zone, so FleetCockpitVisual.spec.mjs goldens (cockpit-default-shell, likely fleet-grid-cards / activity-stream-chips) regenerate in this PR. Note: cockpit-default-shell.png is already red on clean dev (1983px drift, control-verified this session; the golden predates post-#15649 merges such as #15680's zero-state rendering) — the regen absorbs both deltas and the PR body must account for them separately.
Acceptance Criteria
Out of Scope
- The
#15657 vessel-narrow layout itself (containment, banner shrink, bar wrap, narrow restack) — this ticket only unblocks it.
- The stale
cockpit-default-shell golden's other cause (post-#15649 text drift) — absorbed in the regen but not separately repaired here.
- Any change to reveal-overlay behavior or the committed-document schema.
Decision Record impact
aligned-with ADR 0029 — the auto-hide contract already routes reveals through the absolute overlay; an empty in-flow band contradicts the band-sizing rationale recorded in the adapter itself.
Related
- Blocks
#15657 (vessel-narrow layout — AC1 unreachable while the split starves)
#15649 (measurements that recorded the symptom), ADR 0029, #15680 (independent golden-drift co-cause)
Live latest-open sweep: checked latest 20 open issues at 2026-07-24 ~18:31Z; no equivalent found. A2A in-flight claim sweep (last 60 min, all read-states): no competing claim. KB semantic sweep: no coverage.
Origin Session ID: 856622cb-f32e-4ab8-bd11-4d2bb6602f61
Retrieval Hint: "empty edge band starves dock split vessel narrow 280px"
Context
Found while implementing
#15657(FM cockpit vessel-narrow layout). After removing the cockpit's min-content width floors (inline-size containment per the landed own-width discipline), the ~314px vessel window still rendered an empty cockpit: the primary split computed width 0 while its zones' heights split correctly per the committed proportions. Live-DOM probe receipts (2026-07-24, this session):The Problem
DockLayoutAdapter.projectEdgeBand(src/dashboard/DockLayoutAdapter.mjs:663) projects an edge zone's band unconditionally — even whenprojectTabsNode(:783-785) has railed away every one of its items asautoHidden. The FM cockpit's default document (cockpitDockDocument.mjs) does exactly that: all five right-zone items are auto-hidden, so the band is a tabs container with zero live tab items — and it still occupiesinline-size: 17.5rem(280px,resources/scss/src/dashboard/Container.scss.neo-dashboard-dock-edge-band-right) of normal flow at every width:flex-basis: 0) receives zero width, both zones vanish, and the rail is pushed to x=331 — outside its own window. This is the "the right-edge rail keeps its share" observation recorded in#15657's measurements, now root-caused.The adapter's JSDoc states the sizing intent: "bands keep a fixed cross-extent … instead of flexing against the center — an unsized band silently eats workspace geometry the center owns." An empty band eats that geometry too — 280px for nothing.
The Architectural Reality
projectEdgeZoneNode(:580-650) pushes the band wheneverzones.right(etc.) exists (:603/:611/:619/:635) — existence, not liveness, is the condition.projectTabsNodealready computes the live tab flow: items minuscontext.railedItemIds,activeIndex: nullwhen empty. Emptiness is knowable at projection time from the projected config.DockRevealOverlay(position: absolute, z 20 — ADR 0029 "overlay, never push"). The band's function is hosting live (pinned / non-auto-hidden) edge panes.apps/workstation/tour/denseWorkstation.mjs:41commitsleft: 'left-tabs',right: 'split-right'with live panes (and a band may be a split node, not tabs). The fix must not touch populated bands.The Fix
In
projectEdgeZoneNode(or a guard insideprojectEdgeBand): when the projected band config is a tabs projection whose live item set is empty (all items railed), do not push the band into the row/rows. Split-node bands and any band with ≥1 live tab item project exactly as today.Witness: extend
test/playwright/unit/dashboard/DockLayoutAdapter.spec.mjs— (a) an edge zone whose band items are allautoHiddenprojects rail-without-band; (b) a partially-railed zone keeps its band; (c) a split-node band is untouched.Golden impact (reviewed design delta, named up front): removing the desktop dead gutter widens the FM center zone, so
FleetCockpitVisual.spec.mjsgoldens (cockpit-default-shell, likelyfleet-grid-cards/activity-stream-chips) regenerate in this PR. Note:cockpit-default-shell.pngis already red on clean dev (1983px drift, control-verified this session; the golden predates post-#15649merges such as#15680's zero-state rendering) — the regen absorbs both deltas and the PR body must account for them separately.Acceptance Criteria
#15657's 314px probe shows the primary split receiving the row's free width (unblocks its AC1)Out of Scope
#15657vessel-narrow layout itself (containment, banner shrink, bar wrap, narrow restack) — this ticket only unblocks it.cockpit-default-shellgolden's other cause (post-#15649text drift) — absorbed in the regen but not separately repaired here.Decision Record impact
aligned-with ADR 0029 — the auto-hide contract already routes reveals through the absolute overlay; an empty in-flow band contradicts the band-sizing rationale recorded in the adapter itself.
Related
#15657(vessel-narrow layout — AC1 unreachable while the split starves)#15649(measurements that recorded the symptom), ADR 0029,#15680(independent golden-drift co-cause)Live latest-open sweep: checked latest 20 open issues at 2026-07-24 ~18:31Z; no equivalent found. A2A in-flight claim sweep (last 60 min, all read-states): no competing claim. KB semantic sweep: no coverage.
Origin Session ID: 856622cb-f32e-4ab8-bd11-4d2bb6602f61
Retrieval Hint: "empty edge band starves dock split vessel narrow 280px"