From 5a10e406795a429092506611caf59e8ea4cf39e9 Mon Sep 17 00:00:00 2001 From: fujibee Date: Thu, 16 Jul 2026 02:34:45 -0700 Subject: [PATCH] fix(app): give team-status-rail its own gray for zero-pane teams #406 made "unknown" (a pane whose agent type classify() doesn't recognize) render green like idle. But aggregateTeamStatus also returned "unknown" for a team with zero panes at all, so a team nobody has started an agent in looked identical to one full of healthy unrecognized agents. aggregateTeamStatus now returns a distinct "empty" for that case, which keeps the muted-gray base color. --- app/src/App.css | 1 + app/src/agentStatus.test.ts | 8 ++++++-- app/src/agentStatus.ts | 11 ++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/src/App.css b/app/src/App.css index ba6af2a7..c1dc02dc 100644 --- a/app/src/App.css +++ b/app/src/App.css @@ -664,6 +664,7 @@ body.resizing-row { .team-status-dot.status-working { background: #e0af68; } .team-status-dot.status-idle { background: var(--green); } .team-status-dot.status-unknown { background: var(--green); } +.team-status-dot.status-empty { background: var(--muted); } .collapsed-team-status { width: 28px; padding: 2px; diff --git a/app/src/agentStatus.test.ts b/app/src/agentStatus.test.ts index 9f898c67..fde4df9f 100644 --- a/app/src/agentStatus.test.ts +++ b/app/src/agentStatus.test.ts @@ -24,8 +24,12 @@ describe("applyStateChange", () => { }); describe("aggregateTeamStatus", () => { - it("returns unknown for an empty set", () => { - expect(aggregateTeamStatus([])).toBe("unknown"); + it("returns empty for a team with no panes", () => { + expect(aggregateTeamStatus([])).toBe("empty"); + }); + + it("returns unknown for a pane whose agent type isn't recognized", () => { + expect(aggregateTeamStatus([status("unknown")])).toBe("unknown"); }); it("lets one blocked pane beat every other state", () => { diff --git a/app/src/agentStatus.ts b/app/src/agentStatus.ts index d8252a98..ade1b7a8 100644 --- a/app/src/agentStatus.ts +++ b/app/src/agentStatus.ts @@ -1,13 +1,22 @@ export type RawState = "idle" | "working" | "blocked" | "unknown"; export type PaneStatus = { state: RawState }; export type PaneStatusMap = Record; +// A team's aggregate is either one of the pane states, or "empty" — no +// agent has ever been started for that team. "unknown" (a pane exists but +// classify() doesn't recognize its agent type) is deliberately a distinct +// value from "empty" (no pane exists at all): #406 made "unknown" render +// green like idle, since an unhandled type isn't an anomaly, but a team +// nobody has started anything in isn't "idle" — it should read as inert +// gray instead of implying live, healthy agents. +export type TeamAggregateState = RawState | "empty"; export function applyStateChange(map: PaneStatusMap, paneId: string, newState: RawState): PaneStatusMap { if (map[paneId]?.state === newState) return map; return { ...map, [paneId]: { state: newState } }; } -export function aggregateTeamStatus(statuses: PaneStatus[]): RawState { +export function aggregateTeamStatus(statuses: PaneStatus[]): TeamAggregateState { + if (statuses.length === 0) return "empty"; const priority: Record = { blocked: 3, working: 2,