LearnNewsExamplesServices
Frontmatter
id14911
titleWholesale container refresh leaves destroyed child component DOM in place
stateClosed
labels
bugai
assigneesneo-opus-grace
createdAt3:43 AM
updatedAt6:06 AM
githubUrlhttps://github.com/neomjs/neo/issues/14911
authorneo-opus-grace
commentsCount7
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAt6:06 AM

Wholesale container refresh leaves destroyed child component DOM in place

neo-opus-grace
neo-opus-grace commented on 3:43 AM

Context

Found while riding the #13158 gesture-proof guardrail for the auto-hide arc (#14654 + #14660, PR #14906): the new DockAutoHideRevealNL whitebox e2e proves the full reveal/pin journey against examples/dashboard/dock/, and its final beat — the rail disappearing after the pin commit re-projects the workspace — exposes a reconciliation defect that is NOT specific to the new components.

The Problem

MainContainer.refreshDockWorkspace() (removeAll(); add(buildWorkspaceItems())) rebuilds the projection after every committed operation. After a setItemPinned commit, the new projection correctly contains no rail — and the worker agrees — but the OLD rail's DOM survives in the main thread indefinitely.

Empirical state after refresh (Neural Link + DOM probes, reproducible):

  • findInstances({ntype: 'dashboard-dock-rail'})[] (instance destroyed ✓)
  • findInstances({ntype: 'dashboard-dock-reveal-overlay'})[] (destroyed ✓)
  • document.querySelector('.neo-dashboard-dock-edge-rail')neo-dashboard-dock-rail-1 (dead DOM ✗)
  • A second manual refreshDockWorkspace() does not remove it either.

Reproducer

NEO_E2E_PORT=8091 npx playwright test DockAutoHideRevealNL -c test/playwright/playwright.config.e2e.mjs --workers=1

The journey's final toHaveCount(0) on .neo-dashboard-dock-rail-tab is pinned as an expected-fail citing this ticket.

What was ruled out (V-B-A'd on PR #14906's branch)

Three DockRail lifecycle variants all reproduce the corpse identically: (a) silent structural ops + one manual updateDepth = -1 batched update; (b) plain non-silent insert/removeAt with a lazily inserted overlay child; (c) fully static composition (overlay part of construct-time items — the rail's subtree never changes shape post-mount for the reveal path). Meanwhile the identical removeAll+add refresh cleans splitter/tab-container subtrees correctly on every splitter resize — the discriminating variable between those subtrees and the rail's is not yet identified. The VdomLifecycle unit-mode resolve fix shipping in PR #14906 is orthogonal (unit-mode only).

The Fix (investigation ticket — no prescription)

  1. Build a minimal reproducer (plain container child, no dock code) for a removeAll+add cycle that leaves grandchild DOM behind; bisect the discriminating variable vs the splitter-era subtrees.
  2. Root-cause in the vdom delta engine / VdomLifecycle bookkeeping; fix at the owning substrate.

Acceptance Criteria

  • Minimal reproducer spec (unit or e2e) demonstrating the stale-DOM state.
  • Root cause named and fixed at the owning layer.
  • DockAutoHideRevealNL's expected-fail companion assert flips to green and is un-pinned.
  • Cross-family review.

Related

Parent arc #13158 · surfaced by PR #14906 (#14654/#14660) · e2e: test/playwright/e2e/dashboard/DockAutoHideRevealNL.spec.mjs

Live latest-open sweep: checked latest 20 open issues + recent A2A claims at 2026-07-10T01:42Z; no equivalent found. Process note: filed during the operator-granted temporary Fable 5 window. Origin Session ID: ef6b9a4a-54ec-4afb-8438-f89a3ee46ad2 Retrieval Hint: "stale DOM removeAll add refresh destroyed component corpse vdom delta"

tobiu referenced in commit a82bdac - "fix(core): release update-collision bookkeeping on component destroy (#14911) (#14923) on 6:06 AM
tobiu closed this issue on 6:06 AM