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.
This commit is contained in:
Paul Bakaus
2026-07-12 20:04:19 -07:00
parent 421c1a93f5
commit 5e1925f9d2
2 changed files with 17 additions and 1 deletions
@@ -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(() => {});
@@ -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 });