fix: keep paired tab updates live after runtime terminal fallback (#19022)

* fix: preserve session publication during runtime terminal fallback

* refactor(runtime): align fallback epoch comment and test preamble

Match the file's `// Why:` comment convention on the inherited
publication epoch, and drop a redundant duplicate mocks import in the
lineage regression test while keeping the required side-effect order.

No behavior change.
This commit is contained in:
Neil
2026-09-06 18:29:28 -07:00
committed by GitHub
parent 0d973c1505
commit f88cbb4fc9
2 changed files with 59 additions and 1 deletions
@@ -120,7 +120,8 @@ export class OrcaRuntimeWithCreateRuntimeOwnedMobileSessionTerminal extends Orca
}
const next: RuntimeMobileSessionTabsSnapshot = {
worktree: worktreeId,
publicationEpoch: `headless:${Date.now().toString(36)}`,
// Why: a fresh epoch retires the current publisher, so clients drop its later tab updates.
publicationEpoch: existing?.publicationEpoch ?? `headless:${Date.now().toString(36)}`,
snapshotVersion: (existing?.snapshotVersion ?? 0) + 1,
// Why: activating the new tab also focuses its group, so a "+" targeting a specific split group makes that group active too.
activeGroupId:
@@ -0,0 +1,57 @@
import { expect, it, vi } from 'vitest'
import type { RuntimeMobileSessionTabsResult } from '../../shared/runtime-types'
// Fragments stay side-effect ordered: mocks, then lifecycle, then fixtures.
const { OrcaRuntimeService } = await import('./orca-runtime-test-mocks.spec')
await import('./orca-runtime-test-lifecycle.spec')
const { store, TEST_WORKTREE_ID } = await import('./orca-runtime-test-fixtures.spec')
it.each(['renderer:active-generation', 'headless:active-generation'])(
'keeps %s live when runtime-owned creation supplements its inventory',
async (publicationEpoch) => {
const runtime = new OrcaRuntimeService(store)
runtime.setPtyController({
spawn: vi.fn().mockResolvedValue({ id: 'pty-runtime-fallback' }),
write: () => true,
kill: () => true,
getForegroundProcess: async () => null
})
runtime.syncWindowGraph(0, {
tabs: [],
leaves: [],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch,
snapshotVersion: 7,
activeGroupId: null,
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})
const events: RuntimeMobileSessionTabsResult[] = []
const unsubscribe = runtime.onMobileSessionTabsChanged(
(snapshot) => events.push(snapshot),
'paired-client'
)
try {
const created = await runtime.createMobileSessionTerminal(`id:${TEST_WORKTREE_ID}`, {
activate: false,
select: false,
navigation: 'caller',
clientNavigationId: 'paired-client'
})
expect(created.tab.status).toBe('ready')
expect(created.publicationEpoch).toBe(publicationEpoch)
expect(created.snapshotVersion).toBeGreaterThan(7)
expect(events.at(-1)).toMatchObject({
publicationEpoch: `${publicationEpoch}:client-navigation`,
tabs: [expect.objectContaining({ id: created.tab.id, status: 'ready' })]
})
} finally {
unsubscribe()
}
}
)