Context
A consumer workspace boots its dock empty at construct and swaps the real document in later (initDocument after an async licence/session step) — a boot shape the engine's own consumers never exercise (workstation and examples/dashboard/dock both seed a real document at construct time). Under useSharedWorkers: true in a fresh browser profile, that swap races the initial DOM mount and the workspace comes up wrong and stays wrong.
The Problem
Measured on a live app via the Neural Link + Playwright (fresh Chromium profiles, engine dev tip):
- Worker truth is perfect:
get_dock_topology returns the correct committed neo.dock.zone.v1 document (two panes placed); the component tree holds exactly the expected instances (mounted: true, hidden: false).
- DOM lies: the row renders
[old center @ full viewport width][empty new-center slot @ exactly the correct width][splitter][edge band] — the edge band sits at x=2304 in a 1280px viewport, one center-width off-screen. The retained pane never moved into the new projection's slot; the old shell never left.
- Delta stream stops: instrumenting every worker-port message on main, ghost boots receive the initial mount plus one update batch, then silence. A healthy boot (observed once — the race is timing-sensitive, ~6/7 ghost in fresh profiles, 0/many with dedicated workers) shows the full expected sequence, ending in exactly the batches whose absence produces the ghost:
moveNode <retained tab container> → <new center slot> and removeNode <old shell>.
- Not self-healing: a committed
resizeEdgeZone (verified applied: true, document updated) resizes only the center; splitter and band stay frozen at their displaced positions. Every subsequent projection appears poisoned — the page also logs repeating Uncaught (in promise) undefined rejections.
The Architectural Reality
- The consumer path: construct →
dockModel = emptyDocument() → projection A mounts → async gate → initDocument() → refreshDockWorkspace() → reconciler stages projection B, retains panes by identity, and swaps child 0 of the dock host.
Neo.dashboard.dock.Workspace already memorializes the failure class this belongs to: the refresh tail carries a .catch() because "one rejected re-projection used to poison every later one".
- The ghost DOM containing BOTH the old content and the new shell suggests the initial mount serialized a mid-staging vdom tree (old projection + staged shell co-resident), after which the reconciler's deltas — computed against the tree it believed was mounted — target ids/positions that never matched, and each application rejects.
- SharedWorker boot is slower to first mount than dedicated workers (three shared connections + handshakes), which is plausibly what widens the race window from ~never to ~always in fresh profiles.
Reproducer
- Engine-only: give
examples/dashboard/dock the consumer boot shape — construct with an EMPTY document (one empty tabs zone), then apply the real seed via a queued microtask/timeout after construct; set useSharedWorkers: true; load in a fresh Chromium profile (Playwright chromium.launch() per run).
- Assert: every committed pane's DOM rect lies inside the viewport, and the dock host holds exactly ONE projection subtree.
- The failing geometry is deterministic enough to assert on: retained pane at full viewport width, its target slot empty at (viewport − band − splitter) width, band fully off-screen.
The Fix (directions, for the taker to falsify)
- Order the first projection against the mount boundary:
refreshDockWorkspace awaits the host's mount (or the initial mount awaits an in-flight refresh) so the mount never serializes a staging tree.
- And/or: make the reconciler's swap resilient to a mid-staging mount — re-sync against the actually-mounted tree instead of the assumed one, so a lost/mismatched delta degrades to a full re-projection rather than a poisoned channel.
- The repeating
Uncaught (in promise) undefined also deserves a name: whatever rejects there should carry a message; silence made this materially harder to find.
Acceptance Criteria
Out of Scope
- The consumer's own boot-shape choices (empty-at-construct is legitimate; licence-gated catalogs require it).
- The workstation FLIP/pooled-grid reports (#17871, #17872) — possibly the same poisoned-projection family from a different trigger; verify against this fix but do not assume.
Related
- #17871, #17872 — possibly same family (projection divergence under load), different trigger
- #17836 — DockLayouts epic
- Defect-note broadcast 2026-08-30 (fold fingerprint: dock initial-projection swap under SharedWorkers)
Live latest-open sweep: latest 20 open checked 2026-08-30 ~18:15 UTC — no equivalent (#17871/#17872 adjacent, different trigger). A2A claim sweep (status:all, last hour): no overlapping claim.
Origin Session ID: b51135ac-ec45-4689-ac45-0e99fb073069
Retrieval Hint: "SharedWorkers ghost projection moveNode removeNode delta silence fresh profile" · query_recent_turns 2026-08-30 evening (geometry forensics + delta probe)
Context
A consumer workspace boots its dock empty at construct and swaps the real document in later (
initDocumentafter an async licence/session step) — a boot shape the engine's own consumers never exercise (workstation andexamples/dashboard/dockboth seed a real document at construct time). UnderuseSharedWorkers: truein a fresh browser profile, that swap races the initial DOM mount and the workspace comes up wrong and stays wrong.The Problem
Measured on a live app via the Neural Link + Playwright (fresh Chromium profiles, engine dev tip):
get_dock_topologyreturns the correct committedneo.dock.zone.v1document (two panes placed); the component tree holds exactly the expected instances (mounted: true,hidden: false).[old center @ full viewport width][empty new-center slot @ exactly the correct width][splitter][edge band]— the edge band sits at x=2304 in a 1280px viewport, one center-width off-screen. The retained pane never moved into the new projection's slot; the old shell never left.moveNode <retained tab container> → <new center slot>andremoveNode <old shell>.resizeEdgeZone(verifiedapplied: true, document updated) resizes only the center; splitter and band stay frozen at their displaced positions. Every subsequent projection appears poisoned — the page also logs repeatingUncaught (in promise) undefinedrejections.The Architectural Reality
dockModel = emptyDocument()→ projection A mounts → async gate →initDocument()→refreshDockWorkspace()→ reconciler stages projection B, retains panes by identity, and swaps child 0 of the dock host.Neo.dashboard.dock.Workspacealready memorializes the failure class this belongs to: the refresh tail carries a.catch()because "one rejected re-projection used to poison every later one".Reproducer
examples/dashboard/dockthe consumer boot shape — construct with an EMPTY document (one empty tabs zone), then apply the real seed via a queued microtask/timeout after construct; setuseSharedWorkers: true; load in a fresh Chromium profile (Playwrightchromium.launch()per run).The Fix (directions, for the taker to falsify)
refreshDockWorkspaceawaits the host's mount (or the initial mount awaits an in-flight refresh) so the mount never serializes a staging tree.Uncaught (in promise) undefinedalso deserves a name: whatever rejects there should carry a message; silence made this materially harder to find.Acceptance Criteria
useSharedWorkers: truein fresh profiles (N≥5 consecutive runs).undefinedrejection path rejects with a named error.Out of Scope
Related
Live latest-open sweep: latest 20 open checked 2026-08-30 ~18:15 UTC — no equivalent (#17871/#17872 adjacent, different trigger). A2A claim sweep (status:all, last hour): no overlapping claim.
Origin Session ID: b51135ac-ec45-4689-ac45-0e99fb073069
Retrieval Hint: "SharedWorkers ghost projection moveNode removeNode delta silence fresh profile" · query_recent_turns 2026-08-30 evening (geometry forensics + delta probe)