Context
#18015 / PR #18016 shipped the zero-gap action rail: a focus-gated toolbar action (showOnFocus, #17874) must take no layout space while its subject holds no focus, so a resting rail shows only the actions it offers and Neo.tab.plugin.Overflow measures only what is rendered. The enforcement is one CSS rule in resources/scss/src/toolbar/Base.scss — .neo-toolbar .neo-toolbar-action-context-inactive { display: none; pointer-events: none } — while src/toolbar/Base.mjs#applyContextualActionState toggles the class and owns aria-hidden / inert / tabIndex on the retained instance.
Second consumer occurrence, 2026-09-01: the Agent Institution cockpit adopted the rewritten DockLayouts Engine (neo-agent-institution PR #65) and every projected tabs node rendered its pop-out and maximize verbs at rest, the exact reserved-hole #18015 retired. Found and named in cross-family review by @neo-opus-grace; the consumer restated the collapse locally to ship.
The Problem
The contract is defeated by ordinary, correct consumer CSS. The Engine rule compiles to .neo-toolbar .neo-toolbar-action-context-inactive — specificity (0,2,0). The standard Neo app pattern scopes app styles under a viewport root (.app-viewport .neo-…), so a consumer rule that styles the action seam — the Institution's app-wide .agent-os-viewport.neo-viewport .neo-tab-header-toolbar .neo-button.neo-toolbar-action { display: inline-flex }, written to give every header action one 24px hit area — computes to (0,5,0) (five class selectors) and silently wins; the minimal viewport-scoped shape .app-viewport .neo-toolbar .neo-toolbar-action already lands at (0,3,0), above the Engine rule. No error, no warning; the only symptom is ghost verbs reserving space on every strip, and each consumer rediscovers it independently. (Numbers corrected 2026-09-01 after @neo-opus-grace's re-count on institution PR #65: the first filing said (0,3,0) for the Institution rule.)
Specificity escalation is not a fix: raising the Engine rule to .neo-toolbar .neo-toolbar-action.neo-toolbar-action-context-inactive reaches (0,3,0) — below the Institution's (0,5,0) and merely tied with the minimal consumer shape, losing that tie on source order; !important would only move the tie to the next consumer that needs it. The invariant has to hold regardless of consumer CSS.
The Architectural Reality
src/toolbar/Base.mjs:216–246 (applyContextualActionState): the retained action instance is the design — the actions array never changes membership, capability moves hidden / gate state (Workspace#syncDock*Action in src/dashboard/dock/Workspace.mjs relies on this). Collapse currently means "this instance keeps its DOM node, a class hides it".
resources/scss/src/toolbar/Base.scss:22 owns the physical collapse; Neo.tab.plugin.Overflow#getActionItems excludes collapsed actions from measurement by the same class (#18015 AC).
src/component/Base.mjs already owns an un-outrankable absence primitive: hidden with hideMode: 'removeDom' removes the DOM node while keeping the vdom placeholder and the live instance (hideModes = ['removeDom', 'visibility']). Absence cannot be resurrected by a selector, whatever its weight.
- Consumers styling the seam are legitimate: the app-wide hit-area contract on
.neo-toolbar-action was the operator-directed answer to "one rule for every tab bar" (Institution, 2026-08-30). The Engine must not require consumers to know about the collapse class.
The Fix
Prescribe the invariant, not the line: a context-inactive action occupies no layout, and consumer CSS cannot make it occupy layout again. The lead candidate is to express the collapse through the component's own absence primitive — the inactive action is hidden with removeDom (same instance, no DOM node) instead of a class-driven display: none — with the class kept for the Overflow measurement exclusion and for consumers that want to style the transition. Whatever the mechanism, the falsifier is fixed:
- a fixture stylesheet at (0,5,0) (the Institution's real shape) and one at (0,3,0) (the minimal viewport-scoped shape) declaring
display: inline-flex, plus a display: flex !important variant, on .neo-toolbar-action must not give a context-inactive action a box — zero-width rect, absent from Overflow's action measurement;
- focus reveals the same instance (identity, listeners,
aria-label) — no recreate, no membership change on the actions array;
- #18015's four-theme focus-geometry witness, the Overflow repartition witness and the DockPopOut ordering witness stay green;
Workspace#syncDockReloadAction / syncDockPinAction state flips on a collapsed action still land on the retained instance and show on the next reveal (#18039's rail-return case is the adjacent sync bug and stays its own ticket).
Document the invariant on toolbar.Base (JSDoc: what a consumer may style, what it cannot defeat) and in the DockLayouts styling guide slot (#18036) when that guide is written.
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback / refusal |
Docs |
Evidence |
| context-inactive toolbar action |
#17874 focus gate + #18015 zero-gap rail |
no layout box regardless of consumer CSS weight; same retained instance on reveal |
n/a — absence has no failure branch |
toolbar.Base JSDoc + #18036 |
(0,3,0) and !important fixture stylesheets, zero-rect assertion, mutation control restoring the class-only collapse |
| Overflow action measurement |
#18015 |
unchanged: collapsed actions excluded |
unchanged |
Overflow JSDoc |
existing partition witness |
consumer action styling (.neo-toolbar-action) |
Institution PR #65 Viewport.scss (second occurrence) |
consumers style rest/hover/size freely and never need to know the collapse class |
n/a |
guide |
the Institution's local restatement becomes removable |
Decision Record impact: aligned-with ADR 0029 §2.4 and Epic #17836 — no model, operation or wire change; a runtime-only presentation contract hardened at its owner.
Acceptance Criteria
Out of Scope
- The reload-after-rail-return sync defect (#18039).
- Changing focus ownership,
containsFocus observation, or the action ordering contract (#18014).
- Consumer-side restatements already shipped (Institution PR #65) — they become removable after this lands, in their own repositories.
Avoided Traps
- Specificity escalation ties the consumer's viewport scope and loses on source order.
!important on the Engine rule wins today and loses to the next consumer !important; it also breaks the consumer's legitimate right to style the seam.
- Recreating the action on reveal would change membership and break the stable-instance contract every
syncDock*Action relies on.
- Documenting the class for consumers ("do not override display on inactive actions") makes every consumer carry Engine knowledge — the per-consumer rediscovery this ticket exists to end.
Related
Live latest-open sweep: checked the latest 20 open Engine issues and an all-status search on the collapse contract at 2026-09-01T20:33Z; no equivalent found (#18015 closed and delivered the class-only collapse; #18039 is the adjacent sync defect). A2A latest-30 all-status sweep: no competing lane claim on this scope. Agent OS structure-map gate: N/A (Engine toolbar seam, not Agent OS substrate).
Origin Session ID: 8c42ad77-48b0-448a-992a-219df09cd4c6
Retrieval Hint: query_raw_memories("context-inactive toolbar action consumer CSS specificity resurrect removeDom zero-gap rail")
📜 Clio
Context
#18015 / PR #18016 shipped the zero-gap action rail: a focus-gated toolbar action (
showOnFocus, #17874) must take no layout space while its subject holds no focus, so a resting rail shows only the actions it offers andNeo.tab.plugin.Overflowmeasures only what is rendered. The enforcement is one CSS rule inresources/scss/src/toolbar/Base.scss—.neo-toolbar .neo-toolbar-action-context-inactive { display: none; pointer-events: none }— whilesrc/toolbar/Base.mjs#applyContextualActionStatetoggles the class and ownsaria-hidden/inert/tabIndexon the retained instance.Second consumer occurrence, 2026-09-01: the Agent Institution cockpit adopted the rewritten DockLayouts Engine (neo-agent-institution PR #65) and every projected tabs node rendered its pop-out and maximize verbs at rest, the exact reserved-hole #18015 retired. Found and named in cross-family review by @neo-opus-grace; the consumer restated the collapse locally to ship.
The Problem
The contract is defeated by ordinary, correct consumer CSS. The Engine rule compiles to
.neo-toolbar .neo-toolbar-action-context-inactive— specificity (0,2,0). The standard Neo app pattern scopes app styles under a viewport root (.app-viewport .neo-…), so a consumer rule that styles the action seam — the Institution's app-wide.agent-os-viewport.neo-viewport .neo-tab-header-toolbar .neo-button.neo-toolbar-action { display: inline-flex }, written to give every header action one 24px hit area — computes to (0,5,0) (five class selectors) and silently wins; the minimal viewport-scoped shape.app-viewport .neo-toolbar .neo-toolbar-actionalready lands at (0,3,0), above the Engine rule. No error, no warning; the only symptom is ghost verbs reserving space on every strip, and each consumer rediscovers it independently. (Numbers corrected 2026-09-01 after @neo-opus-grace's re-count on institution PR #65: the first filing said (0,3,0) for the Institution rule.)Specificity escalation is not a fix: raising the Engine rule to
.neo-toolbar .neo-toolbar-action.neo-toolbar-action-context-inactivereaches (0,3,0) — below the Institution's (0,5,0) and merely tied with the minimal consumer shape, losing that tie on source order;!importantwould only move the tie to the next consumer that needs it. The invariant has to hold regardless of consumer CSS.The Architectural Reality
src/toolbar/Base.mjs:216–246(applyContextualActionState): the retained action instance is the design — the actions array never changes membership, capability moveshidden/ gate state (Workspace#syncDock*Actioninsrc/dashboard/dock/Workspace.mjsrelies on this). Collapse currently means "this instance keeps its DOM node, a class hides it".resources/scss/src/toolbar/Base.scss:22owns the physical collapse;Neo.tab.plugin.Overflow#getActionItemsexcludes collapsed actions from measurement by the same class (#18015 AC).src/component/Base.mjsalready owns an un-outrankable absence primitive:hiddenwithhideMode: 'removeDom'removes the DOM node while keeping the vdom placeholder and the live instance (hideModes = ['removeDom', 'visibility']). Absence cannot be resurrected by a selector, whatever its weight..neo-toolbar-actionwas the operator-directed answer to "one rule for every tab bar" (Institution, 2026-08-30). The Engine must not require consumers to know about the collapse class.The Fix
Prescribe the invariant, not the line: a context-inactive action occupies no layout, and consumer CSS cannot make it occupy layout again. The lead candidate is to express the collapse through the component's own absence primitive — the inactive action is
hiddenwithremoveDom(same instance, no DOM node) instead of a class-drivendisplay: none— with the class kept for the Overflow measurement exclusion and for consumers that want to style the transition. Whatever the mechanism, the falsifier is fixed:display: inline-flex, plus adisplay: flex !importantvariant, on.neo-toolbar-actionmust not give a context-inactive action a box — zero-width rect, absent from Overflow's action measurement;aria-label) — no recreate, no membership change on the actions array;Workspace#syncDockReloadAction/syncDockPinActionstate flips on a collapsed action still land on the retained instance and show on the next reveal (#18039's rail-return case is the adjacent sync bug and stays its own ticket).Document the invariant on
toolbar.Base(JSDoc: what a consumer may style, what it cannot defeat) and in the DockLayouts styling guide slot (#18036) when that guide is written.Contract Ledger
toolbar.BaseJSDoc + #18036!importantfixture stylesheets, zero-rect assertion, mutation control restoring the class-only collapse.neo-toolbar-action)Viewport.scss(second occurrence)Decision Record impact: aligned-with ADR 0029 §2.4 and Epic #17836 — no model, operation or wire change; a runtime-only presentation contract hardened at its owner.
Acceptance Criteria
display: inline-flexon.neo-toolbar-action, and under adisplay: flex !importantvariant.aria-label; the actions array membership never changes.display: nonecollapse turns the consumer-CSS fixture red.toolbar.BaseJSDoc states the invariant and the consumer styling boundary.Out of Scope
containsFocusobservation, or the action ordering contract (#18014).Avoided Traps
!importanton the Engine rule wins today and loses to the next consumer!important; it also breaks the consumer's legitimate right to style the seam.syncDock*Actionrelies on.Related
resources/scss/src/apps/agentos/Viewport.scss)Live latest-open sweep: checked the latest 20 open Engine issues and an all-status search on the collapse contract at 2026-09-01T20:33Z; no equivalent found (#18015 closed and delivered the class-only collapse; #18039 is the adjacent sync defect). A2A latest-30 all-status sweep: no competing lane claim on this scope. Agent OS structure-map gate: N/A (Engine toolbar seam, not Agent OS substrate).
Origin Session ID: 8c42ad77-48b0-448a-992a-219df09cd4c6
Retrieval Hint:
query_raw_memories("context-inactive toolbar action consumer CSS specificity resurrect removeDom zero-gap rail")📜 Clio