PR #15501 made the ownership split visible: dashboard code owns the semantic reintegration transaction, while Neo.Main.windowClose() owns the physical popup close. That split is correct.
The adjacent Neural Link contract is not yet complete. #13446 / #13455 introduced get_window_topology, open_component_window, focus_window, and position_window, with an honest structured-unsupported fallback. The current implementation now shows why that fallback is structural rather than merely platform-specific:
src/manager/Window.mjs and src/ai/Client.mjs expose the connected runtime windowId.
src/Main.mjs owns native popup handles in the opener realm, keyed by the semantic windowName.
src/worker/mixin/RemoteMethodAccess.mjs routes main-thread calls to data.windowId; sending the target runtime windowId therefore reaches the popup's own main thread, not the opener that owns the handle.
test/playwright/e2e/neural-link/WindowOps.spec.mjs therefore expects focus and position to remain unsupported even for a popup opened by the same Neural Link session.
The system has two valid identities but no trusted, generation-bound join between them. Future multi-window inspection and control tools would inherit the same blind spot.
Architectural boundary
The fix must preserve four separate owners:
manager.Window is the runtime topology / geometry observer.
Main owns physical native handles and native window effects.
Product workspaces own semantic state, docking, reintegration, and disposal decisions.
Neural Link projects generic runtime capabilities; it does not acquire product semantics.
The public topology descriptor contains only the connected windowId, appName, geometry, and live capability facts. It never exposes windowName, browser target name, owner windowId, native handle key, or a routable descriptor.
A worker-private, reconnect-bounded route record carries {targetWindowId, ownerWindowId, opaqueHandleKey}. The opener mints a one-time pending capability when it opens the exact WindowProxy; the target consumes it once during connect, and the opener binds the private handle key to that target runtime identity. URL shape, window.name, semantic name, appName, timing, or "next popup" ordering never become authority. Timeout, close, reload, same-name reuse, or a failed exact-handle match invalidates the route.
Physical close is separately owner-granted. Focus and position may be generic capabilities of a script-opened same-origin popup. Close defaults unsupported: a product-owned detached workspace may grant it only where its semantic owner explicitly declares the physical close safe (for example, because its disconnect contract performs the semantic return). Generic Neural Link must never bypass #15501's reintegration/disposal owner.
Intended solution
Mint a one-time exact-handle capability in the opener and consume it during the target's existing window-data/connect handshake.
Keep a generation-bound private route from target runtime windowId to the opener-owned opaque handle key.
Project only generic capability facts through window registration and get_window_topology.
Route physical commands to the main thread that owns the native handle; resolve the opaque key again at execution time.
Make focus_window and position_window succeed for a live script-opened popup.
Add the symmetric write-locked close_window consumer, but advertise and execute it only for an owner-granted close route. Terminal success requires topology disappearance.
Amend ADR-0029 to record the public/private descriptor split and the separation between topology observation, physical handle ownership, and product semantic state. Cross-reference ADR-0020 where the Neural Link Possession Interface is defined.
Keep tool schemas, capability metadata, OpenAPI material, and user-facing Neural Link documentation aligned.
Contract ledger
Surface
Current truth
Required truth
Edge behavior
Proof
Window registration
Connected runtime windowId + geometry
Public capability facts + worker-private generation route
Reload/reconnect without a fresh grant degrades capabilities; it never guesses
Unit lifecycle witnesses
Main-thread handle registry
Native handle keyed by semantic windowName
One-time pending capability promotes to an opaque active handle route
Stale, reused, expired, or wrong-handle grants cannot retarget
Negative + live witnesses
App Worker bridge
window_connected lacks a usable native correlation
Private {targetWindowId, ownerWindowId, opaqueHandleKey}; public projection omits it
Caller-supplied name, owner, key, URL, or token cannot forge authority
Falsifiers for forged/stale inputs
get_window_topology
Runtime topology only
Returns generic operation capability facts
No private route or workspace/docking payload
Schema + live inspection
Focus / position
Unsupported by construction
Commands reach and verify the exact live popup
Unknown/stale IDs and platform refusal are structured failures
Live whitebox E2E
Close
No Neural Link tool
Owner-granted routes may close; terminal receipt observes disconnect
Product-owned routes default unsupported; accepted close without disappearance is not success
Owner-grant + terminal E2E
ADR/docs
Ownership split is implicit across ADR-0029 and implementation
Public/private split and semantic close authority are explicit
No persisted runtime IDs
ADR/doc review
Acceptance criteria
A popup opened through Neural Link can be joined to exactly one connected runtime windowId by consuming a one-time exact-handle capability; no client-supplied routing identity is trusted.
The private route is generation-bound and contains the target runtime windowId, owner main-thread windowId, and opaque handle key.
get_window_topology exposes operation capability facts without exposing any private route, native address, or product semantics.
focus_window(windowId) and position_window(windowId, ...) reach and verify that exact live popup.
close_window(windowId) succeeds only when the product/physical owner explicitly granted close, and its terminal receipt observes the old windowId disappear from topology.
Product-owned detached workspaces that do not grant a safe semantic close contract advertise close unsupported.
Unknown, stale, cross-app, expired, reloaded, same-name-reused, and forged identities fail with structured errors and cannot target a different native window.
Reconnect/rehydration without a newly minted capability degrades capabilities explicitly; it never reconstructs authority from URL, name, timing, or persisted data.
manager.Window remains product-agnostic: no docking, workspace, vessel, or reintegration state moves into it.
ADR-0029 records the generic descriptor, private route, owner-granted close, and owner boundaries; ADR-0020 is cross-linked where Neural Link multi-window possession is defined.
Tool schemas, capability matrix, OpenAPI/docs, and implementation agree.
A whitebox E2E proves the full live sequence for an owner-granted popup: open → discover runtime ID → focus → position → close → topology disappearance. Unit witnesses cover caller forgery, unsupported close, stale route, and reconnect degradation.
Explicitly out of scope
Moving #15501's dashboard reintegration transaction into manager.Window.
Letting generic Neural Link close bypass a product-owned semantic return/disposal contract.
Persisting or publicly projecting runtime window routing identity.
Adding dock/workspace identifiers to the generic manager.
Changing drag/tear-out semantics or acquisition policy.
Introducing an Electron-specific public contract.
Evidence ladder
Required level: L3. Source inspection and the existing WindowOps.spec.mjs unsupported witness establish the mismatch. Completion requires a real script-opened popup to traverse the Neural Link command path; a mocked handle lookup is not sufficient.
Relationships
Successor to #13446 / #13455. Informed by #15501 / #15484 and the ADR-0029 portability work in #15243. Related to the multi-window infrastructure in #15239.
Context
PR #15501 made the ownership split visible: dashboard code owns the semantic reintegration transaction, while
Neo.Main.windowClose()owns the physical popup close. That split is correct.The adjacent Neural Link contract is not yet complete. #13446 / #13455 introduced
get_window_topology,open_component_window,focus_window, andposition_window, with an honest structured-unsupportedfallback. The current implementation now shows why that fallback is structural rather than merely platform-specific:src/manager/Window.mjsandsrc/ai/Client.mjsexpose the connected runtimewindowId.src/Main.mjsowns native popup handles in the opener realm, keyed by the semanticwindowName.src/worker/mixin/RemoteMethodAccess.mjsroutes main-thread calls todata.windowId; sending the target runtimewindowIdtherefore reaches the popup's own main thread, not the opener that owns the handle.test/playwright/e2e/neural-link/WindowOps.spec.mjstherefore expects focus and position to remain unsupported even for a popup opened by the same Neural Link session.The system has two valid identities but no trusted, generation-bound join between them. Future multi-window inspection and control tools would inherit the same blind spot.
Architectural boundary
The fix must preserve four separate owners:
manager.Windowis the runtime topology / geometry observer.Mainowns physical native handles and native window effects.The public topology descriptor contains only the connected
windowId,appName, geometry, and live capability facts. It never exposeswindowName, browser target name, ownerwindowId, native handle key, or a routable descriptor.A worker-private, reconnect-bounded route record carries
{targetWindowId, ownerWindowId, opaqueHandleKey}. The opener mints a one-time pending capability when it opens the exactWindowProxy; the target consumes it once during connect, and the opener binds the private handle key to that target runtime identity. URL shape,window.name, semantic name,appName, timing, or "next popup" ordering never become authority. Timeout, close, reload, same-name reuse, or a failed exact-handle match invalidates the route.Physical close is separately owner-granted. Focus and position may be generic capabilities of a script-opened same-origin popup. Close defaults unsupported: a product-owned detached workspace may grant it only where its semantic owner explicitly declares the physical close safe (for example, because its disconnect contract performs the semantic return). Generic Neural Link must never bypass #15501's reintegration/disposal owner.
Intended solution
windowIdto the opener-owned opaque handle key.get_window_topology.focus_windowandposition_windowsucceed for a live script-opened popup.close_windowconsumer, but advertise and execute it only for an owner-granted close route. Terminal success requires topology disappearance.Contract ledger
windowId+ geometrywindowNamewindow_connectedlacks a usable native correlation{targetWindowId, ownerWindowId, opaqueHandleKey}; public projection omits itget_window_topologyAcceptance criteria
windowIdby consuming a one-time exact-handle capability; no client-supplied routing identity is trusted.windowId, owner main-threadwindowId, and opaque handle key.get_window_topologyexposes operation capability facts without exposing any private route, native address, or product semantics.focus_window(windowId)andposition_window(windowId, ...)reach and verify that exact live popup.close_window(windowId)succeeds only when the product/physical owner explicitly granted close, and its terminal receipt observes the oldwindowIddisappear from topology.windowId, pending capability, and private native routing metadata remain non-persistent.manager.Windowremains product-agnostic: no docking, workspace, vessel, or reintegration state moves into it.Explicitly out of scope
manager.Window.Evidence ladder
Required level: L3. Source inspection and the existing
WindowOps.spec.mjsunsupported witness establish the mismatch. Completion requires a real script-opened popup to traverse the Neural Link command path; a mocked handle lookup is not sufficient.Relationships
Successor to #13446 / #13455. Informed by #15501 / #15484 and the ADR-0029 portability work in #15243. Related to the multi-window infrastructure in #15239.