[BUG] Interactive → background session handoff still leaves title-only .jsonl stubs on 2.1.270–2.1.273 — 7/7 stubs traced to `bg spawned|claimed-spare <id> (slash)` (follow-up to #81662, #85404)
Preflight Checklist
- [x] I have searched existing issues. This is the same bug as #81662 (closed as stale 2026-09-08, bot asked for a new issue if still relevant) and #85404 (closed as completed 2026-08-17; reproductions reported afterwards on 2.1.237 and 2.1.247). Filing new because both are closed and it still reproduces on the current release.
- [x] This is a single bug report.
- [x] I am using the latest version of Claude Code (2.1.273).
What's Wrong?
When an interactive CLI session is handed off to a background session, the new background session sometimes ends up with a transcript that contains only two metadata records (ai-title, agent-name, ~250 bytes) and no conversation. The original session file gets a continued-in record pointing at the new id, so the conversation effectively moves to an empty file. In the resume picker / agents view it shows up as empty or "it has no saved transcript". The conversation itself is intact under the original id.
On one Linux machine this happened 7 times between 2026-08-31 and 2026-09-16, most recently today on 2.1.273. Every single stub matches, to the millisecond, a daemon handoff logged as (slash):
| handoff (daemon.log) | daemon.log event | original session's continued-in record |
|---|---|---|
| 2026-08-31 23:47:40.920Z | bg spawned <B> (slash) | none found (older original file) |
| 2026-09-01 21:15:09.583Z | bg spawned <B> (slash) | none found (older original file) |
| 2026-09-02 15:38:56.822Z | bg claimed-spare <B> (slash) | 15:38:56.833Z |
| 2026-09-08 15:59:30.126Z | bg claimed-spare <B> (slash) | 15:59:30.159Z |
| 2026-09-08 17:33:33.881Z | bg spawned <B> (slash) | 17:33:33.897Z |
| 2026-09-15 17:38:53.606Z | bg spawned <B> (slash) | 17:38:53.712Z |
| 2026-09-16 15:45:50.058Z | bg spawned <B> (slash) | 15:45:50.079Z |
So both the fresh-spawn path and the claimed-spare path are affected.
How the handoff was triggered: I never typed /background. (slash) is not specific to that command: in 2.1.273 the dispatcher sets source: r==="repl" ? "slash" : r, so every handoff started from the interactive REPL is logged as slash. That includes pressing ← on an empty prompt, which opens the agents view and backgrounds the current conversation. That shortcut is the most likely trigger here: in the five cases where the original transcript records it, the handoff came between 2 minutes and 8 hours after the conversation's last activity, with no command in the transcript. So the bug is easy to hit without realizing a handoff happened at all.
Anatomy of one occurrence (2.1.270 → 2.1.273, 2026-09-15)
A = original interactive session, B = background session id created by the handoff.
A.jsonl: 578 records, 251 user/assistant messages, allversion: 2.1.270. Record 575 is
{"type":"continued-in","timestamp":"2026-09-15T17:38:53.712Z","sessionId":"A","continuedInSessionId":"B"},
followed by last-prompt and cost-state.
B.jsonl: exactly two lines, 252 bytes:{"type":"ai-title",...,"sessionId":"B"}and{"type":"agent-name",...,"sessionId":"B"}. Nouser/assistant/systemrecords.daemon.log:
````
[2026-09-15T17:38:53.606Z] [bg] bg spawned B (slash)
[2026-09-15T18:38:53.630Z] [bg] bg retire B: idle-prompt, idle 60m
[2026-09-15T18:38:53.705Z] [bg] bg settled B (done)
[2026-09-15T23:38:04.990Z] [bg] bg claimed-spare B (fleet)
- Because
Apoints toBandBhas no messages, the conversation looks lost from both entries.
What Should Happen?
The background session should either carry the conversation into B.jsonl or keep writing to/resuming A. A handoff should never leave a continued-in pointer aimed at a transcript with no messages.
Workaround in use
A systemd timer copies the conversation from A into B whenever B is a title-only stub and something else holds continued-in → B (written in compact JSON, because the "has messages" check matches the literal byte string "type":"user"). It has recovered all 7 cases with no data loss. claude --resume A also works by hand.
Environment
- Claude Code 2.1.270 / 2.1.273 (native install), daemon backend
- Fedora Linux 44, kernel 7.1, x86_64
- Interactive CLI sessions (
entrypoint: cli) handed off to the daemon (not via/background, see above)
Happy to provide full sanitized daemon.log excerpts or record-level dumps if useful.