mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 08:03:39 +00:00
fix(runtime): narrow terminal close race settlement
This commit is contained in:
@@ -59,7 +59,8 @@ jobs:
|
||||
pnpm run test:e2e
|
||||
tests/e2e/restart-restore-terminal-input.spec.ts
|
||||
tests/e2e/terminal-restart-persistence.spec.ts
|
||||
--grep "clean restart with a live daemon session|cold-restored pane accepts typing|daemon snapshot relaunch preserves"
|
||||
tests/e2e/windows-headless-terminal-close-lifecycle.spec.ts
|
||||
--grep "clean restart with a live daemon session|cold-restored pane accepts typing|daemon snapshot relaunch preserves|physical Windows exit wins the headless-tab close race"
|
||||
--workers=1
|
||||
|
||||
- name: Upload Playwright traces
|
||||
|
||||
@@ -31441,7 +31441,6 @@ describe('OrcaRuntimeService', () => {
|
||||
runtime.syncWindowGraph(0, { tabs: [], leaves: [] })
|
||||
await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)
|
||||
const handle = runtime.preAllocateHandleForPty(ptyId)
|
||||
vi.spyOn(runtime, 'closeMobileSessionTab').mockRejectedValue(new Error('tab_not_found'))
|
||||
|
||||
await expect(runtime.closeTerminal(handle)).resolves.toEqual({
|
||||
handle,
|
||||
@@ -31449,6 +31448,52 @@ describe('OrcaRuntimeService', () => {
|
||||
ptyKilled: true
|
||||
})
|
||||
expect(kill).toHaveBeenCalledWith(ptyId)
|
||||
expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).toEqual([])
|
||||
})
|
||||
|
||||
it('does not mask a missing durable tab while its headless surface remains live', async () => {
|
||||
const ptyId = 'headless-worker-live-tab'
|
||||
const { runtimeStore } = makeRuntimeStoreWithWorkspaceSession(
|
||||
makeWorkspaceSessionWithHeadlessTerminal({
|
||||
tabsByWorktree: {
|
||||
[TEST_WORKTREE_ID]: [
|
||||
{
|
||||
id: 'host-tab',
|
||||
ptyId,
|
||||
worktreeId: TEST_WORKTREE_ID,
|
||||
title: 'Headless worker',
|
||||
customTitle: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
terminalLayoutsByTabId: {
|
||||
'host-tab': makeHeadlessTerminalLayout({ [HEADLESS_LEAF_ID]: ptyId })
|
||||
}
|
||||
})
|
||||
)
|
||||
const kill = vi.fn(() => false)
|
||||
const runtime = new OrcaRuntimeService(runtimeStore as never)
|
||||
runtime.setPtyController({
|
||||
write: () => true,
|
||||
kill,
|
||||
getForegroundProcess: async () => null,
|
||||
listProcesses: async () => []
|
||||
})
|
||||
runtime.registerPty(ptyId, TEST_WORKTREE_ID, null, {
|
||||
tabId: 'host-tab',
|
||||
leafId: HEADLESS_LEAF_ID
|
||||
})
|
||||
runtime.syncWindowGraph(0, { tabs: [], leaves: [] })
|
||||
await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)
|
||||
const handle = runtime.preAllocateHandleForPty(ptyId)
|
||||
vi.spyOn(runtime, 'closeMobileSessionTab').mockRejectedValue(new Error('tab_not_found'))
|
||||
|
||||
await expect(runtime.closeTerminal(handle)).rejects.toThrow('tab_not_found')
|
||||
expect(kill).toHaveBeenCalledWith(ptyId)
|
||||
expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).not.toEqual([])
|
||||
})
|
||||
|
||||
it('keeps the renderer close transaction for an adopted runtime-owned tab', async () => {
|
||||
|
||||
@@ -29820,16 +29820,17 @@ export class OrcaRuntimeService {
|
||||
try {
|
||||
await this.closeMobileSessionTab(`id:${pty.pty.worktreeId}`, tabId)
|
||||
} catch (error) {
|
||||
// Exit handling or another host-tab transaction may retire this surface first.
|
||||
if (
|
||||
!(error instanceof Error) ||
|
||||
(error.message !== 'workspace_session_unavailable' &&
|
||||
error.message !== 'tab_not_found')
|
||||
) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw error
|
||||
}
|
||||
if (error.message === 'workspace_session_unavailable') {
|
||||
this.notifier?.closeTerminal(tabId)
|
||||
} else if (
|
||||
error.message !== 'tab_not_found' ||
|
||||
this.findMobileTerminalSurface(pty.pty.worktreeId, tabId)
|
||||
) {
|
||||
// Only absence in both persistence and the live publication proves another owner finished teardown.
|
||||
throw error
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -118,7 +118,7 @@ test('physical Windows exit wins the headless-tab close race', async ({ testRepo
|
||||
const processBefore = await queryWindowsAncestry(childPid!)
|
||||
expect(processBefore).toMatchObject([
|
||||
{ pid: childPid, name: 'node.exe' },
|
||||
{ name: 'pwsh.exe' },
|
||||
{ name: expect.stringMatching(/^(?:pwsh|powershell)\.exe$/i) },
|
||||
{ name: 'electron.exe' }
|
||||
])
|
||||
const ptyShellPid = processBefore[1]!.pid
|
||||
|
||||
Reference in New Issue
Block a user