diff --git a/src/infra/local-tui-processes.test.ts b/src/infra/local-tui-processes.test.ts index e347a1edf970..1f72d63607a5 100644 --- a/src/infra/local-tui-processes.test.ts +++ b/src/infra/local-tui-processes.test.ts @@ -134,6 +134,22 @@ describe("local TUI processes", () => { ).resolves.toEqual({ stopped: [], failed: [101] }); }); + it("fails closed when a live process identity can no longer be read", async () => { + const controller = { kill: vi.fn(() => true) }; + let reads = 0; + + await expect( + terminateLocalTuiProcesses({ + processes: [{ pid: 101, command: "openclaw-tui", startTime: "start" }], + controller, + graceMs: 0, + killGraceMs: 0, + readStartTime: () => (++reads === 1 ? "start" : undefined), + }), + ).resolves.toEqual({ stopped: [], failed: [101] }); + expect(controller.kill).not.toHaveBeenCalledWith(101, "SIGKILL"); + }); + it("refuses shared update mutation when a matched client survives", async () => { const processes = [{ pid: 101, command: "openclaw --profile work tui" }]; diff --git a/src/infra/local-tui-processes.ts b/src/infra/local-tui-processes.ts index 7e21691ba04d..d108789e27b1 100644 --- a/src/infra/local-tui-processes.ts +++ b/src/infra/local-tui-processes.ts @@ -182,7 +182,12 @@ export async function terminateLocalTuiProcesses(params: { stopped.push(proc.pid); continue; } - if (!proc.startTime || readStartTime(proc.pid) !== proc.startTime) { + const currentStartTime = readStartTime(proc.pid); + if (!currentStartTime) { + failed.push(proc.pid); + continue; + } + if (currentStartTime !== proc.startTime) { stopped.push(proc.pid); continue; }