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)
- 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.
- Root-cause in the vdom delta engine / VdomLifecycle bookkeeping; fix at the owning substrate.
Acceptance Criteria
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"
Context
Found while riding the #13158 gesture-proof guardrail for the auto-hide arc (#14654 + #14660, PR #14906): the new
DockAutoHideRevealNLwhitebox e2e proves the full reveal/pin journey againstexamples/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 asetItemPinnedcommit, 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 ✗)refreshDockWorkspace()does not remove it either.Reproducer
The journey's final
toHaveCount(0)on.neo-dashboard-dock-rail-tabis 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 = -1batched update; (b) plain non-silentinsert/removeAtwith 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 identicalremoveAll+addrefresh cleans splitter/tab-container subtrees correctly on every splitter resize — the discriminating variable between those subtrees and the rail's is not yet identified. TheVdomLifecycleunit-moderesolvefix shipping in PR #14906 is orthogonal (unit-mode only).The Fix (investigation ticket — no prescription)
removeAll+addcycle that leaves grandchild DOM behind; bisect the discriminating variable vs the splitter-era subtrees.Acceptance Criteria
DockAutoHideRevealNL's expected-fail companion assert flips to green and is un-pinned.Related
Parent arc #13158 · surfaced by PR #14906 (#14654/#14660) · e2e:
test/playwright/e2e/dashboard/DockAutoHideRevealNL.spec.mjsLive 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"