fix: org chart resume button + name collision - #116
Merged
Conversation
nox-0x
approved these changes
Apr 10, 2026
nox-0x
left a comment
Collaborator
There was a problem hiding this comment.
Solid fix. Name collision by keying nodes on claudeSessionId (unique) instead of lowercased name (collision-prone) is exactly right — the chooseCanonical() tiebreak logic (running > exited, newest persistedAt wins) is clean and well-tested. Resume button wiring is correct. Tests are thorough including the startup sweep for crashed-run cleanup. No blocking issues.
3 tasks
aterrylu
marked this pull request as ready for review
April 10, 2026 11:13
Two fixes to the org chart sidebar pane: 1. Resume button on stopped agent cards — hover overlay now shows a green debug-start button alongside the trash button when the agent is not running. Wires to the existing resumeSession store action with isAutonomosAgent: true, which calls POST /api/sessions/:id/resume (added in #110) and restores the full spawn config (template, manager, cwd, autonomousMode) from persisted state. 2. Name collision — killing an agent and respawning with the same name no longer shows both as running. buildOrgChart() previously keyed its node Map by lowercased name, so the second session overwrote the first and the same node object was pushed into the parent's children array multiple times. Now keys nodes by claudeSessionId (unique), groups sessions by name, and picks one canonical session per name via chooseCanonical(): prefer running sessions, break ties by newest persistedAt. OrgNode interface gains status + claudeSessionId fields so the frontend can distinguish live/exited without a name-based lookup. Also fixes a parallel client-side collision hazard: useAgentStatusByName was keyed by lowercased name too, reintroducing the same bug for activity state (currentTool, working status). Renamed to useAgentStatusById and keyed by claudeSessionId. Adds 5 regression tests covering collision scenarios. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
aterrylu
force-pushed
the
terry/orgchart-fixes
branch
from
April 10, 2026 11:28
1947dbb to
e577117
Compare
aterrylu
enabled auto-merge (squash)
April 10, 2026 11:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two bug fixes for the org chart sidebar pane, plus a parallel client-side fix caught during polish.
Bug 1: Resume button on stopped agent cards
Stopped/exited agents in the org chart only had a trash (remove) button. Now the hover overlay shows a green debug-start (Resume) button beside the trash, wired to the existing
resumeSessionstore action (isAutonomosAgent: true), which callsPOST /api/sessions/:id/resume(from #110) to restore the full spawn config from persisted state.Only shown when
!isRunning— running agents still get just the kill/remove button.Bug 2: Name collision — both agents show as "online"
Killing an agent and spawning another with the same name made both appear running in the org chart. Two root causes in
buildOrgChart():Mapwas keyed by lowercased name — the second session silently overwrote the first.nodeobject intoparent.childrenonce for each duplicate, so a child could appear multiple times under one parent.Fix: Nodes are now keyed by
claudeSessionId(unique). Sessions are first grouped by name, thenchooseCanonical()picks one canonical session per name:persistedAt(most recently intended instance)statusfield as "running" (pre-feat: persist exited sessions for resume #110 sessions)OrgNodeinterface gainsstatus+claudeSessionIdso the frontend can distinguish live/exited directly from the chart data, no name lookup needed.Bug 2.5 (caught by polish review): Client-side collision hazard
useAgentStatusByNameinHierarchyPanel.tsxwas keyed by lowercased name too — reintroducing the exact collision class the server fix eliminated, for activity state (currentTool, working/needs_input, etc.). Renamed touseAgentStatusByIdand keyed byclaudeSessionId.AgentCardnow looks up activity state bynode.claudeSessionId.Approach — canonical selection (design call)
For duplicate names, we pick one canonical session and hide the duplicates from the org chart entirely (Option A from the design discussion with @aterrylu):
persistedAtwins among the preferred poolA follow-up issue tracks adding a matching hide-exited toggle to the org chart itself — it has gnarly orphaned-children cases when an exited parent has running descendants, and was deemed out of scope for this PR.
Diagram
flowchart TB subgraph Before["Before (buggy)"] B1[sessions.json] --> B2["Map<name, Node><br/>collision loses data"] B2 --> B3["parent.children.push(node)<br/>duplicates under parent"] end subgraph After["After"] A1[sessions.json] --> A2["Group by name<br/>Map<name, Session[]>"] A2 --> A3["chooseCanonical()<br/>running wins, newest wins"] A3 --> A4["Map<claudeSessionId, Node><br/>unique identity"] A4 --> A5["Tree wired by<br/>canonical sessions only"] endTest plan
orgChart.test.tscovering: running-wins, newest-wins tiebreak, all-exited fallback, no-duplicate-under-parent, schema shape. Includes a startup sweep that removes staleorgchart-test-*entries to survive crashed test runs.make check— 68/68 tests pass, Biome + TSC clean~/.autonomos/sessions.jsonwith prod (tracked as a separate follow-up), making localmake devQA unsafe while prod is up. Please eyeball on the live dashboard after pulling the branch:Follow-ups (separate PRs, not this one)
~/.autonomos/is shared between prod :3100 and dev :3101; dev's resume sweep nearly cloned the running fleet during my QA attempt. Will ship as a stacked PR immediately after this one.handleRemovesilent failure — confirm dialog stays open with no visible feedback if the backend rejects the remove. Pre-existing in the sidebar path too, out of scope here.isProductiondetection viaexistsSync(dashboardDist)— breaks whentsc --buildwrites todist/withoutindex.html. Needs an explicit flag orNODE_ENVcheck.tsx --env-file=X watch src/index.tsargument ordering — tsx parseswatchas the script name. Works in main by accident. Reorder totsx watch --env-file=X src/index.ts.Files changed
packages/server/src/orgChart.ts—buildOrgChart()rewrite,chooseCanonical(),OrgNodeschemapackages/server/src/__tests__/orgChart.test.ts— new, 5 regression testspackages/dashboard/src/components/HierarchyPanel.tsx— Resume button,OrgNodetype,useAgentStatusByIdrename + ID key,targetSessionlookuppackages/dashboard/src/components/Codicon.tsx—debug-starticon added🤖 Generated with Claude Code