From 6ba25c67f4d1cd0fcba80e9b7b920f0591acc41d Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sun, 26 Jul 2026 18:26:08 -0700 Subject: [PATCH] test(codex): cover the runtime controller's resume-pinned account recording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runtime controller's spawn path repeats the same recordCodexPaneAccountForSpawn call the ipc handler makes, but nothing exercised it: reverting either pinnedByResume or launchCodexHomePath at that site left all 392 tests green. That site is the CLI/relay resume path (terminal.ts accepts resumeProviderSession, orca-runtime threads it into ptyController.spawn), so an unnoticed revert there would silently restore the bug for those clients. Also corrects the guard's rationale. The shared runtime mirror only hot-swaps to the current selection on the legacy flag-OFF lane; with the real-home routing that ships unconditionally, syncForCurrentSelection early-returns through syncSelfContainedManagedSelection and never rewrites the mirror's auth.json. Declining to attribute is still right — an unowned home cannot be named — but not for the stated reason, and a pane resumed into the mirror after the per-account rollout is genuinely stale and stays unreported. --- src/main/codex/codex-pane-launch-account.ts | 9 +- src/main/ipc/pty.test.ts | 120 ++++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/main/codex/codex-pane-launch-account.ts b/src/main/codex/codex-pane-launch-account.ts index a59703752f4..33d091cef7c 100644 --- a/src/main/codex/codex-pane-launch-account.ts +++ b/src/main/codex/codex-pane-launch-account.ts @@ -66,8 +66,11 @@ function resolveCodexHomeOwnerAccountId(args: { getCodexSelectionLaneKey(getCodexSelectionTargetForAccount(account)) === laneKey && normalizeRuntimePathForComparison(account.managedHomePath) === launchHome ) - // Why: the shared runtime mirror hot-swaps to whichever account is selected, so - // an unattributable home is no evidence the pane is on a different one. A - // wrong restart notice silently drops every keystroke in that terminal. + // Why: an unowned home cannot be named, and naming the account a pane is stuck + // on is the prompt's whole job — so decline rather than guess. A wrong notice + // silently drops every keystroke in that terminal. Note the shared runtime + // mirror only hot-swaps to the current selection on the legacy flag-OFF lane; + // a pane resumed into it after the per-account rollout is genuinely stale but + // still unnameable, so that cohort stays unreported. return owner ? owner.id : undefined } diff --git a/src/main/ipc/pty.test.ts b/src/main/ipc/pty.test.ts index 10f17505981..b6499469fa4 100644 --- a/src/main/ipc/pty.test.ts +++ b/src/main/ipc/pty.test.ts @@ -13638,6 +13638,126 @@ describe('registerPtyHandlers', () => { expect(forgetCodexPaneAccountMock).toHaveBeenCalledWith('pty-resumed') }) + // Why: the runtime controller is the CLI/relay resume path, and it repeats the + // same recording call the ipc handler makes. Without its own coverage a revert + // there is invisible. + it('records the origin account for a resumed Codex pane spawned by the runtime controller', async () => { + type RuntimeSpawnController = { + spawn(args: Record): Promise<{ id: string }> + } + setLocalPtyProvider({ + spawn: vi.fn(async () => ({ id: 'pty-runtime-resumed' })), + write: vi.fn(), + resize: vi.fn(), + kill: vi.fn(), + shutdown: vi.fn(), + onData: vi.fn(() => vi.fn()), + onExit: vi.fn(() => vi.fn()), + listProcesses: vi.fn(async () => []), + getForegroundProcess: vi.fn(async () => null) + } as never) + const runtime = { + setPtyController: vi.fn(), + registerPty: vi.fn(), + noteTerminalSpawnCommand: vi.fn(), + onPtySpawned: vi.fn(), + onPtyExit: vi.fn(), + onPtyData: vi.fn() + } + const getSettings = vi.fn().mockReturnValue({ + activeCodexManagedAccountId: 'account-b', + codexManagedAccounts: [ + { id: 'account-a', managedHomePath: '/managed/origin/home' }, + { id: 'account-b', managedHomePath: '/managed/current/home' } + ] + }) + handlers.clear() + registerPtyHandlers( + mainWindow as never, + runtime as never, + vi.fn(() => '/managed/current/home'), + getSettings as never, + undefined, + undefined, + { prepareCodexSessionResume: async () => ({ codexHomePath: '/managed/origin/home' }) } + ) + const controller = runtime.setPtyController.mock.calls[0]?.[0] as RuntimeSpawnController + + await controller.spawn({ + cols: 80, + rows: 24, + worktreeId: 'wt-runtime', + command: 'codex resume session-a', + launchAgent: 'codex', + resumeProviderSession: { + key: 'session_id', + id: 'session-a', + transcriptPath: '/managed/origin/home/sessions/2026/07/20/rollout-a.jsonl' + } + }) + + expect(recordCodexPaneAccountMock.mock.calls).toEqual([ + ['pty-runtime-resumed', { selectionKey: 'host', accountId: 'account-a' }] + ]) + expect(forgetCodexPaneAccountMock).not.toHaveBeenCalled() + }) + + it('leaves a runtime-controller resumed Codex pane unattributed when no account owns its home', async () => { + type RuntimeSpawnController = { + spawn(args: Record): Promise<{ id: string }> + } + setLocalPtyProvider({ + spawn: vi.fn(async () => ({ id: 'pty-runtime-resumed' })), + write: vi.fn(), + resize: vi.fn(), + kill: vi.fn(), + shutdown: vi.fn(), + onData: vi.fn(() => vi.fn()), + onExit: vi.fn(() => vi.fn()), + listProcesses: vi.fn(async () => []), + getForegroundProcess: vi.fn(async () => null) + } as never) + const runtime = { + setPtyController: vi.fn(), + registerPty: vi.fn(), + noteTerminalSpawnCommand: vi.fn(), + onPtySpawned: vi.fn(), + onPtyExit: vi.fn(), + onPtyData: vi.fn() + } + const getSettings = vi.fn().mockReturnValue({ + activeCodexManagedAccountId: 'account-b', + codexManagedAccounts: [{ id: 'account-b', managedHomePath: '/managed/current/home' }] + }) + handlers.clear() + registerPtyHandlers( + mainWindow as never, + runtime as never, + vi.fn(() => '/managed/current/home'), + getSettings as never, + undefined, + undefined, + { prepareCodexSessionResume: async () => ({ codexHomePath: '/managed/shared-mirror/home' }) } + ) + const controller = runtime.setPtyController.mock.calls[0]?.[0] as RuntimeSpawnController + + await controller.spawn({ + cols: 80, + rows: 24, + worktreeId: 'wt-runtime', + command: 'codex resume session-a', + launchAgent: 'codex', + resumeProviderSession: { + key: 'session_id', + id: 'session-a', + transcriptPath: '/managed/shared-mirror/home/sessions/2026/07/20/rollout-a.jsonl' + } + }) + + expect(recordCodexPaneAccountMock).not.toHaveBeenCalled() + expect(forgetCodexPaneAccountMock).toHaveBeenCalledWith('pty-runtime-resumed') + }) + it('seeds cold restore at recovered dimensions with a legacy dimensionless fallback', async () => { const oscLinks = [{ row: 0, startCol: 0, endCol: 8, uri: 'https://example.com/restored' }] const coldRestore = {