From 1c9aa40501db9229d61c83ac3bb1f1db737d604b Mon Sep 17 00:00:00 2001 From: JUN Date: Mon, 9 Nov 2026 16:57:03 +0900 Subject: [PATCH 1/3] test(clients): give each Desktop lifecycle case the budget its child process already has --- tests/clients/client-connect.test.ts | 34 ++++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tests/clients/client-connect.test.ts b/tests/clients/client-connect.test.ts index f3472bc4b71..e8f8a792b51 100644 --- a/tests/clients/client-connect.test.ts +++ b/tests/clients/client-connect.test.ts @@ -1342,6 +1342,10 @@ function runDesktopLifecycleScenario(mode: string) { } describe("Desktop copy coherence across client lifecycle", () => { + // Each case runs its scenario in a real `bun test` child, bounded by spawnSync at INTERNAL_DEADLINE_MS. + // Under Bun's 5 s default the case gave up first: a loaded `test changed` run timed out + // `status-receipt` at 5010 ms while its child was still inside its own 15 s deadline (#8041). + const LIFECYCLE_CASE_BUDGET_MS = SPAWN_BUDGET_MS; // The first generated lifecycle fixture pays the full client lifecycle module graph load. // Warm the same relative require prologue before the fixture's 15-second child deadline. beforeAll(async () => { @@ -1356,7 +1360,7 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.pending || r.backupPresent || r.persistedOutcome).toBe(false); expect(r.foreignPreserved && r.baselineUnchanged && r.credentialZeroed).toBe(true); if (r.commits > 0) expect(r.desktopBeforeCommit).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test.each(["recover-backup", "same-old"])("%s returns rolled_back without a false commit", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.error).toBeNull(); @@ -1366,28 +1370,28 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.tokenIsOld && r.desktopIsOld).toBe(true); expect(r.pending || r.backupPresent || r.persistedOutcome).toBe(false); expect(r.baselineUnchanged && r.credentialZeroed).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("normal failed candidate rolls both local copies back before returning failure", () => { const r = runDesktopLifecycleScenario("rollback"); expect(r.error).not.toBeNull(); expect(r.commits).toBe(0); expect(r.tokenIsOld && r.desktopIsOld).toBe(true); expect(r.pending || r.backupPresent).toBe(false); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test.each(["same-old-abort-failure", "recover-neither", "recover-probe-error"])("%s preserves recovery evidence", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.result).toBeNull(); expect(r.error).not.toBeNull(); expect(r.commits).toBe(0); expect(r.pending && r.backupPresent && r.credentialZeroed).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("CLI reports a recovered rollback honestly", () => { const r = runDesktopLifecycleScenario("recover-backup-cli"); expect(r.cliRotation.exitCode).toBe(0); expect(r.cliRotation.value.rotation).toBe("rolled_back"); expect(r.cliRotation.revokedClaim).toBe(false); expect(r.desktopIsOld && r.tokenIsOld).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test.each(["disconnect", "disconnect-foreign", "disconnect-resume", "disconnect-after-clear"])("%s restores projection and retries idempotently", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.error).toBeNull(); @@ -1396,26 +1400,26 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.receiptPhase).toBe("complete"); expect(r.second.tokenRemoved).toBe(false); if (mode === "disconnect-foreign") expect(r.userAddedPreserved).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("legacy current-hub profile disconnects via labeled standard fallback", () => { const r = runDesktopLifecycleScenario("disconnect-legacy"); expect(r.error).toBeNull(); expect(r.result.desktopRestoration).toBe("standard_fallback"); expect(r.desktopHasKey).toBe(false); expect(r.tokenAbsent && r.foreignPreserved).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("protected Desktop edits block destructive disconnect", () => { const r = runDesktopLifecycleScenario("disconnect-protected"); expect(r.error).not.toBeNull(); expect(r.tokenIsOld && r.desktopIsOld).toBe(true); expect(r.stateKind).toBe("connected"); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("post-await sync cannot overwrite a prepared disconnect", () => { const r = runDesktopLifecycleScenario("sync-claim"); expect(r.error).toBe("client_disconnect_pending"); expect(r.catalogUnchanged).toBe(true); expect(r.receiptPhase).toBe("prepared"); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("disconnect expectedOwner refuses a newly connected owner before claiming or deleting state", () => { const r = runDesktopLifecycleScenario("disconnect-expected-owner"); expect(r.result).toBeNull(); @@ -1423,30 +1427,30 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.stateKind).toBe("connected"); expect(r.tokenIsNew && r.catalogUnchanged).toBe(true); expect(r.receiptPhase).toBe("absent"); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("disconnect claims its receipt before Codex-only restoration outside L", () => { const r = runDesktopLifecycleScenario("disconnect-order"); expect(r.error).toBeNull(); expect(r.codexSawPrepared && r.codexOutsideL).toBe(true); expect(r.receiptPhase).toBe("complete"); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("sync CAS preserves a newer token generation and leaves catalog bytes unchanged", () => { const r = runDesktopLifecycleScenario("sync-generation-change"); expect(r.error).toBe("client_connection_changed"); expect(r.tokenIsNew && r.catalogUnchanged).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("full-owner clear cannot delete a newer connection with the same key id", () => { const r = runDesktopLifecycleScenario("clear-owner-change"); expect(r.result).toBe("conflict"); expect(r.stateKind).toBe("connected"); expect(r.tokenIsOld).toBe(true); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test("sync supplies the read-only guard at the actual injection seam", () => { const r = runDesktopLifecycleScenario("sync-queued-guard"); expect(r.guardSeen).toBe(true); expect(r.writesAfterGuard).toBe(0); expect(r.error).toBe("client_disconnect_pending"); - }); + }, LIFECYCLE_CASE_BUDGET_MS); test.each(["status-lock", "status-receipt"])("%s cannot discard another operation's backup", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.error).toBeNull(); @@ -1454,7 +1458,7 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.statusInside.backupPresent).toBe(true); expect(r.statusOutside.kind).toBe(mode === "status-lock" ? "orphan-cleaned" : "recovery-required"); expect(r.backupPresent).toBe(mode === "status-receipt"); - }); + }, LIFECYCLE_CASE_BUDGET_MS); }); describe("a sibling instance never connects, syncs or disconnects the shared Codex home", () => { From a1c0ee6f26feec98c3930e9c086495b1216ee8c5 Mon Sep 17 00:00:00 2001 From: JUN Date: Mon, 9 Nov 2026 18:09:51 +0900 Subject: [PATCH 2/3] test(clients): give the sibling runEvalChild cases the same child-process budget --- tests/clients/client-connect.test.ts | 45 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/clients/client-connect.test.ts b/tests/clients/client-connect.test.ts index e8f8a792b51..77bc4bdcb18 100644 --- a/tests/clients/client-connect.test.ts +++ b/tests/clients/client-connect.test.ts @@ -697,7 +697,7 @@ describe("connect transaction and offline disconnect", () => { expect(run.parsed.calls).toEqual([]); expect(run.parsed.artifacts).toEqual({ token: false, catalog: false, credentialZeroed: true }); } finally { run.cleanup(); } - }); + }, SPAWN_BUDGET_MS); test("commits key id/state last, zeroes authority, and disconnects with the hub offline", () => { const run = runTransactionScenario("success"); try { @@ -774,7 +774,7 @@ describe("connect transaction and offline disconnect", () => { expect(run.configBytes).not.toContain("issued-id"); expect(`${run.parsed.error} ${run.stderr}`).not.toContain(`ocx_data_${"d".repeat(40)}`); } finally { run.cleanup(); } - }); + }, SPAWN_BUDGET_MS); } }); @@ -902,7 +902,7 @@ describe("connected sync and disconnect conflicts", () => { expect(run.parsed.state.kind).toBe("connected"); expect(run.parsed.tokenExists).toBe(true); } finally { run.cleanup(); } - }); + }, SPAWN_BUDGET_MS); test("hub 503 keeps and applies the last-known-good catalog as stale", () => { const run = runConnectedStateScenario("sync-503"); @@ -912,7 +912,7 @@ describe("connected sync and disconnect conflicts", () => { expect(run.parsed.result).toMatchObject({ stale: true, catalogWritten: false, injected: false }); expect(run.parsed.state.kind).toBe("connected"); } finally { run.cleanup(); } - }); + }, SPAWN_BUDGET_MS); test("journal ownership conflict preserves every artifact and connected state", () => { const run = runConnectedStateScenario("disconnect-conflict"); @@ -924,7 +924,7 @@ describe("connected sync and disconnect conflicts", () => { expect(run.parsed.tokenExists).toBe(true); expect(run.parsed.journalExists).toBe(true); } finally { run.cleanup(); } - }); + }, SPAWN_BUDGET_MS); test("a journal left owned by the proxy process does not strand the connection", () => { // Connecting after `ocx start` is the normal path, not an edge case: routing is already @@ -941,7 +941,7 @@ describe("connected sync and disconnect conflicts", () => { expect(run.parsed.state.kind).toBe("disconnected"); expect(run.parsed.journalExists).toBe(false); } finally { run.cleanup(); } - }); + }, SPAWN_BUDGET_MS); }); describe("recoverable connected key rotation", () => { @@ -1032,7 +1032,7 @@ describe("recoverable connected key rotation", () => { } finally { removeTreeWithRetry(opencodexHome); } - }); + }, SPAWN_BUDGET_MS); test("status removes a .prev orphan only when no rotation marker exists", async () => { const home = mkdtempSync(join(tmpdir(), "ocx-client-orphan-")); @@ -1345,7 +1345,6 @@ describe("Desktop copy coherence across client lifecycle", () => { // Each case runs its scenario in a real `bun test` child, bounded by spawnSync at INTERNAL_DEADLINE_MS. // Under Bun's 5 s default the case gave up first: a loaded `test changed` run timed out // `status-receipt` at 5010 ms while its child was still inside its own 15 s deadline (#8041). - const LIFECYCLE_CASE_BUDGET_MS = SPAWN_BUDGET_MS; // The first generated lifecycle fixture pays the full client lifecycle module graph load. // Warm the same relative require prologue before the fixture's 15-second child deadline. beforeAll(async () => { @@ -1360,7 +1359,7 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.pending || r.backupPresent || r.persistedOutcome).toBe(false); expect(r.foreignPreserved && r.baselineUnchanged && r.credentialZeroed).toBe(true); if (r.commits > 0) expect(r.desktopBeforeCommit).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test.each(["recover-backup", "same-old"])("%s returns rolled_back without a false commit", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.error).toBeNull(); @@ -1370,28 +1369,28 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.tokenIsOld && r.desktopIsOld).toBe(true); expect(r.pending || r.backupPresent || r.persistedOutcome).toBe(false); expect(r.baselineUnchanged && r.credentialZeroed).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("normal failed candidate rolls both local copies back before returning failure", () => { const r = runDesktopLifecycleScenario("rollback"); expect(r.error).not.toBeNull(); expect(r.commits).toBe(0); expect(r.tokenIsOld && r.desktopIsOld).toBe(true); expect(r.pending || r.backupPresent).toBe(false); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test.each(["same-old-abort-failure", "recover-neither", "recover-probe-error"])("%s preserves recovery evidence", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.result).toBeNull(); expect(r.error).not.toBeNull(); expect(r.commits).toBe(0); expect(r.pending && r.backupPresent && r.credentialZeroed).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("CLI reports a recovered rollback honestly", () => { const r = runDesktopLifecycleScenario("recover-backup-cli"); expect(r.cliRotation.exitCode).toBe(0); expect(r.cliRotation.value.rotation).toBe("rolled_back"); expect(r.cliRotation.revokedClaim).toBe(false); expect(r.desktopIsOld && r.tokenIsOld).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test.each(["disconnect", "disconnect-foreign", "disconnect-resume", "disconnect-after-clear"])("%s restores projection and retries idempotently", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.error).toBeNull(); @@ -1400,26 +1399,26 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.receiptPhase).toBe("complete"); expect(r.second.tokenRemoved).toBe(false); if (mode === "disconnect-foreign") expect(r.userAddedPreserved).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("legacy current-hub profile disconnects via labeled standard fallback", () => { const r = runDesktopLifecycleScenario("disconnect-legacy"); expect(r.error).toBeNull(); expect(r.result.desktopRestoration).toBe("standard_fallback"); expect(r.desktopHasKey).toBe(false); expect(r.tokenAbsent && r.foreignPreserved).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("protected Desktop edits block destructive disconnect", () => { const r = runDesktopLifecycleScenario("disconnect-protected"); expect(r.error).not.toBeNull(); expect(r.tokenIsOld && r.desktopIsOld).toBe(true); expect(r.stateKind).toBe("connected"); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("post-await sync cannot overwrite a prepared disconnect", () => { const r = runDesktopLifecycleScenario("sync-claim"); expect(r.error).toBe("client_disconnect_pending"); expect(r.catalogUnchanged).toBe(true); expect(r.receiptPhase).toBe("prepared"); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("disconnect expectedOwner refuses a newly connected owner before claiming or deleting state", () => { const r = runDesktopLifecycleScenario("disconnect-expected-owner"); expect(r.result).toBeNull(); @@ -1427,30 +1426,30 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.stateKind).toBe("connected"); expect(r.tokenIsNew && r.catalogUnchanged).toBe(true); expect(r.receiptPhase).toBe("absent"); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("disconnect claims its receipt before Codex-only restoration outside L", () => { const r = runDesktopLifecycleScenario("disconnect-order"); expect(r.error).toBeNull(); expect(r.codexSawPrepared && r.codexOutsideL).toBe(true); expect(r.receiptPhase).toBe("complete"); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("sync CAS preserves a newer token generation and leaves catalog bytes unchanged", () => { const r = runDesktopLifecycleScenario("sync-generation-change"); expect(r.error).toBe("client_connection_changed"); expect(r.tokenIsNew && r.catalogUnchanged).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("full-owner clear cannot delete a newer connection with the same key id", () => { const r = runDesktopLifecycleScenario("clear-owner-change"); expect(r.result).toBe("conflict"); expect(r.stateKind).toBe("connected"); expect(r.tokenIsOld).toBe(true); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test("sync supplies the read-only guard at the actual injection seam", () => { const r = runDesktopLifecycleScenario("sync-queued-guard"); expect(r.guardSeen).toBe(true); expect(r.writesAfterGuard).toBe(0); expect(r.error).toBe("client_disconnect_pending"); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); test.each(["status-lock", "status-receipt"])("%s cannot discard another operation's backup", mode => { const r = runDesktopLifecycleScenario(mode); expect(r.error).toBeNull(); @@ -1458,7 +1457,7 @@ describe("Desktop copy coherence across client lifecycle", () => { expect(r.statusInside.backupPresent).toBe(true); expect(r.statusOutside.kind).toBe(mode === "status-lock" ? "orphan-cleaned" : "recovery-required"); expect(r.backupPresent).toBe(mode === "status-receipt"); - }, LIFECYCLE_CASE_BUDGET_MS); + }, SPAWN_BUDGET_MS); }); describe("a sibling instance never connects, syncs or disconnects the shared Codex home", () => { From db1bdc2aec288ac3f1a08635fe06b12c6658455a Mon Sep 17 00:00:00 2001 From: JUN Date: Mon, 9 Nov 2026 19:14:47 +0900 Subject: [PATCH 3/3] test(clients): stop the Desktop lifecycle sync fixture from probing the host Codex CLI --- tests/clients/client-connect.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/clients/client-connect.test.ts b/tests/clients/client-connect.test.ts index 77bc4bdcb18..1ebd7dd6ecf 100644 --- a/tests/clients/client-connect.test.ts +++ b/tests/clients/client-connect.test.ts @@ -1190,7 +1190,9 @@ function runDesktopLifecycleScenario(mode: string) { await (async () => { let result = null, error = null, second = null, statusInside = null, statusOutside = null, cliRotation = null; const credential = new TextEncoder().encode("ocx_admin_fixture"); - const deps = { fetchImpl, lifecycleLockDeps: lockDeps }; + // No local Codex runtime, as on CI: otherwise sync execs whatever codex CLI the host has + // (seconds on a workstation) to read its effort ladder, which no case here asserts on. + const deps = { fetchImpl, lifecycleLockDeps: lockDeps, catalogCompatibility: { supportedEfforts: () => null } }; try { if (mode.startsWith("disconnect")) { let journalSpy;