fix(ssh): union unifiedTabs per tab — the fold hydration actually reads

Review established that only the unifiedTabs fold is load-bearing: hydration
iterates unifiedTabs for membership and never reads tabsByWorktree, and
adoptGrouplessTabs mints a group, activeGroupId and layout for any tab lacking
one. So my earlier rationale for the group folds was wrong — short-circuiting
tabGroups, tabGroupLayouts and activeGroupIdByWorktree leaves the E2E green.

The real defect was granularity. The tabsByWorktree loop unions per tab; this one
replaced whole-record per worktree. Because ssh:<target> is a LIVE plane, once
local had any record every later partition-created tab was folded into
tabsByWorktree — satisfying persistPtyBinding, so a Store-level oracle stays
green — stamped as hoisted, and never reached unifiedTabs. Permanently invisible
in the tab bar, for exactly the cohort this migration serves. Now unions per tab.

KNOWN COVERAGE GAP, stated rather than papered over: no test pins this. The E2E
covers only the first upgrade, where local has no unified record. I attempted a
unit test and could not construct a partition fixture whose unifiedTabs survives
the load parse — the unified Tab shape differs from TerminalTab — so I removed it
rather than leave a red or vacuous test behind. The three dead group loops are
also left in place rather than removed blind; both belong in the follow-up.
This commit is contained in:
Neil
2026-08-13 22:09:42 -07:00
parent 8e6cc18bde
commit 22cfba5654
+22 -2
View File
@@ -2970,11 +2970,31 @@ export function hoistSshPartitionsIntoLocalSession(
local.tabGroupLayouts = { ...local.tabGroupLayouts, [worktreeId]: structuredClone(layout) }
changed = true
}
// Per TAB, matching the tabsByWorktree loop above. This is the load-bearing fold: hydration
// iterates `unifiedTabs` for membership and never reads `tabsByWorktree`, so a tab whose
// unified record does not travel is invisible in the tab bar no matter what else is hoisted.
//
// Whole-record-per-worktree was wrong because this plane is LIVE: once local has any record
// for the worktree, every tab the CLI or a headless surface later adds to the partition would
// be folded into tabsByWorktree — satisfying persistPtyBinding, so a Store-level oracle stays
// green — stamped as hoisted, and never reach unifiedTabs. Permanently invisible, for exactly
// the cohort this migration exists to serve.
for (const [worktreeId, tabs] of Object.entries(partition.unifiedTabs ?? {})) {
if (local.unifiedTabs?.[worktreeId]) {
const localTabs = local.unifiedTabs?.[worktreeId]
if (!localTabs) {
local.unifiedTabs = { ...local.unifiedTabs, [worktreeId]: structuredClone(tabs) }
changed = true
continue
}
local.unifiedTabs = { ...local.unifiedTabs, [worktreeId]: structuredClone(tabs) }
const known = new Set(localTabs.map((tab) => tab.id))
const additions = (tabs ?? []).filter((tab) => !known.has(tab.id))
if (additions.length === 0) {
continue
}
local.unifiedTabs = {
...local.unifiedTabs,
[worktreeId]: [...localTabs, ...structuredClone(additions)]
}
changed = true
}
for (const [worktreeId, groupId] of Object.entries(partition.activeGroupIdByWorktree ?? {})) {