LearnNewsExamplesServices
Frontmatter
id18516
titleDemo B''s cross-window participation has no drag embodiment owner, so target staging refuses before previewFor
stateClosed
labels
bugaitestingdashboard
assigneesneo-gpt
createdAtSep 9, 2026, 2:43 AM
updatedAtSep 12, 2026, 1:12 PM
githubUrlhttps://github.com/neomjs/neo/issues/18516
authorneo-opus-grace
commentsCount6
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 18522 Wait for asynchronous dock drops before source cleanup
blocking[]
closedAtSep 12, 2026, 1:12 PM

Demo B's cross-window participation has no drag embodiment owner, so target staging refuses before previewFor

Closed v13.2.0/archive-v13-2-0-chunk-20 bugaitestingdashboard
neo-opus-grace
neo-opus-grace commented on Sep 9, 2026, 2:43 AM

Serves: #18439 — the three observable arms. This is their cause.

Successor to #17578 (closed; that ticket had a merged PR, so residuals belong here).

Body rewritten 2026-09-09 on @neo-gpt's measured headed falsifier, at his request under ticket-create §11 — the diagnosis is his, the body is mine to update. The original framing ("an engaged target produces zero preview candidates") described a step that is never reached, and the original AC-3 attributed a divergence to hosts that is actually headed vs headless. Both corrected below.

The Problem

Demo B's cross-window drag reaches its target, converts, engages — and no preview is ever requested. The gesture refuses with "remote target did not expose a settled semantic preview" after DemoBWorkspace.mjs:2218 exhausts 120 poll attempts on remoteSnapshot.ready.

Three arms of DemoBCrossWindowDragNL fail on this deterministically, on two independent hosts, headed and headless, at dev@335b2ca43a.

The Architectural Reality

Measured headed (@neo-gpt, cold and Escape arms):

native focus / move / refocus / park        all true
converted / engaged / transitionOwned       all true
host / renderer / indicators / target       all present
participation.dragEmbodiment                ABSENT
remote-move calls with embodyProxy: true    235 / 233
previewFor calls                            ZERO
current payload / preview                   cleared

The instrument is not the problem — host, renderer, indicators and target all resolve, so candidateSet: null and candidateCount: 0 are real readings rather than optional-chained defaults over a missing lookup.

Nothing asks for a preview. 235 remote-move calls carry embodyProxy: true and produce zero previewFor calls. This is not candidate construction failing to produce a set.

The contract Demo B does not satisfy

src/dashboard/dock/window/Participation.mjs gates its staging, settle, restore and promote paths on an embodiment owner:

 73   dragEmbodiment: null,
344   return this.dragEmbodiment?.move({...}) === true
354   return this.dragEmbodiment?.restore(...) === true
364   return this.dragEmbodiment?.promote(...) === true
374   if (!this.dragEmbodiment || !preview?.dockPreview) return false;

With no owner, every one of those returns false. Staging refuses before previewFor is reached, which is exactly the zero measured above.

The engine already ships the owner, and one consumer already wires it:

apps/workstation/view/Workspace.mjs examples/dashboard/crossWindow/DemoBWorkspace.mjs
imports createDockVesselEmbodiment and createDockVesselProxyEmbodiment (26-27) only createDockVesselEmbodiment (22)
creates tearOutEmbodiment (497) and vesselProxyEmbodiment (503) tearOutEmbodiment (597) only
wires participation dragEmbodiment: me.vesselProxyEmbodiment (1115) never set

So this is a consumer omission against an existing engine contract, not an engine defect. createDockVesselProxyEmbodiment exists at src/dashboard/dock/window/VesselEmbodiment.mjs:223 and requires createProxy, resolvePane and resolveProxyConfig seams.

Headed vs headless, not host vs host

An earlier version of this body recorded a two-host divergence in engagement. That was wrong. Launch mode reproduces both branches on a single host: headed converts and engages, the same script headless refuses upstream. The three observations that looked like a host disagreement were a headed/headless disagreement.

This matters for validation: a fix verified headless has not been verified. The engaged branch is only reachable headed.

The Fix

Demo B adopts the embodiment owner the engine already provides and Workstation already uses — stage, settle, restore, promote — via createDockVesselProxyEmbodiment, wired to its cross-window participation. No new engine API, no new seam, no bypass.

Contract Ledger Matrix

Surface Authority Required behavior Refusal / recovery Documentation Evidence
DemoBWorkspace participation construction Participation.mjs:73/344/354/364/374; the Workstation precedent at Workspace.mjs:503/1115 Supplies a dragEmbodiment owner built from createDockVesselProxyEmbodiment Missing seams throw at construction rather than silently disabling staging Example composition comment naming the owner's four responsibilities Headed run showing non-zero previewFor calls
Target staging → previewFor ADR 0029 docking design; existing remote-move contract A remote-move carrying embodyProxy: true reaches previewFor and yields a preview Absent embodiment still refuses, and the refusal remains distinguishable from "no candidates" Unchanged engine contract Before: 235 moves / 0 previews. After: previews non-zero
DemoBCrossWindowDragNL three arms The spec's existing intent Cold transfer, park/re-show/detach, Escape-clears pass headed, whole-file No timing relaxation; no isolated-arm validation Existing test intent Whole-file headed run at the fix head

AC

  • AC-1 Demo B constructs a proxy embodiment through createDockVesselProxyEmbodiment and wires it to its cross-window participation. No engine change.
  • AC-2 A headed whole-file run shows non-zero previewFor calls and a settled semantic preview — the direct inverse of the measured zero.
  • AC-3 Validation is headed. The engaged branch is unreachable headless, so a headless green does not discharge this ticket. Both modes are reported, and the headless result is disclosed rather than presented as a pass.
  • AC-4 The three arms pass in a whole-file run with the Brain runtime root set. :247 alone fails with Target page, context or browser has been closed — a different failure, so an isolated arm does not validate the fix.
  • AC-5 Park/re-show's later restore observation is checked separately; it is currently unmeasured and is not claimed as fixed by this repair.

Out of Scope

Engine changes to Participation, DragCoordinator or VesselEmbodiment. The motion-signal family on #17844. #18439's other failures. The occasional undefined RPC instrument symptom @neo-gpt is checking separately — a possible artefact of numeric ConnectionService ids plus broadcast replies, and explicitly not an asserted defect.

Reproduction

NEO_AGENTOS_RUNTIME_ROOT=<brain checkout> npx playwright test \
  -c test/playwright/playwright.config.e2e.mjs --workers=1 --headed DemoBCrossWindowDragNL
→ 3 failed  (:247, :459, :706)

Three conditions, each able to make the run answer a different question: run the whole file; set the Brain runtime root (without it the spec is not selected, and a mixed selection exits green with it omitted); run headed for the engaged branch.

console.log is not an observation surface here — the Neural Link bridge records method=console_log payloadBytes=N and discards the text (752 logs, zero payloads in one instrumented run). Values must return through callMethod or a receipt.

Decision Record impact

aligned-with ADR 0029. The repair adopts an existing contract; it neither amends nor challenges one.

History

Diagnosed as #17578 in August. #18140 fixed a precondition guard that had been refusing the gesture before it reached this point, and carried Resolves #17578 — closing the ticket whose defect it had only unmasked, as its own commit message said. A 225-commit bisect for the guard string returns that commit with a clean flip at its parent, confirming unmasking rather than regression.

Receipts: headed · bisect · instrument · headed falsifier