Background job (bg/fleet) session bookkeeping: dangling sessionId makes sessions appear vanished; spurious "source session not found" crash
Background job (bg/fleet) session bookkeeping: dangling sessionId makes sessions appear vanished; spurious "source session not found" crash
Environment: Claude Code 2.1.203–2.1.211, macOS 15 (darwin), daemon-backed background jobs (/bg slash command + fleet view).
Symptom 1: job's declared session is a stub; real conversation stays under the source UUID
A background job's state.json records sessionId/resumeSessionId = a UUID whose .jsonl only ever contains two lines (ai-title, agent-name) — the actual conversation lives, and keeps growing, under the source session's UUID. Any UI path that resolves the session through the job record (fleet click, job list resume) hits the stub and reports the session missing, so users believe the transcript was deleted.
Evidence (job 27d888f5):
- Conversation (prompt + full answer) written 2026-07-18T20:27–20:31Z under session
70c6aaaf-e49d-43d4-9992-a03e9be918f9(257 KB, intact). - Job created 2026-07-19T08:17:03Z (
daemon.log:bg spawned 27d888f5 (slash)) with freshsessionId 27d888f5-...; that file is 272 bytes, mtime = creation time, only title stubs. - Job settled
doneat 09:17:38Z withdetailtext that exists only in the source transcript. - Fleet click 2026-07-21T12:15:22Z (
bg claimed-spare 27d888f5 (fleet)) → session resolves to stub → shown as not found/vanished. - Manually running
claude --resume 70c6aaaf-...works; the conversation was never lost, only the pointer dangles.
Observed worker spawn shape: --bg-pty-host ... -- <version> --session-id <jobId> --fork-session --resume <sourceTranscriptPath> ... — in some runs the fork materializes under the declared --session-id (content lands there), in others it never does (stub only), and the job state is never reconciled to the transcript that actually holds the conversation.
Symptom 2: spurious "source session not found" crash while the file exists
Job f8df35a1 (created 2026-07-15T16:39Z from a live session, same daemon): never executed a turn (its session file stayed a 256-byte title stub), then at daemon respawn crashed:
[2026-07-16T11:20:37.864Z] [bg] bg settled f8df35a1 (crashed): source session /Users/mee/.claude/projects/-Users-mee/b8aec045-....jsonl not found
Verified the same minute: that file existed, was valid JSONL (79 parseable lines), readable, with original birthtime — it was never missing. On crash the daemon renamed the job's stub to <sessionId>.orphaned-<epochMs>-<hash>.jsonl.
Impact / asks
- Users interpret dangling pointers as data loss ("my session vanished") — three independent occurrences in one week on this machine.
- After a fork, reconcile the job's
sessionId/resumeSessionIdto the transcript that actually received the conversation (or verify the fork wrote to the declared id). - When session resolution fails, fall back to locating the transcript by title/content or report the source UUID instead of a bare not-found.
- Fix the false-negative existence check behind the "source session not found" crash.