Context
Follow-up to merged #19509, filed separately at the operator's request so #19502 remains paired with its one merged PR. Vega found the failure while validating Institution #650; Sophie independently reproduced it.
Controlled consumer: Institution d2485dbe58e8608347fc7f3da71a573ed19a061a, Brain 2445eb360f284c3e7208e2b02371f826a1d98c5c, Node 24.19, Playwright 1.63. All non-Engine dependency versions were held to the same lock and themes rebuilt before each run.
| Engine |
Full visual suite |
d75cc68543d2bc19d9c37760d5de1ed87cc426e8 |
51/51 |
7aae7c3166b7fc35ace2550763681791bb84e7f4 |
50/51; Perspectives pressed-state assertion fails |
a43fdd381ba10742bd62d0a35817a27fcad77b17 |
50/51; Perspectives passes, a different Home-field settling assertion fails |
Vega also reports a 51/51 a43fdd38 control and two failing 7aae7c31 full runs; her failing artifacts were overwritten. Sophie's failing trace, error context and terminal worker-state attachment are retained. The tighter independent a43fdd38 run passes the rail case but is not an all-green suite: its different Home-field settling failure is retained without attributing it to this producer. Source comparison a43fdd381b..7aae7c3166 -- src changes only src/mixin/VdomLifecycle.mjs; that is a regression locator, not yet the mechanism.
Current witness (2026-10-09, Engine 1671fdc56b)
The unchanged fixed-lock full suite passes 51/51, but that is not closure. The earlier version comparison is timing-sensitive: a diagnostic build with all five runtime/SCSS files changed since 7aae7c31 restored byte-for-byte to that earlier version also passes 51/51 now. Reversing only #19525 passes 51/51 too. These controls do not establish a causal fix.
A focused browser witness reproduces the loss 3/3 on the current unmodified candidate, using two consecutive real Playwright mouse clicks: Golden Path, then Perspectives. Read-only wrappers record the existing methods; renderers and worker transport remain real. No sleeps, retried clicks or forced pressed classes are involved.
| Event (App-worker performance time, ms; first arm) |
Observed state |
964.5: setActiveItem(stream-tabs, goldenPath) projection queued |
prior strip selection |
| 965.3: refresh begins |
staged reconciliation active |
| 979.7: Rail 1 accepts Perspectives |
revealed-focused; button 51 pressed: true |
| 1144.4: refresh completes |
Rail 1 unregistered; replacement Rail 2 idle, revealedItemId: null |
| settled read |
new button 127 pressed: false; VDOM/vnode classes and DOM all unpressed |
The other two real-mouse arms have the same identity/state loss. The slower walk passes. A test-workspace-only isolation sending selection through existing retainTopology preserves Rail 1 and button 51 with matching App/VDOM/vnode/DOM state in 3/3 arms. That establishes identity retention as the useful seam; it is not the chosen production fix.
The Problem
In test/playwright/visual/FleetCockpitVisual.spec.mjs of the Institution, “every strip and rail pane reads one head” walks Activity → Tasks → Memories → Mailbox → Catch up → Golden Path → Perspectives. After the Perspectives click, the unchanged assertion waits ten seconds for its rail tab's pressed class.
The failing trace resolves successive button IDs 137 → 142 → 151 during that wait. A diagnostic that runs only after failure finds:
- the surviving Perspectives button has
pressed: false in App state;
- its VDOM and vnode both lack
pressed;
needsVdomUpdate and isVdomUpdating are false;
- its rail's reveal machine is
idle, with revealedItemId: null.
This is not a pressed class stranded on the same component. The earlier committed strip selection finishes its staged projection after a newer rail reveal was accepted, destroying the accepting rail and constructing an idle replacement. A full-suite pass alone does not discharge this timing failure.
The Architectural Reality
dashboard/dock/interaction/Rail.mjs:onTabClick feeds the reveal machine; syncRevealedTabState writes each button's pressed config.
button/Base.mjs:afterSetPressed projects that config into its class list.
dashboard/dock/Workspace.mjs and projection/Reconciler.mjs:reconcileProjection own committed projection and chrome lifetimes.
projection/Reconciler.mjs:prepareTabChromeProjection and moveRetainedTabChrome preserve tabs nodes during a staged projection; edge rails are currently rebuilt. The stable-topology path already retains rail instances and updates their railItems / dockZoneDocument.
Rail.afterSetRailItems already calls reconcileTabs and syncRevealOverlay; new reveal machinery is not needed. Source verified at 1671fdc56b.
setActiveItem currently takes the staged path. Other topology commits can replace an unchanged rail too; Ada independently witnessed an already-open reveal being reset by reconciliation.
Design authority: Rail's documented click contract opens a focused transient reveal, and its tab state shows which pane is revealed. The existing consumer assertion enforces that contract.
The Fix
Retain an unchanged edge-rail instance through staged reconciliation, as existing tabs chrome is retained. Match structural rail identity, edge and membership; reparent the live rail into the staged shell and update its document/metadata through the existing reactive seam. Its reveal machine, bound overlay and pressed buttons survive by identity. Edge or membership changes retain the established rebuild/retirement behavior.
This is the agreed Ada/Sophie shape. Routing only selection through a fast path would fix the witnessed pair but leave unchanged rails vulnerable to other staged commits. Copying/replaying reveal state into a replacement would re-run entry/focus behavior. Neither is the selected repair.
No new caller argument, user obligation or wire format is proposed. Decision Record impact: none; this extends the existing retained-chrome mechanism.
Contract Ledger
Surface lookups: current Reconciler.reconcileProjection staged planner/mover and stable rail update path; Rail.afterSetRailItems at 1671fdc56b. New private helpers, if needed, remain implementation details.
| Target Surface |
Source of Authority |
Behavior |
Fallback / Edge Case |
Docs |
Evidence |
Reconciler.reconcileProjection(options), staged path |
Rail's click/reveal contract; this defect's accepted-intent witness |
A rail with matching dock identity, edge and membership survives by instance identity, including its reveal machine, overlay binding and pressed tab. Existing options/results remain compatible. |
Changed edge/membership, absent or replaced structural identity follows the existing rebuild/retirement path. Failure cleanup must not destroy a retained live rail as discarded staging chrome. |
Reconciler JSDoc documents retained rail ownership beside tabs |
Real unit identity/state assertions; browser accepted-click witness; failure/changed-membership controls |
Rail.railItems, Rail.dockZoneDocument and afterSetRailItems |
Existing reactive rail seam |
Retained rails receive the new committed metadata/document through existing setters; no separate reveal-state copy or replay API |
A removed reveal target follows existing rail reconciliation semantics; no revived dismissed intent or duplicate pane/overlay |
Existing seam plus planner/mover ownership notes |
State and DOM agree; pane/overlay/tab identity and metadata checks |
Acceptance Criteria
Out of Scope
The independent structural-overlap repair (#19513), Dock blank frame (#19514), root nesting (#19521), and grid stale highlight (#19515) retain their own tickets and owners. Do not assume they share this producer. No installed-app replacement or live-harness restart.
Avoided Traps
A successful Playwright click is not proof the same component survives. “Full-suite dependent” is observed; state leaked by an earlier test is not established. Installing an Engine without saving must still verify the rest of the dependency graph; a refreshed Playwright version invalidates a one-variable comparison.
Related and ownership
#19502 · #19509 · #19513 · neomjs/neo-agent-institution#646 · neomjs/neo-agent-institution#650. Shared ownership accepted: Ada owns the Engine Reconciler implementation and one resolving PR; Sophie owns the Institution consumer witness and primary review. Both are co-assigned. The PR will close this ticket only.
Creation checks: fresh latest-20-open and latest-30 all-state A2A sweeps found no equivalent or competing claim. #19521 was body-read: it concerns root nesting, not this rail click. Own assignments #19502 and #19515 were body-read and are distinct. MC query on Perspectives/rail/pressed/replacement returned the earlier rail-tour precedent (3fb99c61-9559-427d-a602-b4200e81acdb), not a decision on this failure; KB returned older Dock implementation anchors, not a duplicate. The operator explicitly requested this separate post-merge ticket.
Origin Session ID: e6ce4d70-a7ff-454e-996d-e7c25efdf4cf
Retrieval Hint: "Perspectives rail pressed false button 137 142 151 replacement full visual suite 7aae7c31"
Context
Follow-up to merged #19509, filed separately at the operator's request so #19502 remains paired with its one merged PR. Vega found the failure while validating Institution #650; Sophie independently reproduced it.
Controlled consumer: Institution
d2485dbe58e8608347fc7f3da71a573ed19a061a, Brain2445eb360f284c3e7208e2b02371f826a1d98c5c, Node 24.19, Playwright 1.63. All non-Engine dependency versions were held to the same lock and themes rebuilt before each run.d75cc68543d2bc19d9c37760d5de1ed87cc426e87aae7c3166b7fc35ace2550763681791bb84e7f4a43fdd381ba10742bd62d0a35817a27fcad77b17Vega also reports a 51/51
a43fdd38control and two failing7aae7c31full runs; her failing artifacts were overwritten. Sophie's failing trace, error context and terminal worker-state attachment are retained. The tighter independenta43fdd38run passes the rail case but is not an all-green suite: its different Home-field settling failure is retained without attributing it to this producer. Source comparisona43fdd381b..7aae7c3166 -- srcchanges onlysrc/mixin/VdomLifecycle.mjs; that is a regression locator, not yet the mechanism.Current witness (2026-10-09, Engine
1671fdc56b)The unchanged fixed-lock full suite passes 51/51, but that is not closure. The earlier version comparison is timing-sensitive: a diagnostic build with all five runtime/SCSS files changed since
7aae7c31restored byte-for-byte to that earlier version also passes 51/51 now. Reversing only #19525 passes 51/51 too. These controls do not establish a causal fix.A focused browser witness reproduces the loss 3/3 on the current unmodified candidate, using two consecutive real Playwright mouse clicks: Golden Path, then Perspectives. Read-only wrappers record the existing methods; renderers and worker transport remain real. No sleeps, retried clicks or forced pressed classes are involved.
setActiveItem(stream-tabs, goldenPath)projection queuedrevealed-focused; button 51pressed: trueidle,revealedItemId: nullpressed: false; VDOM/vnode classes and DOM all unpressedThe other two real-mouse arms have the same identity/state loss. The slower walk passes. A test-workspace-only isolation sending selection through existing
retainTopologypreserves Rail 1 and button 51 with matching App/VDOM/vnode/DOM state in 3/3 arms. That establishes identity retention as the useful seam; it is not the chosen production fix.The Problem
In
test/playwright/visual/FleetCockpitVisual.spec.mjsof the Institution, “every strip and rail pane reads one head” walks Activity → Tasks → Memories → Mailbox → Catch up → Golden Path → Perspectives. After the Perspectives click, the unchanged assertion waits ten seconds for its rail tab'spressedclass.The failing trace resolves successive button IDs 137 → 142 → 151 during that wait. A diagnostic that runs only after failure finds:
pressed: falsein App state;pressed;needsVdomUpdateandisVdomUpdatingare false;idle, withrevealedItemId: null.This is not a pressed class stranded on the same component. The earlier committed strip selection finishes its staged projection after a newer rail reveal was accepted, destroying the accepting rail and constructing an idle replacement. A full-suite pass alone does not discharge this timing failure.
The Architectural Reality
dashboard/dock/interaction/Rail.mjs:onTabClickfeeds the reveal machine;syncRevealedTabStatewrites each button'spressedconfig.button/Base.mjs:afterSetPressedprojects that config into its class list.dashboard/dock/Workspace.mjsandprojection/Reconciler.mjs:reconcileProjectionown committed projection and chrome lifetimes.projection/Reconciler.mjs:prepareTabChromeProjectionandmoveRetainedTabChromepreserve tabs nodes during a staged projection; edge rails are currently rebuilt. The stable-topology path already retains rail instances and updates theirrailItems/dockZoneDocument.Rail.afterSetRailItemsalready callsreconcileTabsandsyncRevealOverlay; new reveal machinery is not needed. Source verified at1671fdc56b.setActiveItemcurrently takes the staged path. Other topology commits can replace an unchanged rail too; Ada independently witnessed an already-open reveal being reset by reconciliation.Design authority: Rail's documented click contract opens a focused transient reveal, and its tab state shows which pane is revealed. The existing consumer assertion enforces that contract.
The Fix
Retain an unchanged edge-rail instance through staged reconciliation, as existing tabs chrome is retained. Match structural rail identity, edge and membership; reparent the live rail into the staged shell and update its document/metadata through the existing reactive seam. Its reveal machine, bound overlay and pressed buttons survive by identity. Edge or membership changes retain the established rebuild/retirement behavior.
This is the agreed Ada/Sophie shape. Routing only selection through a fast path would fix the witnessed pair but leave unchanged rails vulnerable to other staged commits. Copying/replaying reveal state into a replacement would re-run entry/focus behavior. Neither is the selected repair.
No new caller argument, user obligation or wire format is proposed. Decision Record impact: none; this extends the existing retained-chrome mechanism.
Contract Ledger
Surface lookups: current
Reconciler.reconcileProjectionstaged planner/mover and stable rail update path;Rail.afterSetRailItemsat1671fdc56b. New private helpers, if needed, remain implementation details.Reconciler.reconcileProjection(options), staged pathRail.railItems,Rail.dockZoneDocumentandafterSetRailItemsAcceptance Criteria
Out of Scope
The independent structural-overlap repair (#19513), Dock blank frame (#19514), root nesting (#19521), and grid stale highlight (#19515) retain their own tickets and owners. Do not assume they share this producer. No installed-app replacement or live-harness restart.
Avoided Traps
A successful Playwright click is not proof the same component survives. “Full-suite dependent” is observed; state leaked by an earlier test is not established. Installing an Engine without saving must still verify the rest of the dependency graph; a refreshed Playwright version invalidates a one-variable comparison.
Related and ownership
#19502 · #19509 · #19513 · neomjs/neo-agent-institution#646 · neomjs/neo-agent-institution#650. Shared ownership accepted: Ada owns the Engine Reconciler implementation and one resolving PR; Sophie owns the Institution consumer witness and primary review. Both are co-assigned. The PR will close this ticket only.
Creation checks: fresh latest-20-open and latest-30 all-state A2A sweeps found no equivalent or competing claim. #19521 was body-read: it concerns root nesting, not this rail click. Own assignments #19502 and #19515 were body-read and are distinct. MC query on Perspectives/rail/pressed/replacement returned the earlier rail-tour precedent (
3fb99c61-9559-427d-a602-b4200e81acdb), not a decision on this failure; KB returned older Dock implementation anchors, not a duplicate. The operator explicitly requested this separate post-merge ticket.Origin Session ID: e6ce4d70-a7ff-454e-996d-e7c25efdf4cf
Retrieval Hint: "Perspectives rail pressed false button 137 142 151 replacement full visual suite 7aae7c31"