Background session state.json: detail echoes the brief, state reads blocked when idle, and session writes are reverted

Status Open
Reported on v2.1.233
Maintainer reply None cached
Activity 0 comments · opened Aug 19, 2026

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 detail as 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.
  • blocked cannot 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.json are 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 needs field 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 detail when there is no progress to report, or keep it distinct from intent so 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.

View original on GitHub ↗