From e9cf1f1ede38321620559423318c1adc27fea07a Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sat, 8 Aug 2026 11:40:06 -0700 Subject: [PATCH] Stop reporting crashed sessions as running forever MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A session directory with a context/ subdir but no readable run.json never reached its first saveState call, so it isn't running — it crashed during setup before the run loop could write any state. listSessions now reports that case as crashed, which drops it from the default resume picker (only running/cancelled sessions are shown there) instead of showing a dead session as live indefinitely. --- src/session/index.ts | 7 +++++-- src/session/list-sessions.test.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/session/index.ts b/src/session/index.ts index 8bb51313d..9c1bccc2f 100644 --- a/src/session/index.ts +++ b/src/session/index.ts @@ -244,7 +244,10 @@ export async function listSessions(cwd: string, home: string = homedir()): Promi }); continue; } - // TUI sessions persist conversation under context/ before run.json exists. + // A session directory with context/ but no readable run.json never + // reached its first saveState call (see src/tui/runner.ts's early + // "running" write) and therefore isn't actually running: report it as + // crashed rather than fabricating liveness. try { const dirStat = await stat(sessionDir(cwd, entry, home)); await stat(sessionContextDir(cwd, entry, home)); @@ -252,7 +255,7 @@ export async function listSessions(cwd: string, home: string = homedir()): Promi sessionId: entry, task: "(conversation)", startedAt: dirStat.birthtimeMs > 0 ? dirStat.birthtimeMs : dirStat.mtimeMs, - status: "running", + status: "crashed", }); } catch { // Not a resumable session directory. diff --git a/src/session/list-sessions.test.ts b/src/session/list-sessions.test.ts index 85b10f0a0..ac869334f 100644 --- a/src/session/list-sessions.test.ts +++ b/src/session/list-sessions.test.ts @@ -30,6 +30,15 @@ test("listSessions includes TUI sessions with context/ but no run.json", async ( expect(row?.task).toBe("Untitled session"); }); +test("listSessions reports crashed, not running, for a session with no readable run.json", async () => { + const sessionId = generateSessionId(); + await initSessionDir(cwd, sessionId, home); + const listed = await listSessions(cwd, home); + const row = listed.find((s) => s.sessionId === sessionId); + expect(row?.status).not.toBe("running"); + expect(row?.status).toBe("crashed"); +}); + test("listSessions prefers run.json task title when present", async () => { const sessionId = generateSessionId(); await initSessionDir(cwd, sessionId, home);