Context
Operator observation 2026-08-02 on the workstation demo, after correcting my first reading of it:
"not 'over' the tab title, but right next to the tab title text. the active part of the tab strip goes indeed below the ... button."
So the overflow control's position is fine — it sits beside the tab label as intended. What is wrong is that the active-tab indicator runs underneath it: the 3px strip segment that marks the active tab extends into the space the floating ... control occupies.
Evidence level, stated plainly: the mechanism is confirmed in source; the symptom is operator-observed; I did NOT reproduce it live. I could not stage an overflowing tab bar in my session — every workstation header carries 1–2 tabs that still fit at viewports down to 460px, so the control never rendered. Filing on a source-grounded candidate mechanism rather than a measurement, and AC1 is "reproduce it first".
Live latest-open sweep 2026-08-02T23:4xZ (newest #16423, my own). Neighbours checked: #6246 (CLOSED — tab strip indicator color), #16406 (tab drag paint artifact), #15649 (CLOSED — FM cockpit card breakpoint). None owns indicator geometry against the overflow control.
The Problem
Two consumers derive layout from one geometry, and only one of them knows the overflow control exists.
- The overflow plugin reserves the control's width when it decides which tabs to hide.
src/tab/plugin/Overflow.mjs:57 describes its core as "active-never-hidden packing with overflow-only control-width reservation" — the packing math explicitly subtracts the control.
- The active-tab indicator derives its geometry purely from the active tab button's rect, with no clamp and no knowledge of that reservation.
src/tab/Strip.mjs:99-128:
moveActiveIndicator(rects) {
let tabStripRect = rects.shift(),
rect = rects[1] || rects[0];
...
activeTabIndicator.style = {
left : `${rect.left - tabStripRect.left}px`,
width: `${rect.width}px`
};rect is the active tab button's rect. Nothing bounds left + width against the strip's usable width — the width that remains once the control is placed.
Why the control does not push the indicator out of the way by itself. src/tab/plugin/Overflow.mjs:17 documents it as an "OUT-OF-COLLECTION floating button rooted at document.body, deliberately NOT a member of owner.items". Being body-rooted and floating, it takes no space in the toolbar's flow — it is painted over the header. So an active tab button may legitimately extend beneath it, and an indicator sized from that button follows it there.
That composition — reserved-for-packing but not-reserved-for-painting — is a coherent explanation for exactly what the operator sees.
The Architectural Reality
src/tab/Strip.mjs:99-128 — moveActiveIndicator; the unclamped left/width assignment above. Owns the indicator.
src/tab/plugin/Overflow.mjs:17 — the control is body-rooted and out-of-collection, so it does not consume flow width.
src/tab/plugin/Overflow.mjs:57-126 — the pure packing decision that does reserve control width, and the natural-width measurement discipline it depends on.
src/dashboard/DockLayoutAdapter.mjs:7 — imports the plugin; the dock composition is where a header realistically accumulates enough tabs to overflow.
apps/workstation/view/Workspace.mjs:757 / :2030 / :2123 — the dense tour drives the real overflow control via getPlugin('tab-overflow'), which is the cheapest known route to the overflowing state.
The fix belongs in src/tab/, not in the workstation app: any tab.Container with the overflow plugin is exposed, and the workstation is only where it was noticed.
The Fix
(Prescription — pick one deliberately.)
- Clamp the indicator to the strip's usable width.
moveActiveIndicator bounds left + width by the strip width minus the reserved control width, so the segment stops where the control begins. Smallest change; keeps one owner for the geometry. Needs the reservation to be readable from the Strip, which today it is not.
- Publish the reservation as strip geometry. The plugin already computes the control-width reservation for packing; expose it so the Strip consumes the same number rather than re-deriving one. Removes the second-source-of-truth risk rather than papering over it — the cost is a contract between plugin and Strip that does not exist yet.
- Give the control flow space. Make it a real toolbar member so it occupies width and the active button can never extend under it. Cleanest geometrically, but
Overflow.mjs:17 records the out-of-collection rooting as deliberate — reversing it would need that rationale re-examined, not just overridden.
My read is 2: the defect is precisely that one reservation has two would-be consumers and only one of them can see it, so publishing it fixes the class rather than this instance. 1 is the acceptable smaller step if the contract in 2 is judged too heavy. 3 should not be taken without first reading why the control was rooted out-of-collection — that decision predates this ticket and likely has a reason.
Acceptance Criteria
Out of Scope
#16423 — workstation card sizing. Same screenshot, unrelated mechanism.
#16406 — tab drag label paint. Drag-time FLIP, not steady-state geometry.
- The overflow plugin's packing algorithm itself. It reserves the control width correctly; the gap is that nothing else can read that reservation.
- Re-litigating the body-rooted control. Option 3 touches it, and that is exactly why option 3 carries a read-first condition.
Avoided Traps
- Probing the toolbar's children for the control. I did this first and measured zero intersections at two viewports, which reads as "no defect" and is simply the wrong tree — the control is rooted at
document.body. A probe that cannot contain the answer returns a confident false negative.
- Testing for clipping when the defect is overlap. My first instrument compared
scrollWidth to clientWidth on the tab buttons. That measures truncation, not intersection, and the operator's correction is what exposed it. Recording both failed probes so the next person starts from the right instrument.
- Reading
strip.firstElementChild as the painted indicator. It reported width: 0 at every viewport I measured, including ones where the underline is plainly visible in a screenshot — so that selector is wrong too, and any measurement built on it is unreliable. Whoever reproduces this should first establish which element actually paints the segment.
- Assuming the indicator is "just cosmetic". It is the only affordance showing which tab is active; a segment running under an opaque floating control misreports the active tab's extent.
Related
#16423 — workstation cards size from the viewport, not their pane (same operator screenshot, different defect)
#16406 — tab drag paints the tab header label over the content label
#6246 — CLOSED; tab strip indicator color (different concern, same element)
src/tab/Strip.mjs, src/tab/plugin/Overflow.mjs, src/dashboard/DockLayoutAdapter.mjs
Decision Record impact: none. Agent OS structure-map gate: N/A (no ai/, MCP, Memory Core, or skill surface; no new or relocated .mjs).
Origin Session ID: 8c150fe3-e8a4-4475-8694-a6f92115dce9
Retrieval Hint: query_raw_memories("tab strip active indicator runs under floating overflow control body-rooted out-of-collection unclamped width")
Context
Operator observation 2026-08-02 on the workstation demo, after correcting my first reading of it:
So the overflow control's position is fine — it sits beside the tab label as intended. What is wrong is that the active-tab indicator runs underneath it: the 3px strip segment that marks the active tab extends into the space the floating
...control occupies.Evidence level, stated plainly: the mechanism is confirmed in source; the symptom is operator-observed; I did NOT reproduce it live. I could not stage an overflowing tab bar in my session — every workstation header carries 1–2 tabs that still fit at viewports down to 460px, so the control never rendered. Filing on a source-grounded candidate mechanism rather than a measurement, and AC1 is "reproduce it first".
Live latest-open sweep 2026-08-02T23:4xZ (newest
#16423, my own). Neighbours checked:#6246(CLOSED — tab strip indicator color),#16406(tab drag paint artifact),#15649(CLOSED — FM cockpit card breakpoint). None owns indicator geometry against the overflow control.The Problem
Two consumers derive layout from one geometry, and only one of them knows the overflow control exists.
src/tab/plugin/Overflow.mjs:57describes its core as "active-never-hidden packing with overflow-only control-width reservation" — the packing math explicitly subtracts the control.src/tab/Strip.mjs:99-128:moveActiveIndicator(rects) { let tabStripRect = rects.shift(), rect = rects[1] || rects[0]; ... activeTabIndicator.style = { left : `${rect.left - tabStripRect.left}px`, width: `${rect.width}px` };rectis the active tab button's rect. Nothing boundsleft + widthagainst the strip's usable width — the width that remains once the control is placed.Why the control does not push the indicator out of the way by itself.
src/tab/plugin/Overflow.mjs:17documents it as an "OUT-OF-COLLECTION floating button rooted atdocument.body, deliberately NOT a member ofowner.items". Being body-rooted and floating, it takes no space in the toolbar's flow — it is painted over the header. So an active tab button may legitimately extend beneath it, and an indicator sized from that button follows it there.That composition — reserved-for-packing but not-reserved-for-painting — is a coherent explanation for exactly what the operator sees.
The Architectural Reality
src/tab/Strip.mjs:99-128—moveActiveIndicator; the unclampedleft/widthassignment above. Owns the indicator.src/tab/plugin/Overflow.mjs:17— the control is body-rooted and out-of-collection, so it does not consume flow width.src/tab/plugin/Overflow.mjs:57-126— the pure packing decision that does reserve control width, and the natural-width measurement discipline it depends on.src/dashboard/DockLayoutAdapter.mjs:7— imports the plugin; the dock composition is where a header realistically accumulates enough tabs to overflow.apps/workstation/view/Workspace.mjs:757/:2030/:2123— the dense tour drives the real overflow control viagetPlugin('tab-overflow'), which is the cheapest known route to the overflowing state.The fix belongs in
src/tab/, not in the workstation app: anytab.Containerwith the overflow plugin is exposed, and the workstation is only where it was noticed.The Fix
(Prescription — pick one deliberately.)
moveActiveIndicatorboundsleft + widthby the strip width minus the reserved control width, so the segment stops where the control begins. Smallest change; keeps one owner for the geometry. Needs the reservation to be readable from the Strip, which today it is not.Overflow.mjs:17records the out-of-collection rooting as deliberate — reversing it would need that rationale re-examined, not just overridden.My read is 2: the defect is precisely that one reservation has two would-be consumers and only one of them can see it, so publishing it fixes the class rather than this instance. 1 is the acceptable smaller step if the contract in 2 is judged too heavy. 3 should not be taken without first reading why the control was rooted out-of-collection — that decision predates this ticket and likely has a reason.
Acceptance Criteria
tabBarPosition: 'top'and'bottom'; theleft/rightbranch ofmoveActiveIndicatorsetsheight/topinstead and needs its own verdict — either the same clamp on the vertical axis, or an explicit statement that the control does not overlap there.Out of Scope
#16423— workstation card sizing. Same screenshot, unrelated mechanism.#16406— tab drag label paint. Drag-time FLIP, not steady-state geometry.Avoided Traps
document.body. A probe that cannot contain the answer returns a confident false negative.scrollWidthtoclientWidthon the tab buttons. That measures truncation, not intersection, and the operator's correction is what exposed it. Recording both failed probes so the next person starts from the right instrument.strip.firstElementChildas the painted indicator. It reportedwidth: 0at every viewport I measured, including ones where the underline is plainly visible in a screenshot — so that selector is wrong too, and any measurement built on it is unreliable. Whoever reproduces this should first establish which element actually paints the segment.Related
#16423— workstation cards size from the viewport, not their pane (same operator screenshot, different defect)#16406— tab drag paints the tab header label over the content label#6246— CLOSED; tab strip indicator color (different concern, same element)src/tab/Strip.mjs,src/tab/plugin/Overflow.mjs,src/dashboard/DockLayoutAdapter.mjsDecision Record impact:
none. Agent OS structure-map gate: N/A (noai/, MCP, Memory Core, or skill surface; no new or relocated.mjs).Origin Session ID: 8c150fe3-e8a4-4475-8694-a6f92115dce9
Retrieval Hint:
query_raw_memories("tab strip active indicator runs under floating overflow control body-rooted out-of-collection unclamped width")