diff --git a/src/cli/index.ts b/src/cli/index.ts index 5a995987dd..ff7be3ae60 100755 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -222,7 +222,6 @@ async function handleStart(options: { block?: boolean } = {}) { const serviceToken = loadServiceTokenFromFile(process.env); if (serviceToken) process.env.OPENCODEX_API_AUTH_TOKEN = serviceToken; const requestedPort = parsePortOption(); - if (!currentExternalCodexModelProvider()) reconcileJournal(); const existingPid = readPid(); if (existingPid) { const live = await findLiveProxy(); @@ -232,6 +231,10 @@ async function handleStart(options: { block?: boolean } = {}) { } removePid(existingPid); } + // A losing concurrent start must not restore the active proxy's Codex config. + // Establish that the PID-file owner is stale before reconciling a dead journal; + // a healthy owner exits above without changing integration state (#1230). + if (!currentExternalCodexModelProvider()) reconcileJournal(); // Interactive-only update prompt. Must run BEFORE we bind a port / write a // PID: choosing "Update now" installs globally and exits, so we never want a diff --git a/tests/cli-start-journal-order.test.ts b/tests/cli-start-journal-order.test.ts new file mode 100644 index 0000000000..446211225d --- /dev/null +++ b/tests/cli-start-journal-order.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const source = readFileSync(join(import.meta.dir, "..", "src", "cli", "index.ts"), "utf8"); + +function handleStartSource(): string { + const start = source.indexOf("async function handleStart("); + const end = source.indexOf("async function handleEnsure(", start); + expect(start).toBeGreaterThanOrEqual(0); + expect(end).toBeGreaterThan(start); + return source.slice(start, end); +} + +describe("handleStart journal ownership ordering (#1230)", () => { + test("a healthy PID-file proxy is detected before journal reconciliation", () => { + const handleStart = handleStartSource(); + const readPid = handleStart.indexOf("const existingPid = readPid();"); + const findLive = handleStart.indexOf("const live = await findLiveProxy();", readPid); + const healthyExit = handleStart.indexOf("process.exit(1);", findLive); + const removeStalePid = handleStart.indexOf("removePid(existingPid);", healthyExit); + const reconcile = handleStart.indexOf("reconcileJournal();", removeStalePid); + const updatePrompt = handleStart.indexOf("await maybeShowUpdatePrompt();", reconcile); + + expect(readPid).toBeGreaterThanOrEqual(0); + expect(findLive).toBeGreaterThan(readPid); + expect(healthyExit).toBeGreaterThan(findLive); + expect(removeStalePid).toBeGreaterThan(healthyExit); + expect(reconcile).toBeGreaterThan(removeStalePid); + expect(updatePrompt).toBeGreaterThan(reconcile); + }); +});