Background session state.json: detail echoes the brief, state reads blocked when idle, and session writes are reverted
Environment
Claude Code 2.1.233, Linux. Background sessions started with claude --bg -n <name> --permission-mode auto --settings <file> --append-system-prompt-file <file>.
What happens
~/.claude/jobs/<short-id>/state.json carries state and detail. Both appear to be written by the harness rather than by the session, and two of their behaviours make the file hard to consume from outside:
1. detail holds a truncation of the session's intent, not its progress. For a session given a long brief via --append-system-prompt-file, detail repeatedly contained the first ~140 characters of that brief verbatim, including the leading Issue #… Read it first: … boilerplate, rather than a progress line. Comparing detail against the intent field in the same file shows one is a prefix of the other.
2. state reads blocked for a session that is merely idle, and a session cannot correct either field. One session wrote corrected values into its own state.json (state=done, a real detail), re-read the file two minutes later, and found both fields restored to blocked and the brief truncation. It reported that no tool in its set writes those fields.
Independently, at the same moment, claude agents --json reported that same session as working with a live pid — so the two sources disagreed, and the agent list was the accurate one.
Why it matters
These files are the only per-session progress surface available to something supervising sessions from outside (a scheduler, a monitor, a second Claude session). Two concrete costs we measured:
- A supervising process that shows
detailas status displays the session's own instructions as if they were a report. When that supervisor is itself an LLM call, it pays tokens to read a brief it already has. blockedcannot be distinguished from "idle, waiting for input", so every finished-but-not-exited session reads as blocked. An external watcher either raises false alarms or has to ignore the field entirely.
What would help
- Document which fields in
state.jsonare harness-owned and which a session may write. If they are all harness-owned, saying so is enough — the current situation invites sessions to write values that are silently reverted. - Distinguish "idle, awaiting input" from "blocked on a question the session asked". The
needsfield already carries the question when there is one, and it is written by the session and not overwritten, so the pair is nearly there. - Either omit
detailwhen there is no progress to report, or keep it distinct fromintentso a consumer can tell them apart without a prefix comparison.
Workaround
Read state from claude agents --json and treat state.json as a source for needs, output and intent only; suppress detail when it is a prefix of intent.