fix(terminal): reveal splits while the source pane binds (#23692)

* test(e2e): observe passive terminal restoration before activation

* fix(terminal): reveal persisted splits during source binding publication

* test(terminal): handle nullable persisted layout roots
This commit is contained in:
Neil
2026-09-28 16:34:36 -07:00
committed by GitHub
parent d0db35c18c
commit 2aed2cf64a
3 changed files with 30 additions and 4 deletions
@@ -54,12 +54,13 @@ export class OrcaRuntimeWithResolveTerminalSplitSourceAuthority extends OrcaRunt
)
const rendererTab = this.tabs.get(tabId)
const rendererLeaf = this.leaves.get(this.getLeafKey(tabId, leafId))
// A mounted pane can publish before its PTY binds; persisted identity fences that gap.
const rendererMounted = Boolean(
rendererTab &&
rendererLeaf &&
runtimeWorktreeIdsEqual(rendererTab.worktreeId, worktreeId) &&
runtimeWorktreeIdsEqual(rendererLeaf.worktreeId, worktreeId) &&
rendererLeaf.ptyId === ptyId
(rendererLeaf.ptyId === ptyId || (persisted && rendererLeaf.ptyId === null))
)
if (persisted && persistedLayout) {
return {
@@ -78,6 +78,7 @@ function createHarness(
deferSpawn?: boolean
includePairedSnapshot?: boolean
rendererMounted?: boolean
rendererPtyId?: string | null
graphOnlySource?: boolean
sourceIncarnationId?: string
stopAndWaitResult?: boolean
@@ -169,7 +170,7 @@ function createHarness(
worktreeId: WORKTREE_ID,
leafId: SOURCE_LEAF_ID,
paneRuntimeId: 1,
ptyId: SOURCE_PTY_ID
ptyId: options.rendererPtyId === undefined ? SOURCE_PTY_ID : options.rendererPtyId
}
]
: [],
@@ -283,6 +284,19 @@ describe('remote runtime terminal split authority', () => {
})
})
it('reveals a persisted split while its mounted source is still publishing its PTY binding', async () => {
const harness = createHarness(true, { rendererMounted: true, rendererPtyId: null })
await harness.runtime.splitTerminal(harness.handle, { direction: 'vertical' })
expect(harness.revealTerminalSession).toHaveBeenCalledOnce()
expect(harness.getSession().terminalLayoutsByTabId[TAB_ID]?.root?.type).toBe('split')
})
it('does not reveal a persisted split into a renderer bound to a different PTY', async () => {
const harness = createHarness(true, { rendererMounted: true, rendererPtyId: 'replacement' })
await harness.runtime.splitTerminal(harness.handle, { direction: 'vertical' })
expect(harness.revealTerminalSession).not.toHaveBeenCalled()
})
it('splits a persisted tab without consulting an unmounted host renderer', async () => {
const harness = createHarness()
@@ -137,8 +137,19 @@ test('durable whole-tab close removes a split tab across restart', async (// oxl
const secondLaunch = await session.launch()
secondApp = secondLaunch.app
await waitForSessionReady(secondLaunch.page)
const restoredWorktreeId = await attachRepoAndOpenTerminal(secondLaunch.page, repoPath)
expect(restoredWorktreeId).toBe(worktreeId)
// Activation deliberately creates a fresh terminal; observe passive hydration first.
await expect
.poll(() =>
secondLaunch.page.evaluate(
(id) =>
window.__store
?.getState()
.allWorktrees()
.some((worktree) => worktree.id === id),
worktreeId
)
)
.toBe(true)
// Why: wait past initial worktree effects so this checks resurrection, not
// only the first hydrated frame before default-tab logic has run.