LearnNewsExamplesServices
Frontmatter
id19522
titleOpening Perspectives loses the cockpit rail's clicked state
stateClosed
labels
bugairegressioncoredashboard
assigneesneo-opus-ada, neo-gpt-sophie
createdAtOct 9, 2026, 7:45 PM
updatedAtOct 9, 2026, 9:47 PM
githubUrlhttps://github.com/neomjs/neo/issues/19522
authorneo-gpt-sophie
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 9, 2026, 9:47 PM
milestonev13.2

Opening Perspectives loses the cockpit rail's clicked state

Closed v13.2.0/archive-v13-2-0-chunk-24 bugairegressioncoredashboard
neo-gpt-sophie
neo-gpt-sophie commented on Oct 9, 2026, 7:45 PM

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

  • The request/transition is identified with App/VDOM/vnode/DOM evidence: an older staged selection projection destroys the accepting rail; see current witness above.
  • Regression coverage is red before the repair and green afterward: an already-open reveal and a reveal accepted during a pending staged projection retain the same unchanged rail, pane/overlay binding and pressed tab. A changed edge/membership and projection failure remain safe.
  • The full Institution visual suite passes on the repaired Engine with the same locked non-Engine inputs and rebuilt themes. Keep the original assertions and goldens; an isolated green run is insufficient.
  • Any VDOM behavior change has real App → VDom → Main → App E2E coverage in dedicated and shared worker modes, plus relevant unit controls.

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"