Agents View shows wrong status (needs-input with no question, false red/failed, stale green) — overloaded state + stale exit-cause artifacts
Summary
Separate symptom from #72221 / #72233 / #73332 (daemon restart churn + worker reaping), but likely rooted in the same backend data: the Agents View status dot / category shown for a session is frequently wrong or ambiguous — a session can show "needs input" (yellow) with nothing actually pending, can render red/failed for a job that's actually fine, or can look green/active while stalled. Investigated with read-only forensics against local job state files, this machine's Electron app logs, and the daemon log — the underlying state.json/timeline.jsonl data itself is genuinely ambiguous in the ways described below, so this isn't purely a rendering bug.
Environment: macOS (Apple Silicon), Claude Code 2.1.199, Agents View / background-job (FleetView) sessions.
Evidence
1. state:"blocked" ("needs input") is overloaded and frequently has no real question in it.
Across sampled timeline.jsonl files, only 4 literal state values exist anywhere: done, blocked, working, failed — there is no distinct value for "genuinely waiting on a user reply" vs. "posted a status update" vs. "just logged the user's last chat message." Concrete entries with state:"blocked" and plainly non-question detail text:
{"at":"2026-06-30T18:28:54.167Z","state":"blocked","detail":"Wait however I just noticed that my API key has no more funds maybe that is the reason..."}
{"at":"2026-06-29T18:43:44.382Z","state":"blocked","detail":"I want you to improve all the AI racers and make them brutally aggressive..."}
{"at":"2026-06-29T19:09:53.889Z","state":"blocked","detail":"Continue","text":""}
The first two are literally the user's own chat messages being stored as the "blocked" detail — not a question the agent is asking. If the "needs input" tab in Agents View is driven by state === "blocked", users will see sessions listed there that aren't waiting on anything.
2. state:"failed" is attached to benign continuations, not actual failures.
At least 5 timestamped instances across 3 different jobs (2026-06-30 to 2026-07-02) where detail is literally "Continue" / "continue" (a benign continuation, sometimes even the user just saying "continue"):
{"at":"2026-06-30T01:12:33.800Z","state":"failed","detail":"Continue","text":""}
{"at":"2026-07-02T04:01:09.709Z","state":"failed","detail":"Continue","text":""}
{"at":"2026-07-02T04:11:10.981Z","state":"failed","detail":"Continue","text":""}
{"at":"2026-07-02T18:17:34.138Z","state":"failed","detail":"continue","text":""}
{"at":"2026-07-02T08:45:12.354Z","state":"failed","detail":"continue look you just stopped as well for no reason","text":""}
3. Stale exit-cause artifact files outlive legitimate completion, plausibly driving false "red"/failed rendering for jobs that actually finished fine.
Two separate job directories show a worker reaped by the orphan-watchdog (exit-cause: "ptyhost_orphan_watchdog", same mechanism documented in #73332), followed hours later by the job legitimately reaching state:"done" with a real result — but the exit-cause file is never cleared:
- Job
6e4c3443:exit-causewritten 2026-07-02T11:02:02Z;state.jsonreaches"state":"done"(PR marked ready for review) at 2026-07-02T16:48:33Z — 5h46m later, stale file still present. - Job
cdb62222:exit-causewritten 2026-06-30T21:49:10Z;state.jsonreaches"state":"done"at 2026-07-02T01:40:48Z.
If any part of the status derivation reads that leftover exit-cause file (or a cached value derived from it) instead of solely the current state.json.state, a genuinely-completed job would render as failed/red.
4. Some job directories have a worker reaped before any state.json was ever written — no terminal state exists for a UI to read.
Job dirs 89707091 and a0fc0aa6 contain only an exit-cause: "ptyhost_orphan_watchdog" file (2026-07-02T11:02, 2026-07-01T12:31) with empty tmp/ and zero timeline entries. If Agents View enumerates job directories for its session list and a job has no state file, there's nothing telling it the job is dead — consistent with a plausible default-to-active/green fallback.
5. Live-caught internal field contradiction.
While this investigation was running, this session's own state.json was read mid-flight showing "state":"done" while simultaneously "tempo":"active" and "inFlight":{"tasks":1,...} — contradictory fields in the same file at the same instant (transient — the field later correctly updated to "working" as the session continued). Flagging as evidence the state machine can be internally inconsistent even momentarily, which is enough to produce a wrong instantaneous UI read.
6. Electron-app-level: stale local session cache outlives server-side session lifetime, confirmed via repeated 404s after long focus gaps.
~/Library/Logs/Claude/main.log / claude.ai-web.log show a repeated pattern (5+ occurrences spanning 2026-06-28 to 2026-07-02) of long focus gaps (5–16 hours, consistent with sleep/idle) followed immediately by the renderer retrying a queued mutation against session IDs the server already discarded:
23:26:02 [error] [REACT_QUERY_CLIENT] QueryClient error: {"type":"not_found_error","statusCode":404,
"endpoint":"/v1/code/sessions/session_01EGfVyCAunfEfELmym9URUp/mark_read", ...
"message":"Session session_01EGfVyCAunfEfELmym9URUp not found"}
The same two session IDs recur across at least 8 separate resume events from 2026-06-30 through 2026-07-02 — the client has been carrying stale/dead session references in its cache for 24+ hours, re-firing failed mutations on every refocus instead of evicting them once the server 404s. Direct evidence of local UI state diverging from server truth.
Why this is likely one bug family, not unrelated noise
Between #72221 / #72233 / #73332 and this issue: the backend state/tempo/exit-cause vocabulary is too coarse to distinguish "genuinely done," "failed," "benign pause," and "actually waiting on the user" — and leftover artifacts from the already-documented worker-reaping bug (exit-cause files, missing state.json) aren't cleaned up when a job recovers. A status-rendering UI reading this data will unavoidably show the wrong dot/category sometimes, independent of whatever specific frontend logic consumes it.
Ask
- Add a distinct state value (or explicit boolean flag) for "genuinely awaiting a user reply" vs. "posted an update, still working" — stop overloading
blockedfor both, and stop storing the user's own chat text as a "blocked" detail. - Don't mark benign continuations (
detail:"Continue"/"continue") asstate:"failed". - Clear/supersede the
exit-causeartifact once a job reaches a legitimate terminaldonestate, and make sure status derivation doesn't key off a stale leftoverexit-causefile. - Guarantee a job always gets a
state.jsonwritten before its process can be reaped, so there's always a terminal state to render instead of an implicit fallback. - On the Electron/renderer side, evict session IDs from the local cache once the server returns 404 for them, instead of retrying indefinitely across refocus cycles.
Related: #72221, #72233, #73332 (same machine, same investigation thread). Happy to share raw state.json/timeline.jsonl/log excerpts on request.