LearnNewsExamplesServices
Frontmatter
id18255
titleA restored layout brings the tabs back but not the tab you were on: the topology diff has no active-item category
stateClosed
labels
bugaiarchitecturedashboard
assigneesneo-opus-grace
createdAtSep 4, 2026, 4:32 AM
updatedAtSep 4, 2026, 10:19 AM
githubUrlhttps://github.com/neomjs/neo/issues/18255
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 4, 2026, 10:19 AM

A restored layout brings the tabs back but not the tab you were on: the topology diff has no active-item category

Closed v13.2.0/archive-v13-2-0-chunk-18 bugaiarchitecturedashboard
neo-opus-grace
neo-opus-grace commented on Sep 4, 2026, 4:32 AM

Context

The operator's requirement for dock layouts is "reload the multi-window app with the saved layout ⇒ getting the same multi-window setup." #18252 / PR #18253 made the restore path reachable at all — the seams it needs had no implementer. This is the next measured thing between that path and the sentence: a restored layout brings the tabs back, but not the tab you were on.

Promotion of a defect-note (ticket-create §1e), on the operator-escalation trigger: the active tab is part of "the same setup".

Measured at dev@274b63beac

Real capture, real reconciler, a real workspace (the arm added by PR #18253, test/playwright/unit/dashboard/DockTopologySeams.spec.mjs):

hop nodes['main-tabs'].activeItemId
capture strategy — kept, the record is complete
live composition moves on swarm
after TopologyReconciler.reconcile() swarm — the live value survives

Tab membership and order restore correctly in the same run. Only the active item does not.

The chain, verified end to end

  1. model/TopologyReconciler.mjs:606 delegates each slot to RestorePlanner.restoreToward(live, captured).
  2. persistence/RestorePlanner.mjs builds its plan entirely from TopologyDiff.diffDockDocuments(current, captured), emitting addTab, moveItem, resizeSplit, setItemAutoHidden.
  3. model/TopologyDiff.mjs:105 returns exactly six categories:
{moves: [], adds: [], removes: [], resizes: [], tabReorders: [], autoHideFlips: [], unchanged: [], errors: []}

There is no active-item category. The planner cannot emit what the diff never reports, so the string activeItemId appears nowhere in TopologyDiff.mjs, RestorePlanner.mjs or TopologyReconciler.mjs.

It is unaddressed rather than declined — no comment anywhere argues that the active tab is runtime-only state, and the capture format keeps it, which is the opposite argument.

The verb already exists

model/Operations.mjs:253 implements setActiveItem(document, {tabsNodeId, itemId}), registered in the operation table at :46 and classed topology at :87. Nothing new is needed in the model — this is a diff category and one plan step.

Proposed change

  • TopologyDiff gains an active-item category for tabs nodes whose activeItemId differs between current and captured, in the same shape as the existing ones ({nodeId, from, to}).
  • RestorePlanner emits setActiveItem for each, ordered after the adds/moves/reorders — the active item must be a member of the node when it is set, so a step that runs before its own item arrives would be rejected by the executor.

Acceptance Criteria

  • AC-1 — A restore returns the captured activeItemId for every tabs node. Red-first: the arm in DockTopologySeams.spec.mjs currently pins the live value surviving, and that pin flips to the captured value.
  • AC-2 — Ordering: the emitted setActiveItem runs after the item it names has arrived in the node. An arm covers the case where the active item is one the restore had to addTab or moveItem first.
  • AC-3 — A node whose active item is already correct emits no step. Restore plans stay minimal; a no-op step is a spurious commit.
  • AC-4 — An active item the capture names but the restore cannot place (it landed in surplus, or the node no longer exists) does not emit a step and does not fail the restore. Restore never destroys, and it must not start throwing here either.
  • AC-5 — Mutation-verified: removing the emit reds AC-1's arm, and removing the ordering rule reds AC-2's.

Non-goals

  • The single-window (captureScope: 'window') path, unless it shares the planner — measure, do not assume.
  • Focus. Which tab is ACTIVE is document state; which element has DOM focus is not, and restoring focus is a separate question nobody has asked for.
  • Any change to the capture format. It already carries the value.

Decision Record impact

aligned-with ADR 0029. No new concept: an existing model verb reaches an existing plan.

Related

#18252 / PR #18253 (made the restore path reachable; this gap is behind it and survives it) · #18151 (epic) · #17838 (closed — the ticket that added setActiveItem to the model)

Sweep before filing

Live latest-20 open at 2026-09-04T02:3xZ — no equivalent. Own-assignment sweep over restore OR perspective OR planner — #18252 (the seams, distinct), #17844, none this. A2A claim sweep over the last 14 messages: only @neo-opus-vega's #18241 is an active claim, no overlap. Closed-queue RestorePlanner OR capturePerspective checked while filing #18252 — #14652, #16315, #16316, none covers the active item.

Authored by Grace (Claude Opus 5, Claude Code). Session 118d8435-8d23-4745-a28a-8d22da918aac.