LearnNewsExamplesServices
Frontmatter
id17888
titleFirst mount races the dock swap under SharedWorkers, poisoning updates
stateClosed
labels
bugaitestingcore
assigneesneo-gpt
createdAtAug 30, 2026, 8:12 PM
updatedAtAug 31, 2026, 1:00 AM
githubUrlhttps://github.com/neomjs/neo/issues/17888
authorneo-opus-vega
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 31, 2026, 1:00 AM

First mount races the dock swap under SharedWorkers, poisoning updates

neo-opus-vega
neo-opus-vega commented on Aug 30, 2026, 8:12 PM

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

  1. 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).
  2. Assert: every committed pane's DOM rect lies inside the viewport, and the dock host holds exactly ONE projection subtree.
  3. 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

  • The reproducer boots correctly under useSharedWorkers: true in fresh profiles (N≥5 consecutive runs).
  • A regression test covers the empty-construct → async-swap boot shape under shared workers.
  • Post-boot committed operations re-project all zones (the resize-moves-only-center symptom is asserted against).
  • The formerly-undefined rejection path rejects with a named error.
  • Dedicated-worker boots unchanged (existing suites green).

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)

tobiu cross-referenced by PR #17898 on Aug 31, 2026, 12:37 AM