From e87772b3a4b66797d53e1bb6d033d143a11a1aca Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:34:49 -0700 Subject: [PATCH] test: retire a dormant worker through host-owned status (#23686) * test(e2e): await renderer recovery after worker exit * test(e2e): publish worker recovery through authenticated hooks * test: keep retired background worker dormant before activation --- ...completed-worker-retirement-resume.spec.ts | 116 +++++------------- .../completed-worker-retirement-fixture.ts | 30 ++++- 2 files changed, 57 insertions(+), 89 deletions(-) diff --git a/tests/e2e/completed-worker-retirement-resume.spec.ts b/tests/e2e/completed-worker-retirement-resume.spec.ts index 6935cbe1895..0af7ca46f12 100644 --- a/tests/e2e/completed-worker-retirement-resume.spec.ts +++ b/tests/e2e/completed-worker-retirement-resume.spec.ts @@ -160,21 +160,13 @@ for (const closeMode of ['terminal-close-cli', 'worker-release'] as const) { const workerBefore = terminalIdentity(worker) const workerPaneKey = `${worker.tabId}:${worker.leafId}` expect(worker.worktreeId).toBe(targetWorktreeId) - await orcaPage.evaluate( - ({ tabId, worktreeId }) => { - window.dispatchEvent( - new CustomEvent('orca-background-mount-terminal-worktree', { - detail: { worktreeId, tabIds: [tabId] } - }) - ) - }, - { tabId: worker.tabId, worktreeId: targetWorktreeId } - ) - await expect - .poll(() => - orcaPage.evaluate((tabId) => Boolean(window.__paneManagers?.get(tabId)), workerBefore.tabId) + // Keep the never-opened worker dormant so foreground-shell cleanup cannot retire it first. + expect( + await orcaPage.evaluate( + (tabId) => Boolean(window.__paneManagers?.get(tabId)), + workerBefore.tabId ) - .toBe(true) + ).toBe(false) expect( await orcaPage.evaluate( (worktreeId) => window.__store?.getState().everActivatedWorktreeIds.has(worktreeId), @@ -202,70 +194,13 @@ for (const closeMode of ['terminal-close-cli', 'worker-release'] as const) { throw new Error('Background worker did not receive its dispatch capability') } - const transcriptPath = seedCurrentCodexTranscript( - isolatedHome, - PROVIDER_SESSION_ID, - targetWorktreePath - ) + seedCurrentCodexTranscript(isolatedHome, PROVIDER_SESSION_ID, targetWorktreePath) - await orcaPage.evaluate( - ({ - agentCommand, - paneKey, - providerSessionId, - tabId, - terminalHandle, - transcriptPath, - worktreeId - }) => { - const state = window.__store?.getState() - if (!state) { - throw new Error('Renderer store unavailable') - } - const providerSession = { - key: 'session_id' as const, - id: providerSessionId, - transcriptPath - } - const metadata = { tabId, worktreeId, terminalHandle } - const recovery = { - providerSession, - launchConfig: { - // Why not bare 'codex': resume prefers the captured command over - // agentCmdOverrides, so a bare name would resolve the machine's real - // Codex off PATH and unpin the adoption leg this spec exercises. - agentCommand, - agentArgs: '--dangerously-bypass-approvals-and-sandbox', - agentEnv: {} - } - } - state.setAgentStatus( - paneKey, - { state: 'working', prompt: 'Report completion, then exit normally', agentType: 'codex' }, - 'Completed background worker', - undefined, - metadata, - recovery - ) - state.setAgentStatus( - paneKey, - { state: 'done', prompt: 'Report completion, then exit normally', agentType: 'codex' }, - 'Completed background worker', - undefined, - metadata, - recovery - ) - }, - { - agentCommand: completedWorkerFakeCodexCommand, - paneKey: workerPaneKey, - providerSessionId: PROVIDER_SESSION_ID, - tabId: worker.tabId, - terminalHandle: workerHandle, - transcriptPath, - worktreeId: targetWorktreeId - } - ) + await client.call('terminal.send', { + terminal: workerHandle, + text: 'ORCA_E2E_PUBLISH_DONE', + enter: true + }) const expectedRecovery = { origin: 'live', @@ -348,18 +283,23 @@ for (const closeMode of ['terminal-close-cli', 'worker-release'] as const) { await expect .poll(() => readCompletedWorkerLedger().filter((event) => event.event === 'normal-exit')) .toHaveLength(1) - expect( - await orcaPage.evaluate( - ({ paneKey, tabId, worktreeId }) => { - const state = window.__store?.getState() - return { - tabPresent: Boolean(state?.tabsByWorktree[worktreeId]?.some((tab) => tab.id === tabId)), - recoveryPresent: Boolean(state?.sleepingAgentSessionsByPaneKey[paneKey]) - } - }, - { paneKey: workerPaneKey, tabId: workerBefore.tabId, worktreeId: targetWorktreeId } + // The exit marker precedes the asynchronous renderer recovery update. + await expect + .poll(() => + orcaPage.evaluate( + ({ paneKey, tabId, worktreeId }) => { + const state = window.__store?.getState() + return { + tabPresent: Boolean( + state?.tabsByWorktree[worktreeId]?.some((tab) => tab.id === tabId) + ), + recoveryPresent: Boolean(state?.sleepingAgentSessionsByPaneKey[paneKey]) + } + }, + { paneKey: workerPaneKey, tabId: workerBefore.tabId, worktreeId: targetWorktreeId } + ) ) - ).toEqual({ tabPresent: true, recoveryPresent: true }) + .toEqual({ tabPresent: true, recoveryPresent: true }) await orcaPage.evaluate( ({ paneKey, tabId, worktreeId }) => { diff --git a/tests/e2e/helpers/completed-worker-retirement-fixture.ts b/tests/e2e/helpers/completed-worker-retirement-fixture.ts index 5ac0d8c1948..89ae5297fe4 100644 --- a/tests/e2e/helpers/completed-worker-retirement-fixture.ts +++ b/tests/e2e/helpers/completed-worker-retirement-fixture.ts @@ -20,14 +20,34 @@ import { buildFakeAgentCommandOverride } from './fake-agent-command-override' import { FAKE_AGENT_PASTE_END_SCANNER_SOURCE } from './fake-agent-paste-end-scanner' const fakeCliDir = mkdtempSync(path.join(os.tmpdir(), 'orca-e2e-retired-worker-')) +const recoveryConfigPath = path.join(fakeCliDir, 'recovery-config.json') const lifecycleLedgerPath = path.join(fakeCliDir, 'codex-lifecycle.jsonl') export const completedWorkerFakeCodexCommand = buildFakeAgentCommandOverride( path.join(fakeCliDir, process.platform === 'win32' ? 'codex.cmd' : 'codex') ) const fakeCodexSource = ` -const { appendFileSync } = require('node:fs') +const { appendFileSync, readFileSync } = require('node:fs') const ledger = process.env.ORCA_E2E_CODEX_LIFECYCLE_LEDGER const append = (event) => appendFileSync(ledger, JSON.stringify({ pid: process.pid, ...event }) + '\\n') +async function publishRecovery() { + const config = JSON.parse(readFileSync(${JSON.stringify(recoveryConfigPath)}, 'utf8')) + for (const hook_event_name of ['UserPromptSubmit', 'Stop']) { + const response = await fetch('http://127.0.0.1:' + process.env.ORCA_AGENT_HOOK_PORT + '/hook/codex', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-Orca-Agent-Hook-Token': process.env.ORCA_AGENT_HOOK_TOKEN }, + body: JSON.stringify({ + paneKey: process.env.ORCA_PANE_KEY, + tabId: process.env.ORCA_TAB_ID, + worktreeId: process.env.ORCA_WORKTREE_ID, + launchToken: process.env.ORCA_AGENT_LAUNCH_TOKEN, + env: process.env.ORCA_AGENT_HOOK_ENV, + version: process.env.ORCA_AGENT_HOOK_VERSION, + payload: { hook_event_name, prompt: 'Report completion, then exit normally', ...config } + }) + }) + if (response.status !== 204) throw new Error('Recovery hook rejected: ' + response.status) + } +} const args = process.argv.slice(2) if (args.includes('app-server')) { process.stderr.write("error: unrecognized subcommand 'app-server'\\n") @@ -44,6 +64,10 @@ process.stdin.on('data', (chunk) => { process.stdout.write('\\x1b[?25h') } append({ event: 'input', input }) + if (input.includes('ORCA_E2E_PUBLISH_DONE')) { + void publishRecovery().catch((error) => process.stderr.write(String(error))) + return + } if (input.includes('ORCA_E2E_EXIT_AFTER_DONE')) { append({ event: 'normal-exit' }) process.exit(0) @@ -179,6 +203,10 @@ export function seedCurrentCodexTranscript( payload: { id: providerSessionId, cwd } })}\n` ) + writeFileSync( + recoveryConfigPath, + JSON.stringify({ session_id: providerSessionId, transcript_path: transcriptPath }) + ) return transcriptPath }