diff --git a/src/main/persistence.ts b/src/main/persistence.ts index c2ebf0abb43..6281e118f82 100644 --- a/src/main/persistence.ts +++ b/src/main/persistence.ts @@ -2950,11 +2950,18 @@ export function hoistSshPartitionsIntoLocalSession( } changed = true } - // The tab bar renders from the active GROUP's tabOrder, not from tabsByWorktree, so a tab - // folded in without its group exists in the session and is still invisible. That is the whole - // reported symptom, and the Store-level oracle cannot see it because persistPtyBinding only - // consults tabsByWorktree. Groups, their split layout, the unified tab records and the focused - // group all have to travel with the panes. + // NOT redundant with `adoptGrouplessTabs`, which mints ONE group and a single leaf. These + // carry what it cannot reconstruct: the multi-group split arrangement (direction and ratio), + // the user's tabOrder (it degrades to sortOrder), which group and tab were focused, and the + // Ctrl+Tab MRU. And `buildHydratedTabState` takes the unified path only when `tabGroups` is + // present at all (tabs-hydration.ts) — without this fold, a profile whose tab state came + // entirely from the partition drops into the legacy hydration path, which is a different code + // path for exactly the upgrade cohort. + // + // Known asymmetry, follow-up rather than blocker: `tabGroups` is whole-record local-wins while + // `unifiedTabs` is per-tab. A partition group arriving after local has any group for that + // worktree never travels, and its tabs are adopted into local's first group — visible, but in + // the wrong group, order and split. Same shape as the unified-ledger defect, softer landing. for (const [worktreeId, groups] of Object.entries(partition.tabGroups ?? {})) { if (local.tabGroups?.[worktreeId]) { continue diff --git a/tests/e2e/ssh-partition-upgrade-tab-restore.spec.ts b/tests/e2e/ssh-partition-upgrade-tab-restore.spec.ts index 7e5ecf9ab0f..989f115e246 100644 --- a/tests/e2e/ssh-partition-upgrade-tab-restore.spec.ts +++ b/tests/e2e/ssh-partition-upgrade-tab-restore.spec.ts @@ -148,6 +148,14 @@ test.describe('an upgrading profile keeps the tabs its old build left in the ssh ).toEqual(tabsBefore.slice().sort()) // 2. The panes came back ATTACHED, not blank — the other half of the report. + // + // KNOWN WEAKNESS: readPaneIdentitySnapshot resolves the ACTIVE tab and returns only its + // panes, so this covers one of the three tabs — two could restore blank and this still + // passes. Assertion 3 is weaker still: a bare shell count is satisfied by N killed and N + // respawned, so it cannot show the panes reattached to the SAME shells. A verified patch + // exists that clicks each tab and compares ptyIds captured before quit; it did not apply + // cleanly here (its [data-tab-id] locator does not resolve in this tree) and is left for a + // follow-up rather than landed red. await expect .poll( async () => {