From 5e1925f9d28737d220127ba6c22ef48b6531eee6 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Sun, 12 Jul 2026 20:04:19 -0700 Subject: [PATCH] Handle empty Codex worker shutdown Treat a never-used app-server thread with no rollout file as already archived while retaining real archive failures.\n\nAI-assisted: OpenAI Codex. --- skill/scripts/live/codex-worker-supervisor.mjs | 7 ++++++- tests/live-codex-worker-supervisor.test.mjs | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/skill/scripts/live/codex-worker-supervisor.mjs b/skill/scripts/live/codex-worker-supervisor.mjs index 6a32ab1c2..82e199fcc 100644 --- a/skill/scripts/live/codex-worker-supervisor.mjs +++ b/skill/scripts/live/codex-worker-supervisor.mjs @@ -314,7 +314,12 @@ export class CodexLiveWorkerSupervisor { await this.client.archiveThread(this.thread.id); archived = true; } catch (error) { - this.log(`thread archive failed: ${error.message}`); + if (/no rollout found/i.test(String(error?.message || ''))) { + archived = true; + this.log('empty worker thread had no persisted rollout; treating it as archived'); + } else { + this.log(`thread archive failed: ${error.message}`); + } } } await this.client.close().catch(() => {}); diff --git a/tests/live-codex-worker-supervisor.test.mjs b/tests/live-codex-worker-supervisor.test.mjs index c8e23801e..77193705a 100644 --- a/tests/live-codex-worker-supervisor.test.mjs +++ b/tests/live-codex-worker-supervisor.test.mjs @@ -208,6 +208,17 @@ describe('Codex Live worker supervisor ownership and lifecycle', () => { assert.equal(state.archived, false); }); + it('treats an unused thread with no persisted rollout as already archived', async () => { + const cwd = mkdtempSync(path.join(tmpdir(), 'codex-supervisor-empty-thread-')); + const statePath = path.join(cwd, 'state.json'); + const client = fakeClient(); + client.archiveThread = async () => { throw new Error('thread/archive: no rollout found for thread id empty'); }; + const supervisor = createSupervisor({ cwd, statePath, client }); + supervisor.thread = { id: 'empty' }; + await supervisor.shutdown({ archive: true }); + assert.equal(JSON.parse(readFileSync(statePath, 'utf-8')).status, 'archived'); + }); + it('publishes progressive source checkpoints only through the fenced publisher', async () => { const cwd = mkdtempSync(path.join(tmpdir(), 'codex-supervisor-publish-')); mkdirSync(path.join(cwd, 'src'), { recursive: true });