[BUG] Desktop sidebar Group by State: row dot and bucket derive from disjoint inputs, so Completed sessions render the pulsing Working dot
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
In the Claude Desktop Code tab sidebar with Group by → State, the per-row status dot contradicts the bucket the row is filed under. Sessions sitting in Completed render the filled, pulsing dot that means "working".
This is not a refresh lag. The bucket and the dot are computed by different functions that share no inputs at all, and nothing reconciles them.
Bucketing enters at kC in assets/v1/shared-15-Chp4bBSH.js, which splits on whether the row has a server-side session object:
function kC(e,t,s,n,a){const o=n.has(e.id),r=a.get(e.id),i=void 0!==r&&r>=Date.parse(e.timestamp)||wc(e._originalSession,e.timestamp);if(e._originalSession){const n=Sc(e._originalSession,o,t.has(e._originalSession.id),s.has(e._originalSession.id))??"working";return"blocked"===n&&i?s.has(e._originalSession.id)?"review":"done":n}return e.isArchived?"done":o?"working":i||"requires_action"!==e.sessionStatus?"running"===e.sessionStatus||"pending"===e.sessionStatus?"working":s.has(e.id)?"review":e.createdAt&&e.createdAt!==e.timestamp?"done":"working":"blocked"}
Local (non-cloud) sessions have no _originalSession, so they take the second branch. Its liveness proxies are membership in respondingIds (o) and a sessionStatus of running/pending. If neither holds, it falls to:
e.createdAt && e.createdAt !== e.timestamp ? "done" : "working"
That test means "has been touched since it was created", which is true of every session that has ever run more than one turn. So a local session buckets as Completed whenever it is not currently in respondingIds, regardless of whether it is running.
The dot comes from fI in assets/v1/shared-10-BhF4qSrx.js:
function fI(e){return e.isArchived?e.hasCompleted&&e.isUnread?"ready":"idle":e.hasError?"error":"awaiting"===e.liveStatus?"awaiting":"running"===e.liveStatus?"running":e.hasCompleted&&e.isUnread?"ready":"idle"}
The pulse is "running"===e.liveStatus. Neither bucketing branch reads liveStatus at any point. The cloud branch (Sc, which is pI in shared-10) has the same gap, and additionally uses "done" as its terminal fallback:
function pI(e,t=!1,n=!1,s=!1){return"deleted"===e.session_status?null:"archived"===e.session_status?"done":t?"working":"requires_action"===e.session_status?"blocked":oI(e.session_status,e.worker_status)?"working":iI(e.post_turn_summary?.status_category)?"blocked":n?"done":s?"review":e.created_at===e.updated_at?"working":"done"}
function oI(e,t){return"pending"===e||"running"===e&&"idle"!==t}
So on both paths, liveStatus drives the pulse and nothing in bucketing consults it, while Completed doubles as the fallback for anything unclassifiable.
What Should Happen?
The row indicator and the bucket should derive from one state, or at minimum the bucket should consult liveStatus. A session that is actively working should not be filed under Completed, and a row filed under Completed should not render the working dot.
Suggested fixes, any of:
- Have both bucketing branches consult
liveStatusbefore any fallback, so a session the UI is currently drawing as running cannot bucket asdone. - Derive the row dot from the bucket result rather than from
fIindependently, so the two cannot disagree by construction. - Replace the local branch's
createdAt !== timestamptest. "Has been touched since creation" is not evidence of completion, and it is true of nearly every session. - Stop using
"done"as the terminal fallback on the cloud branch. An unclassifiable session is not a completed one, and Completed currently doubles as the unknown bucket.
Error Messages/Logs
N/A. No error is emitted. The two derivations each succeed and return contradictory results.
Steps to Reproduce
- Open the Claude Desktop app,
Codetab. - In the sessions sidebar, set Group by → State.
- Have several local (non-cloud) sessions in git worktrees, at least one of which has run more than one turn and is actively working.
- Observe rows filed under Completed rendering the filled, pulsing dot, alongside rows under Working rendering the same dot.
Supporting measurement, from disk, no UI needed:
- Scan the local session records under
~/Library/Application Support/Claude/claude-code-sessions/<space>/<id>/local_*.json. - None of them carry
session_status,worker_status,sessionStatusorworkerStatus. I scanned 515 records on this machine and got 0 hits for all four. The keys present aresessionId,cliSessionId,cwd,worktreePath,branch,createdAt,lastActivityAt,model,isArchived,title,prs,classifierSummaryEnabled, and similar. - With
sessionStatusundefined, the local branch ofkCcannot reach its"running"/"pending"working case. It resolves viarespondingIdsor falls through tocreatedAt !== timestamp ? "done" : "working", which sends any multi-turn session to Completed.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
N/A
Claude Code Version
2.1.220 (Claude Code), inside Claude Desktop for macOS 1.30096.1
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
Screenshot of the sidebar under Group by → State, showing Needs input, Working and Completed buckets. The filled dots are the pulsing working indicator, and they appear on rows in Completed as well as Working.
Related, same class of defect on a different surface:
- #64036, FleetView background agents show actively-working agents under Completed. That one traces to a stale text-classifier verdict in
~/.claude/jobs/<id>/state.jsonrather than to two disjoint derivations, so the mechanism differs, but the user-visible failure is identical. Open since 2026-05-30, marked stale. - #83554, Agents view buckets on state rather than tempo.
Two independent surfaces landing on "actively working, shown as Completed" suggests the bucketing model rather than either view is the thing to fix.
Caveat on evidence: the mechanism above is derived from reading the minified bundle plus the on-disk session records. I have not attached a runtime capture proving that a specific Completed row has liveStatus === "running". The two function bodies and the absent session_status field are the evidence offered.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗