Context
The headed portability work in #15243 audits the landed generic dashboard tear-out lineage before QT-docking defaults consume it. Its acquisition contract is explicit: Neo.Main.windowOpen() returns a Boolean, and a blocked popup returns false rather than throwing. During the row-4/row-7 source audit, the generic dashboard path was found to violate that contract.
An exact executable probe against current dev stubbed Neo.Main.windowOpen() to resolve false and invoked Neo.dashboard.Container#openWidgetInPopup. The method still returned a success descriptor and left detachedItems.size === 1 with the widget recorded at index 0. No popup existed.
The Problem
src/dashboard/Container.mjs stages the widget in detachedItems before awaiting Neo.Main.windowOpen(), then discards the Boolean result and always returns popup geometry. Container#onDragBoundaryExit treats that descriptor as successful admission and starts the OS-window pointer-follow embodiment.
A blocked popup therefore creates three coupled false truths:
detachedItems claims a vessel owns the live widget although no window exists;
- the sort zone enters window-drag mode and hides/reflows the in-window embodiment;
- the caller receives popup geometry and
windowName as if admission succeeded.
The catch path cannot catch the primary failure because src/Main.mjs#windowOpen normalizes window.open() to a strict Boolean. The helper is also called by the legacy resumeWindowDrag() path. That mid-gesture close/re-acquire design is being retired by #15396 (park/re-show, zero reacquisition); until that composition lands, the helper must at least fail honestly, preserve any pre-existing detached entry, and never dereference a missing descriptor.
The Architectural Reality
src/Main.mjs:544-566 owns popup materialization and returns true only when window.open() produced a Window.
src/dashboard/Container.mjs:271-283 owns the generic dashboard's provisional detachedItems bookkeeping and popup request.
Container#onDragBoundaryExit owns initial boundary admission.
src/draggable/dashboard/SortZone.mjs#startWindowDrag is an effect that must run only after vessel admission. It hides the proxy/placeholder, expands the source layout, and arms Neo.main.addon.DragDrop.
- The provisional
detachedItems write must remain before the remote await to preserve the fast window-connect race, but it must roll back when the Boolean result is false.
- A helper call may replace a pre-existing detached entry (the legacy resume path). Rollback therefore means restore the prior value when one existed; delete only a newly staged entry.
- #15396 owns the architectural fix for mid-gesture out-conversion: park and re-show the same vessel, never reacquire. This leaf does not compete with that owner.
This is a production bug in the existing generic dashboard admission owner, not matrix work. #15243 records portability truth; this leaf repairs the falsified initial-admission invariant and makes the legacy caller fail honestly while #15396 removes its premise.
The Fix
Make the dashboard popup admission helper transactional around the Boolean result:
openWidgetInPopup snapshots any prior detachedItems entry, stages the provisional value before the await, captures Neo.Main.windowOpen()'s Boolean, and restores the prior value (or deletes a newly staged entry) when the result is false or the call throws. It returns a popup descriptor only on true and a fail-closed value otherwise.
onDragBoundaryExit recognizes failed admission, clears the container and sort-zone window-drag state, and leaves the live drag in its in-window embodiment. It must not call startWindowDrag.
- The legacy
resumeWindowDrag caller guards the fail-closed result so it cannot dereference a missing descriptor or arm pointer-follow without a vessel. This is containment only; #15396 remains the owner of eliminating mid-gesture reacquisition.
- Add a focused dashboard
Container unit witness with false and true controls, provisional-entry rollback, and the guarded legacy caller. The false control must model Boolean false, not throw.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
Neo.dashboard.Container#openWidgetInPopup |
src/Main.mjs#windowOpen Boolean contract |
Return popup geometry only after true; restore prior bookkeeping on false/throw |
Fail-closed result, source ownership remains honest |
JSDoc names Boolean admission |
Unit false/true + pre-existing-entry controls |
Neo.dashboard.Container#onDragBoundaryExit |
dashboard Container gesture owner |
Start window drag only after admitted vessel |
Retain in-window drag with no source mutation |
Existing method JSDoc extended |
Sort-zone spy: zero startWindowDrag on false |
legacy Container#resumeWindowDrag |
#15396 successor authority |
Guard failed helper result; do not claim reacquisition succeeded |
Preserve prior detached entry; do not arm DragDrop |
JSDoc points to park/re-show successor |
False result does not throw or arm |
Decision Record impact
Aligned with ADR 0029 §2.8.3's Boolean vessel-admission and fail-closed invariants. Depends on #15396 for the successor park/re-show lifecycle; no ADR amendment in this leaf.
Acceptance Criteria
Out of Scope
QT dock-specific DockTearOut choreography; #15396 park/re-show implementation; popup-over-popup arbitration; headed Windows/Linux receipts; redesigning detachedItems; changing Neo.Main.windowOpen's Boolean API; cloud or external-client validation.
Avoided Traps
- Catch-only rollback — rejected because blocked popups return
false and do not throw.
- Write
detachedItems only after the await — rejected because a successfully opened window can connect before the App-Worker response returns; provisional-before-await plus rollback preserves both race orders.
- Repair legacy resume by making reacquisition more elaborate — rejected because #15396 already owns the correct park/re-show successor.
- Absorb the production fix into #15243 — rejected because the matrix ticket explicitly excludes implementation defaults and must remain an honest measurement authority.
Related
Parent epic #15239; discovered by #15243; successor lifecycle #15396; contract authority D#15204 and ADR 0029; generic lineage #7204 / #8160.
Structural pre-flight: the production owner remains src/dashboard/Container.mjs. The new witness follows the existing test/playwright/unit/dashboard sibling suite; no new production module or directory is introduced.
Live latest-open sweep: refreshed immediately before creation; no equivalent found.
A2A claim sweep: refreshed immediately before creation; no competing claim found.
Origin Session ID: 019f6981-3a8c-7530-a68b-50a2788698d0
Retrieval Hint: "dashboard Container windowOpen false detachedItems blocked popup fail closed"
Context
The headed portability work in #15243 audits the landed generic dashboard tear-out lineage before QT-docking defaults consume it. Its acquisition contract is explicit:
Neo.Main.windowOpen()returns a Boolean, and a blocked popup returnsfalserather than throwing. During the row-4/row-7 source audit, the generic dashboard path was found to violate that contract.An exact executable probe against current
devstubbedNeo.Main.windowOpen()to resolvefalseand invokedNeo.dashboard.Container#openWidgetInPopup. The method still returned a success descriptor and leftdetachedItems.size === 1with the widget recorded at index 0. No popup existed.The Problem
src/dashboard/Container.mjsstages the widget indetachedItemsbefore awaitingNeo.Main.windowOpen(), then discards the Boolean result and always returns popup geometry.Container#onDragBoundaryExittreats that descriptor as successful admission and starts the OS-window pointer-follow embodiment.A blocked popup therefore creates three coupled false truths:
detachedItemsclaims a vessel owns the live widget although no window exists;windowNameas if admission succeeded.The catch path cannot catch the primary failure because
src/Main.mjs#windowOpennormalizeswindow.open()to a strict Boolean. The helper is also called by the legacyresumeWindowDrag()path. That mid-gesture close/re-acquire design is being retired by #15396 (park/re-show, zero reacquisition); until that composition lands, the helper must at least fail honestly, preserve any pre-existing detached entry, and never dereference a missing descriptor.The Architectural Reality
src/Main.mjs:544-566owns popup materialization and returnstrueonly whenwindow.open()produced aWindow.src/dashboard/Container.mjs:271-283owns the generic dashboard's provisionaldetachedItemsbookkeeping and popup request.Container#onDragBoundaryExitowns initial boundary admission.src/draggable/dashboard/SortZone.mjs#startWindowDragis an effect that must run only after vessel admission. It hides the proxy/placeholder, expands the source layout, and armsNeo.main.addon.DragDrop.detachedItemswrite must remain before the remote await to preserve the fast window-connect race, but it must roll back when the Boolean result is false.This is a production bug in the existing generic dashboard admission owner, not matrix work. #15243 records portability truth; this leaf repairs the falsified initial-admission invariant and makes the legacy caller fail honestly while #15396 removes its premise.
The Fix
Make the dashboard popup admission helper transactional around the Boolean result:
openWidgetInPopupsnapshots any priordetachedItemsentry, stages the provisional value before the await, capturesNeo.Main.windowOpen()'s Boolean, and restores the prior value (or deletes a newly staged entry) when the result is false or the call throws. It returns a popup descriptor only ontrueand a fail-closed value otherwise.onDragBoundaryExitrecognizes failed admission, clears the container and sort-zone window-drag state, and leaves the live drag in its in-window embodiment. It must not callstartWindowDrag.resumeWindowDragcaller guards the fail-closed result so it cannot dereference a missing descriptor or arm pointer-follow without a vessel. This is containment only; #15396 remains the owner of eliminating mid-gesture reacquisition.Containerunit witness with false and true controls, provisional-entry rollback, and the guarded legacy caller. The false control must model Booleanfalse, not throw.Contract Ledger
Neo.dashboard.Container#openWidgetInPopupsrc/Main.mjs#windowOpenBoolean contracttrue; restore prior bookkeeping on false/throwNeo.dashboard.Container#onDragBoundaryExitContainergesture ownerstartWindowDragon falseContainer#resumeWindowDragDragDropDecision Record impact
Aligned with ADR 0029 §2.8.3's Boolean vessel-admission and fail-closed invariants. Depends on #15396 for the successor park/re-show lifecycle; no ADR amendment in this leaf.
Acceptance Criteria
falsefromNeo.Main.windowOpencausesopenWidgetInPopupto return fail-closed and removes a newly provisionaldetachedItemsentry.falsedoes not callSortZone#startWindowDragand restores both container-owned and sort-zone-owned window-drag state.Neo.main.addon.DragDrop; #15396 remains the path that removes reacquisition entirely.truepreserves the existing fast-connect-safe ordering and returns the current popup geometry/windowNamedescriptor.test/playwright/unit/dashboard/Container.spec.mjssuite proves the false/true controls at the realContainermethod seam.Out of Scope
QT dock-specific
DockTearOutchoreography; #15396 park/re-show implementation; popup-over-popup arbitration; headed Windows/Linux receipts; redesigningdetachedItems; changingNeo.Main.windowOpen's Boolean API; cloud or external-client validation.Avoided Traps
falseand do not throw.detachedItemsonly after the await — rejected because a successfully opened window can connect before the App-Worker response returns; provisional-before-await plus rollback preserves both race orders.Related
Parent epic #15239; discovered by #15243; successor lifecycle #15396; contract authority D#15204 and ADR 0029; generic lineage #7204 / #8160.
Structural pre-flight: the production owner remains
src/dashboard/Container.mjs. The new witness follows the existingtest/playwright/unit/dashboardsibling suite; no new production module or directory is introduced.Live latest-open sweep: refreshed immediately before creation; no equivalent found. A2A claim sweep: refreshed immediately before creation; no competing claim found.
Origin Session ID: 019f6981-3a8c-7530-a68b-50a2788698d0
Retrieval Hint: "dashboard Container windowOpen false detachedItems blocked popup fail closed"