LearnNewsExamplesServices
Frontmatter
id19619
titleA tear-out Workspace without the WindowPosition addon fails silently
stateClosed
labels
bugaidashboard
assigneesneo-opus-grace
createdAtOct 11, 2026, 8:43 PM
updatedAtOct 11, 2026, 9:26 PM
githubUrlhttps://github.com/neomjs/neo/issues/19619
authorneo-opus-grace
commentsCount0
parentIssue14800
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 19625 The dashboard post''s step 5 lists WindowPosition, which the dock will load itself
closedAtOct 11, 2026, 9:26 PM

A tear-out Workspace without the WindowPosition addon fails silently

neo-opus-grace
neo-opus-grace commented on Oct 11, 2026, 8:43 PM

Context

The operator's pre-release hand check (#14800, set on 2026-10-09 in 6089875583) failed on 2026-10-11 ~18:40Z. In the installed Fleet Manager (Institution 4183eccd, Engine e8af9f3c3b, Electron 43.5, macOS), he popped a pane out through its tab header's pop-out action and dragged the native popup by its title bar over the main app. No drop zones painted. In the Workstation the same journey paints them (the 13.2 film's native-return frame; WorkstationNativeTitlebarDragNL). His rule from 10-10: a failed check is a small release blocker, likely a Dock enhancement.

The Problem

A window moved by its native title bar is invisible to the page: no pointer events reach it. The engine learns where it is only through Neo.main.addon.WindowPosition, whose poll publishes windowPositionChange into Neo.manager.Window. Workspace#observeWindowGeometry arms that poll through an optional chain, Neo.main?.addon?.WindowPosition?.setConfigs({observeMovement: true, observeResize: true, windowId}) (src/dashboard/dock/Workspace.mjs:1428-1429). An app that does not list the addon in mainThreadAddons gets a silent no-op. The manager's row for the vessel then stays its connect-time snapshot, every cross-window claim hit-tests a stale frame, and no drop zone answers.

The no-op is deliberate ("the app's opt-in is the addon itself", the #18077 spec), but nothing tells an app that turned tear-out on and forgot the opt-in. The Fleet Manager is that app: apps/agentos/neo-config.json lists DockFlip, DragDrop, LocalStorage, Navigator, ResizeObserver, Stylesheet, WS/ShellPlane, with enableDockTearOutLifecycle: true. The Workstation and the crossWindow example list WindowPosition. No Dock guide names the addon, so every 13.2 adopter of tear-out can fall into the same silent failure.

The Architectural Reality

  • Main-thread addon remotes are registered before the application loads: Main.mjs awaits each addon's remotesReady() before WorkerManager.onWorkerConstructed. Absence at Workspace construction therefore means the app does not list the addon; it is never a race.
  • The precedent is #19449: a tear-out host whose getDockProjectionOptions() publishes no crossWindowSortGroup gets one warning per class, because a torn-out window could never return. This is the same failure one dependency lower.
  • observeWindowGeometry is the documented override seam for "a realm that publishes geometry another way". The check belongs in its default body, so an override skips it.

The Fix

Re-scoped 2026-10-11 ~18:50Z on the operator's direction: main-thread addons load lazily (Neo.currentWorker.getAddon(name, windowId), the dock's own Maximize plugin uses it for ResizeObserver), so the engine pulls the addon in itself instead of warning about a missing neo-config.json entry.

  1. Workspace#observeWindowGeometry(windowId) loads WindowPosition into that window through getAddon, then arms both observations. The load is per window, and that is the point: the Fleet Manager's vessel runs its own child app (apps/agentos/childapps/widget/neo-config.json: DocumentHead, DragDrop, Navigator, Stylesheet), so even a main-app config entry would not have reached it, and an armed call through another window's proxy reaches a realm without the addon.
  2. Admitted vessels always load it (an admitted vessel is multi-window by definition). The host's own window loads it when the App Worker is a SharedWorker (Neo.currentWorker.isSharedWorker, the runtime fact, at the operator's direction): only such a worker serves several windows, so a dock in any other app (a dock layout without multi-window) loads nothing and starts no poll.
  3. DockLayoutsWindows.md: the engine loads the addon on demand; no mainThreadAddons entry is needed for tear-out.
  4. Unit arms in the #18077 describe: a tear-out host without the addon loads it for its own window; an admitted vessel loads it for the vessel's window; a plain host without it loads nothing.

The Fleet Manager needs no config change: it picks the fix up with its next engine pin move.

Acceptance Criteria

  • AC-1 A tear-out Workspace whose app does not list WindowPosition arms movement and resize for its window through getAddon('WindowPosition', windowId).
  • AC-2 An admitted vessel arms its own window the same way, before the connection reaches an ownership branch.
  • AC-3 A dock whose App Worker is not a SharedWorker loads nothing and starts no poll; in a SharedWorker app every dock host arms its window, whether or not the app lists the addon.
  • AC-4 DockLayoutsWindows.md states that the engine loads the addon on demand.
  • AC-5 The new arms fail on the old optional chain (red-first).

Out of Scope

The Fleet Manager's pin move and reinstall, the Electron shell, and pointer-path drags.

Related

#14800 (cut gate), #19449 (sibling warning), #18077 (geometry arming), #19539 (cross-window claim).

tobiu referenced in commit 489b140 - "fix(dock): the Workspace loads WindowPosition into each window it arms (#19619) (#19622) on Oct 11, 2026, 9:26 PM
tobiu closed this issue on Oct 11, 2026, 9:26 PM
tobiu referenced in commit 309f268 - "docs(blog): step 5 leaves WindowPosition to the dock, which loads it per window (#19625) (#19628) on Oct 11, 2026, 9:51 PM