diff --git a/src/lib/onboard/forward-start.test.ts b/src/lib/onboard/forward-start.test.ts index 76954deee40..549e9eb9949 100644 --- a/src/lib/onboard/forward-start.test.ts +++ b/src/lib/onboard/forward-start.test.ts @@ -949,7 +949,9 @@ describe("runDetachedForwardStartWithRetries", () => { events.push("spawn-1"); fs.writeSync( stderr, - "Error: code: 'The system is not in a state required for the operation's execution', message: \"sandbox is not ready\"\n", + `Error: × code: 'The system is not in a state required for the operation's + │ execution', message: "sandbox is not ready" +`, ); return { pid: 784 }; }) @@ -988,7 +990,9 @@ describe("runDetachedForwardStartWithRetries", () => { const spawn = vi.fn().mockImplementation(({ stderr }: { stderr: number }) => { fs.writeSync( stderr, - "Permission denied (publickey); previous attempt reported Error: code: 'The system is not in a state required for the operation's execution', message: \"sandbox is not ready\"\n", + `Permission denied (publickey); previous attempt reported Error: × code: 'The system is not in a state required for the operation's + │ execution', message: "sandbox is not ready" +`, ); return { pid: 784 }; }); @@ -1132,7 +1136,8 @@ describe("looksLikeForwardListenerStartFailure", () => { it("matches only definitive listener termination diagnostics", () => { expect( looksLikeForwardListenerStartFailure( - "Error: code: 'The system is not in a state required for the operation's execution', message: \"sandbox is not ready\"", + `Error: × code: 'The system is not in a state required for the operation's + │ execution', message: "sandbox is not ready"`, ), ).toBe(true); expect( diff --git a/src/lib/onboard/forward-start.ts b/src/lib/onboard/forward-start.ts index 18ee12564ed..219c8a8d9ea 100644 --- a/src/lib/onboard/forward-start.ts +++ b/src/lib/onboard/forward-start.ts @@ -110,7 +110,8 @@ const OPENSHELL_SANDBOX_NOT_READY_DIAGNOSTIC = /^Error: code: 'The system is not in a state required for the operation's execution', message: "sandbox is not ready"$/i; function looksLikeSandboxNotReadyForwardStart(diagnostic: string): boolean { - return OPENSHELL_SANDBOX_NOT_READY_DIAGNOSTIC.test(diagnostic); + const normalized = compactText(diagnostic.replace(/[×│]/gu, " ")); + return OPENSHELL_SANDBOX_NOT_READY_DIAGNOSTIC.test(normalized); } /** diff --git a/test/e2e/live/gateway-guard-recovery.test.ts b/test/e2e/live/gateway-guard-recovery.test.ts index 92fd65b0bdd..72453288f45 100644 --- a/test/e2e/live/gateway-guard-recovery.test.ts +++ b/test/e2e/live/gateway-guard-recovery.test.ts @@ -157,13 +157,14 @@ async function inspectStartupCommand( return result.stdout.trim(); } -async function waitForSandboxExecAfterContainerRestart( +async function waitForSandboxExecReady( host: HostCliClient, sandboxName: string, progress: TestProgress, + artifactPrefix: string, ): Promise { await pollUntil({ - artifactPrefix: "legacy-restart-openshell-ready", + artifactPrefix, attempts: 12, delayMs: 3_000, probe: async (_attempt, artifactName) => @@ -454,6 +455,15 @@ test("gateway recovery restores /tmp guard chain after pod-recreate wipe (#2701) }, ); expect(createLegacyKeepalive.exitCode, resultText(createLegacyKeepalive)).toBe(0); + // Do not overlap the fixture's recreation with the restart below. The + // fixture runs in its own process, so the host must observe the replacement + // through OpenShell before starting the next container lifecycle transition. + await waitForSandboxExecReady( + host, + instance.sandboxName, + progress, + "legacy-recreate-openshell-ready", + ); const legacyContainerId = await findSandboxContainer(host, "legacy-restart-container-before"); expect(legacyContainerId).not.toBe(recoveredContainerId); @@ -466,7 +476,12 @@ test("gateway recovery restores /tmp guard chain after pod-recreate wipe (#2701) timeoutMs: 120_000, }); expect(legacyRestart.exitCode, resultText(legacyRestart)).toBe(0); - await waitForSandboxExecAfterContainerRestart(host, instance.sandboxName, progress); + await waitForSandboxExecReady( + host, + instance.sandboxName, + progress, + "legacy-restart-openshell-ready", + ); await gateway.waitForMissingManagedSupervisor(legacyContainerId, { onRetry: (attempt) => progress.event(`managed supervisor absence proof retry ${attempt}`), });