✅ Disposition — PR #18638
Not a regression: the goldens predate the reveal floor. A railed pane resolves its owning edge (commits 0.14 on the right), and RevealOverlay#syncSnapshot renders max(revealExtent, defaultRevealFraction). The Workstation's floor of 0.35 landed in dfcbda2959 (#18038, fixing #18028) 75 minutes after ddde9ba701 (#18019) wrote the 359px goldens, so the 886px overlay is that floor over a narrower committed band. The Fix section's remaining candidates below are superseded by this measurement; everything under this block is the derivation.
PR #18638 regenerates all four reveal-pin goldens, makes the arm read [revealExtent, floor] and the rendered 0.35 width ratio before its screenshots (red at 0.13999 with the floor mutated out), and keeps the resolver contract as a unit arm.
Serves: #17844 — the e2e battery's largest owned-able cluster. Two of its 36 failures, one cause.
The Problem
A revealed rail pane's overlay is 2.5× wider than its committed baseline, in both themes.
WorkstationNL.spec.mjs:2276 the reveal header matches inline tab chrome under neo-theme-neo-dark
WorkstationNL.spec.mjs:2276 the reveal header matches inline tab chrome under neo-theme-neo-light
Expected an image 359px by 33px, received 886px by 33px
dark — 17921 pixels differ (ratio 0.62)
light — 629 pixels differ (ratio 0.03)
The height is exact (33px) and the width is not. That rules out the usual screenshot-drift explanations: font rendering, DPI and antialiasing move pixels, not element width by 2.5×. The dark arm's 0.62 difference ratio is a consequence of the size mismatch, not an independent finding — the light arm shows the same structural divergence at 0.03.
The Architectural Reality
The header spanning the overlay is correct by design — resources/scss/src/dashboard/Container.scss:200: "both span the overlay's width". So the header is not the subject; the overlay's resolved width is.
The overlay takes its width from a resolved extent, not from CSS:
Rail.mjs:907 revealExtent: railItem ? me.resolveRevealExtent(railItem.dockItemId) : null
Rail.mjs:862-869 → LayoutAdapter.resolveRevealExtent(document, itemId) ?? null
"fail-soft to null — the overlay then uses its default fraction"
LayoutAdapter.mjs:706 walks nodes for a `tabs` node whose items include itemId, then its parent⚠️ The single unknown, and it is one question rather than a fork: resolveRevealExtent locates the item by finding a tabs node that contains it. This test unpins commits to the rail before revealing it (:2306), and a railed item is not in a tabs node — so childId is undefined, the walk never runs, and the resolver returns the fail-soft null. What default fraction does the overlay then apply, and is 886px that default?
Answer that and the disposition follows without a second decision. It is one probe: reveal a railed item at this tree and read the resolved revealExtent.
Sharpened after reading the committed topology — a specific, falsifiable mechanism
apps/workstation/tour/denseWorkstation.mjs:
right zone : {nodeId: 'split-right', extent: 0.14, resizable: true}
'split-right' : vertical split, children ['right-top-tabs', 'right-bottom-tabs'], sizes [0.5, 0.5]
'right-bottom-tabs': {type: 'tabs', items: ['commits']} ← commits is the SOLE itemA clean walk resolves 0.14 — nowhere near 886px. So the resolver is very likely not completing the walk at all, and the reason is now nameable:
⚠️ AC-1 IS MEASURED, AND IT FALSIFIES BOTH CLAIMS BELOW
Measured at dev@d94be7d48d, model tier, against the real initialDocument — resolveRevealExtent is pure over the document, so this needs no app:
| state |
right-bottom-tabs |
its items |
resolveRevealExtent('commits') |
| baseline |
exists |
["commits"] |
0.14 |
after setItemAutoHidden(commits, true) |
still exists |
still ["commits"] |
0.14 |
The spec's own poll (:2303) pins its end state as pinned === false, autoHidden === true, so this is the state it reaches.
1. The pruning hypothesis is wrong. The node is not pruned, childId resolves, the walk completes, and the resolver returns the committed right-zone extent. It never returns the fail-soft null. The prose below reasoned from a plausible mechanism to a conclusion instead of running the one-expression probe it was itself prescribing — the probe cost one unit spec.
2. 0.14 IS the expected width, which relocates the whole defect. denseWorkstation.mjs declares workspace: {width: 2560}, and 0.14 × 2560 = 358.4 — the baseline's 359px. So the resolver is producing exactly the number the assertion wants, and the 886px divergence is downstream of it: in how the overlay consumes the resolved extent, or in what box the header spans. Neither branch in The Fix below applies, because both were premised on a fail-soft that does not happen.
✅ That bound is discharged — the 2560 is the spec's own, not the tour's. The enclosing test.describe at WorkstationNL.spec.mjs:590 declares test.use({viewport: {height: 1440, width: 2560}}) at :594, and no column-0 close falls between :590 and this test at :2276, so it governs. bootActionAcceptance sets no viewport and the e2e config sets none, so test.use is the only source and there is no competing value.
⚠️ Read the near-miss before trusting the number, because it nearly hid an error. I first took 2560 from denseWorkstation.mjs's declared workspace: {width: 2560} — the tour's film stage, which is not what sizes this run. When I noticed neither the config nor the boot helper set a viewport, the working assumption became Playwright's 1280 default, and 1280 is exactly half of 2560: a wrong model and the right model produce the same 359px through different arithmetic (0.14 × 2560 at DSF 1, versus 0.14 × 1280 at DSF 2). The pixel count alone cannot discriminate them. What settles it is the test.use above and the absence of any deviceScaleFactor override in the config, the fixture, or this spec. Anyone re-deriving this should verify the viewport at its source rather than recomputing from the baseline.
3. The "second defect" was not a defect. A split child resolving its zone's whole 0.14 rather than a 0.07 share is resolveRevealExtent's documented contract, stated one line above the code: "Split sizes govern split children only; they must never be borrowed as edge-band authority." Measured — metrics resolves 0.14. Descriptive, not prescriptive: an asymmetry I found and called a defect without reading the docblock that governs it.
The baseline predates the change window. workstation-reveal-pin-neo-theme-neo-dark-enabled-chromium-darwin.png was last written on 2026-09-01 (ddde9ba701, #18019). LayoutAdapter.mjs has moved eight times since, including the whole declarative-layout chain (#18492, #18511) and the reference migration (#18536). So the baseline is not evidence that 359px is still correct — it is evidence of what the width was on 2026-09-01.
The Fix
Amended 2026-09-12 — AC-1 is discharged, and its result refutes the fork this section originally offered. The measurement it demanded has been taken, so the branch below replaces the two that stood here.
Measured (executed, not hand-walked — test/playwright/unit/apps/workstation/Workspace.spec.mjs, commit bf14b2dd90): DockLayoutAdapter.resolveRevealExtent(initialDocument, 'graph') → 0.11, and 'inspector' → 0.17. Both railed panes (autoHidden: true) sit in real type: 'tabs' nodes — left-tabs and bottom-tabs — whose parent is the root edge-zone, and its left / bottom descriptors commit exactly those fractions. Both clear the resolver's extent > 0 && extent < 1 guard.
So the original first branch is wrong at the resolver: the resolver resolves. It does not fail to locate a railed item, and railing never moves an item out of its tabs node — collectAutoHiddenItems derives the rail at projection time, so the committed document still lists the pane where it lives.
And the original second branch is not established either. The observed ~886px against the 2560px film stage is ≈ 0.346, which matches the Workstation's own defaultRevealFraction: 0.35 — the fallback, not a committed extent. Against the bottom edge's 0.17 (~435px) and the right's 0.14 (~358px), 886/358 ≈ 2.5 is the reported ratio. A rendered fallback cannot be explained by the resolver answering wrongly, because it answered.
The remaining fork, narrowed to two checkable candidates:
- The Rail never asks.
Rail#resolveRevealExtent (src/dashboard/dock/interaction/Rail.mjs:862) returns null outright when this.dockZoneDocument is falsy, before the adapter is consulted. The Rail receives it as dockZoneDocument: context.dockZoneDocument from createEdgeRail. If that is absent or stale on the rail's projection, every railed pane fail-softs regardless of what the document commits.
- The overlay does not use what it receives.
syncRevealOverlay sets revealExtent: railItem ? me.resolveRevealExtent(railItem.dockItemId) : null, so a railItem that is null at that moment — or an overlay that prefers its defaultRevealFraction over a supplied extent — produces the same pixels.
Discriminating between them is one read of the overlay's extent-vs-fraction precedence plus one assertion that a projected rail carries a live dockZoneDocument. The baseline still does not move until that lands — the original warning holds and is the reason this ticket exists rather than a baseline refresh.
AC
- AC-1 The resolved
revealExtent for a railed item at the current tree is measured and recorded, with the value. Not inferred from the pixel width.
- AC-2 ⚠️ Both themes move together. They are one cause: a fix that greens
neo-dark and leaves neo-light red has fixed the pixels, not the width.
- AC-3 If the outcome is a baseline regeneration, the commit names which change moved the width. A regenerated baseline with no named cause is indistinguishable from hiding a regression, and #17844 records that the previous censuses could not tell those apart.
- AC-4 Non-vacuity: whichever way it lands, the arm must fail on the un-fixed tree. A screenshot arm that passes only because its baseline was refreshed proves nothing.
- AC-5 #17844's title-keyed census is updated: these two leave the 36 by title, not by decrementing a count.
Out of Scope
The other 34 failures in #17844. DockPinCollapseNL:267's two reveal-pin arms — those are 1% pixel drift (86 and 76 pixels) with no size mismatch, which is a different class and probably a genuine platform artifact; do not fold them in on the strength of both mentioning "reveal pin". The reveal machine's transition, dismissal and pinning behaviour, all of which pass.
Avoided Traps
Do not regenerate the baseline first — it is the only surviving record of the pre-change width, and once overwritten the regression-versus-intended question cannot be reopened. Do not treat this as screenshot flake: the height matches exactly and the width does not, which platform rendering cannot produce. Do not group it with DockPinCollapseNL's reveal-pin arms on shared vocabulary; their failure shape is sub-1% drift, not a 2.5× size divergence. Do not fix the header — it spans the overlay by design, and the overlay's width is the subject.
Decision Record impact
none pending the measurement. If the fail-soft path turns out to be load-bearing for railed reveals, that is a contract statement worth writing down where the seam lives.
Sweeps and ownership
Live latest-open sweep plus all-state searches on reveal/extent/overlay/baseline: only #17844 (the parent census) and closed #15657 (a narrow-vessel rail layout, different subject). Own-assignment sweep run at filing time.
Measured by @neo-opus-grace during #17844's re-census at dev@22c183ccf9 — 36 failed · 222 passed · 9 skipped, NEO_AGENTOS_RUNTIME_ROOT wired, headless, --workers=1.
Retrieval Hint: reveal overlay width 886 359 resolveRevealExtent railed item fail-soft default fraction baseline 2026-09-01
Origin Session ID: c4dc8abc-31fa-4ecd-9aef-5209ccfd16c0
Serves:#17844 — the e2e battery's largest owned-able cluster. Two of its 36 failures, one cause.The Problem
A revealed rail pane's overlay is 2.5× wider than its committed baseline, in both themes.
The height is exact (33px) and the width is not. That rules out the usual screenshot-drift explanations: font rendering, DPI and antialiasing move pixels, not element width by 2.5×. The dark arm's 0.62 difference ratio is a consequence of the size mismatch, not an independent finding — the light arm shows the same structural divergence at 0.03.
The Architectural Reality
The header spanning the overlay is correct by design —
resources/scss/src/dashboard/Container.scss:200: "both span the overlay's width". So the header is not the subject; the overlay's resolved width is.The overlay takes its width from a resolved extent, not from CSS:
Rail.mjs:907 revealExtent: railItem ? me.resolveRevealExtent(railItem.dockItemId) : null Rail.mjs:862-869 → LayoutAdapter.resolveRevealExtent(document, itemId) ?? null "fail-soft to null — the overlay then uses its default fraction" LayoutAdapter.mjs:706 walks nodes for a `tabs` node whose items include itemId, then its parent⚠️ The single unknown, and it is one question rather than a fork:
resolveRevealExtentlocates the item by finding atabsnode that contains it. This test unpinscommitsto the rail before revealing it (:2306), and a railed item is not in a tabs node — sochildIdis undefined, the walk never runs, and the resolver returns the fail-softnull. What default fraction does the overlay then apply, and is 886px that default?Answer that and the disposition follows without a second decision. It is one probe: reveal a railed item at this tree and read the resolved
revealExtent.Sharpened after reading the committed topology — a specific, falsifiable mechanism
apps/workstation/tour/denseWorkstation.mjs:right zone : {nodeId: 'split-right', extent: 0.14, resizable: true} 'split-right' : vertical split, children ['right-top-tabs', 'right-bottom-tabs'], sizes [0.5, 0.5] 'right-bottom-tabs': {type: 'tabs', items: ['commits']} ← commits is the SOLE itemA clean walk resolves 0.14 — nowhere near 886px. So the resolver is very likely not completing the walk at all, and the reason is now nameable:
The baseline predates the change window.
workstation-reveal-pin-neo-theme-neo-dark-enabled-chromium-darwin.pngwas last written on 2026-09-01 (ddde9ba701, #18019).LayoutAdapter.mjshas moved eight times since, including the whole declarative-layout chain (#18492, #18511) and thereferencemigration (#18536). So the baseline is not evidence that 359px is still correct — it is evidence of what the width was on 2026-09-01.The Fix
Amended 2026-09-12 — AC-1 is discharged, and its result refutes the fork this section originally offered. The measurement it demanded has been taken, so the branch below replaces the two that stood here.
Measured (executed, not hand-walked —
test/playwright/unit/apps/workstation/Workspace.spec.mjs, commitbf14b2dd90):DockLayoutAdapter.resolveRevealExtent(initialDocument, 'graph')→ 0.11, and'inspector'→ 0.17. Both railed panes (autoHidden: true) sit in realtype: 'tabs'nodes —left-tabsandbottom-tabs— whose parent is therootedge-zone, and itsleft/bottomdescriptors commit exactly those fractions. Both clear the resolver'sextent > 0 && extent < 1guard.So the original first branch is wrong at the resolver: the resolver resolves. It does not fail to locate a railed item, and railing never moves an item out of its tabs node —
collectAutoHiddenItemsderives the rail at projection time, so the committed document still lists the pane where it lives.And the original second branch is not established either. The observed ~886px against the 2560px film stage is ≈ 0.346, which matches the Workstation's own
defaultRevealFraction: 0.35— the fallback, not a committed extent. Against the bottom edge's 0.17 (~435px) and the right's 0.14 (~358px), 886/358 ≈ 2.5 is the reported ratio. A rendered fallback cannot be explained by the resolver answering wrongly, because it answered.The remaining fork, narrowed to two checkable candidates:
Rail#resolveRevealExtent(src/dashboard/dock/interaction/Rail.mjs:862) returnsnulloutright whenthis.dockZoneDocumentis falsy, before the adapter is consulted. The Rail receives it asdockZoneDocument: context.dockZoneDocumentfromcreateEdgeRail. If that is absent or stale on the rail's projection, every railed pane fail-softs regardless of what the document commits.syncRevealOverlaysetsrevealExtent: railItem ? me.resolveRevealExtent(railItem.dockItemId) : null, so arailItemthat is null at that moment — or an overlay that prefers itsdefaultRevealFractionover a supplied extent — produces the same pixels.Discriminating between them is one read of the overlay's extent-vs-fraction precedence plus one assertion that a projected rail carries a live
dockZoneDocument. The baseline still does not move until that lands — the original warning holds and is the reason this ticket exists rather than a baseline refresh.AC
revealExtentfor a railed item at the current tree is measured and recorded, with the value. Not inferred from the pixel width.neo-darkand leavesneo-lightred has fixed the pixels, not the width.Out of Scope
The other 34 failures in #17844.
DockPinCollapseNL:267's two reveal-pin arms — those are 1% pixel drift (86 and 76 pixels) with no size mismatch, which is a different class and probably a genuine platform artifact; do not fold them in on the strength of both mentioning "reveal pin". The reveal machine's transition, dismissal and pinning behaviour, all of which pass.Avoided Traps
Do not regenerate the baseline first — it is the only surviving record of the pre-change width, and once overwritten the regression-versus-intended question cannot be reopened. Do not treat this as screenshot flake: the height matches exactly and the width does not, which platform rendering cannot produce. Do not group it with
DockPinCollapseNL's reveal-pin arms on shared vocabulary; their failure shape is sub-1% drift, not a 2.5× size divergence. Do not fix the header — it spans the overlay by design, and the overlay's width is the subject.Decision Record impact
nonepending the measurement. If the fail-soft path turns out to be load-bearing for railed reveals, that is a contract statement worth writing down where the seam lives.Sweeps and ownership
Live latest-open sweep plus all-state searches on reveal/extent/overlay/baseline: only #17844 (the parent census) and closed #15657 (a narrow-vessel rail layout, different subject). Own-assignment sweep run at filing time.
Measured by @neo-opus-grace during #17844's re-census at
dev@22c183ccf9— 36 failed · 222 passed · 9 skipped,NEO_AGENTOS_RUNTIME_ROOTwired, headless,--workers=1.Retrieval Hint:
reveal overlay width 886 359 resolveRevealExtent railed item fail-soft default fraction baseline 2026-09-01Origin Session ID: c4dc8abc-31fa-4ecd-9aef-5209ccfd16c0