You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The project sidebar lists every session whose metadata file exists on disk, with no filter or grouping by terminal state. Sessions that the user has explicitly killed, sessions that auto-terminated on pr_merged, and sessions in cleanup/stuck/detecting after lifecycle resolution all stay in the sidebar indefinitely, sandwiched in with live sessions — until something deletes their JSON file (effectively only ao session cleanup, and only for sessions that meet a lifecycle-specific predicate). On a 1-2 week old machine you end up with 20+ terminated rows for every 1 live row.
Source: Live AO session, in-chat triage with Claude orchestrator | Reported by:@Itrytoohard Analyzed against:5897b4e8 (AgentWrapper/agent-orchestrator origin/main as of 2026-06-08) Environment: macOS 14.6 arm64, Node v22.11.0, tmux runtime Confidence:High — traced API + dashboard rendering, reproduced with raw /api/sessions response.
Reproduction
Run AO for a week with sessions that come and go (spawn workers, merge PRs, ao session kill, get auto-terminated on merge, etc.).
Open the dashboard at http://localhost:3000.
Look at the left-rail project sidebar.
Expected: sidebar shows live/actionable sessions; terminated sessions are either hidden, collapsed under a foldout, or auto-aged-out.
Actual: every session metadata file gets rendered as a sidebar row, regardless of canonical state. Live rows mixed in with dead ones.
Concrete data from this user's machine right now (GET /api/sessions):
count: 23 sessions returned
breakdown:
ao-1 .. ao-5 killed/terminated (5 rows, all manually_killed days ago)
ao-6 killed/detecting (1 row)
ao-7 .. ao-8 cleanup/terminated (2 rows, pr_merged auto-terminated)
ao-9 killed/detecting
ao-10 .. ao-14 killed/terminated (5 rows, all from a single `ao stop` at 18:05 today)
ao-2-1 killed/detecting
ao-2-2 cleanup/terminated (pr_merged from another agent days ago)
ao-2-3 .. ao-2-8 killed/{terminated,detecting} (6 rows)
ao-2-9 cleanup/terminated (just merged its PR)
ao-2-orchestrator working/working (← the only LIVE row, hidden among 22 dead ones)
22 of 23 rows are not-live. The single live row (ao-2-orchestrator) is visually indistinguishable from the rest at the sidebar level.
Root Cause
The dashboard reads session listings from /api/sessions, which the running web server backs by enumerating every JSON file under ~/.agent-orchestrator/projects/<projectId>/sessions/*.json for every project in ~/.agent-orchestrator/config.yaml — independent of whether the lifecycle worker is actually polling that project. The sessions array returned has no excludeTerminal flag and the sidebar consumer (packages/web/src/components/ProjectSidebar.tsx, sourced from /api/sessions per the file comment) does not filter by lifecycle.session.state or legacy status. Terminal-state sessions are first-class items in the response.
There is no observable cleanup pressure: ao session cleanup only deletes JSON files for sessions whose lifecycle state matches a specific predicate (e.g., pr_mergedwith cleanup-eligible runtime state), and is opt-in. Killed sessions, stuck sessions, sessions with detecting / runtime_lost, and partially-terminated sessions all linger.
Suggested Fix
Three options sized small → large; pick the smallest the team is comfortable with:
Sidebar-side filter with a toggle. In ProjectSidebar.tsx, default-hide sessions whose canonical lifecycle.session.state is terminated or done, AND whose lifecycle terminatedAt is older than e.g. 24 h. Surface a "Show terminated (N)" disclosure / toggle for the remainder so users can still reach them. No API changes. Lowest blast radius. Recommended starting point.
Grouped sidebar sections. Same data, but split into "Active", "Needs attention" (stuck, needs_input, detecting), and "Recent (terminated, last 24h)" sections, with older terminated sessions collapsed under a "History" foldout. Pattern mirrors what the kanban already does for Done/Terminated per #1588.
API-side scope flag. Add ?scope=active|all to /api/sessions so the sidebar can ask for the live subset and the kanban / history views can ask for everything. Reduces payload size as a bonus; today every dashboard refresh ships all 23 (or 230) session blobs.
Bonus cleanup hygiene: a periodic background trim that deletes session JSONs whose terminatedAt is older than, say, 30 days AND whose cleanup flag has fired — would prevent the on-disk pile-up that drives the sidebar bloat in the first place.
Impact
Sidebar is unusable as a "what's running right now" view. Once a machine accumulates more than a handful of historical sessions, the live one is needle-in-a-haystack.
Click confusion. Users click "terminated" rows expecting them to be openable; some are restorable, some aren't, with no consistent visual cue.
Cross-project clutter. Because /api/sessions aggregates across all projects in the global config — even projects the daemon isn't actively polling — terminated rows from projects you stopped weeks ago still appear (e.g. user has safesplit_3f4dcfd77a rows surfacing despite the lifecycle worker not polling that project).
Lifecycle-bug amplifier. Sessions that hit #19's auto-terminate-on-PR-merge loop and#1933's stuck-never-promoted-to-terminated state both linger forever in the sidebar even after the underlying issue is "resolved" by the user.
Related
#1875 — opposite framing: "No sessions shown" when sessions exist but ARE hidden by done/killed (suggests some filter exists somewhere, just not in the sidebar). Reconcile fix here against that one.
#1588 — show session ID in Done/Terminated section on dashboard (so a Done/Terminated grouping exists in the kanban; sidebar lacks the same pattern).
#19 — auto-terminate-on-PR-merge with no opt-out. Every merged-PR session adds a fresh terminated row to this sidebar.
#1933 — stuck sessions don't auto-promote to terminated and linger. Compounds the clutter.
#1524 — ao session cleanup leaves worktrees on disk for sessions already in terminal lifecycle state. Same family of "lifecycle says dead, dashboard still shows it" problems.
Bug
The project sidebar lists every session whose metadata file exists on disk, with no filter or grouping by terminal state. Sessions that the user has explicitly killed, sessions that auto-terminated on
pr_merged, and sessions incleanup/stuck/detectingafter lifecycle resolution all stay in the sidebar indefinitely, sandwiched in with live sessions — until something deletes their JSON file (effectively onlyao session cleanup, and only for sessions that meet a lifecycle-specific predicate). On a 1-2 week old machine you end up with 20+ terminated rows for every 1 live row.Source: Live AO session, in-chat triage with Claude orchestrator | Reported by: @Itrytoohard
Analyzed against:
5897b4e8(AgentWrapper/agent-orchestrator origin/main as of 2026-06-08)Environment: macOS 14.6 arm64, Node v22.11.0, tmux runtime
Confidence: High — traced API + dashboard rendering, reproduced with raw
/api/sessionsresponse.Reproduction
ao session kill, get auto-terminated on merge, etc.).http://localhost:3000.Concrete data from this user's machine right now (
GET /api/sessions):22 of 23 rows are not-live. The single live row (
ao-2-orchestrator) is visually indistinguishable from the rest at the sidebar level.Root Cause
The dashboard reads session listings from
/api/sessions, which the running web server backs by enumerating every JSON file under~/.agent-orchestrator/projects/<projectId>/sessions/*.jsonfor every project in~/.agent-orchestrator/config.yaml— independent of whether the lifecycle worker is actually polling that project. The sessions array returned has noexcludeTerminalflag and the sidebar consumer (packages/web/src/components/ProjectSidebar.tsx, sourced from/api/sessionsper the file comment) does not filter bylifecycle.session.stateor legacystatus. Terminal-state sessions are first-class items in the response.There is no observable cleanup pressure:
ao session cleanuponly deletes JSON files for sessions whose lifecycle state matches a specific predicate (e.g.,pr_mergedwith cleanup-eligible runtime state), and is opt-in. Killed sessions, stuck sessions, sessions withdetecting / runtime_lost, and partially-terminated sessions all linger.Suggested Fix
Three options sized small → large; pick the smallest the team is comfortable with:
Sidebar-side filter with a toggle. In
ProjectSidebar.tsx, default-hide sessions whose canonicallifecycle.session.stateisterminatedordone, AND whose lifecycleterminatedAtis older than e.g. 24 h. Surface a "Show terminated (N)" disclosure / toggle for the remainder so users can still reach them. No API changes. Lowest blast radius. Recommended starting point.Grouped sidebar sections. Same data, but split into "Active", "Needs attention" (
stuck,needs_input,detecting), and "Recent (terminated, last 24h)" sections, with older terminated sessions collapsed under a "History" foldout. Pattern mirrors what the kanban already does for Done/Terminated per #1588.API-side scope flag. Add
?scope=active|allto/api/sessionsso the sidebar can ask for the live subset and the kanban / history views can ask for everything. Reduces payload size as a bonus; today every dashboard refresh ships all 23 (or 230) session blobs.Bonus cleanup hygiene: a periodic background trim that deletes session JSONs whose
terminatedAtis older than, say, 30 days AND whosecleanupflag has fired — would prevent the on-disk pile-up that drives the sidebar bloat in the first place.Impact
/api/sessionsaggregates across all projects in the global config — even projects the daemon isn't actively polling — terminated rows from projects you stopped weeks ago still appear (e.g. user hassafesplit_3f4dcfd77arows surfacing despite the lifecycle worker not polling that project).Related
stucksessions don't auto-promote toterminatedand linger. Compounds the clutter.ao session cleanupleaves worktrees on disk for sessions already in terminal lifecycle state. Same family of "lifecycle says dead, dashboard still shows it" problems.