diff --git a/extensions/subagents/navigation.test.ts b/extensions/subagents/navigation.test.ts index dce0b42c..6e58b59f 100644 --- a/extensions/subagents/navigation.test.ts +++ b/extensions/subagents/navigation.test.ts @@ -113,7 +113,7 @@ test("subagent strip matches Workflow's bounded one-line affordance", () => { } }); -test("the metrics tail stays quiet while a run is healthy", () => { +test("a lone subagent needs no count: glyph and name carry the state", () => { const strip = new BelowEditorStripState(); const render = (status: SubagentSnapshot["status"]) => { const entry = selectSubagentStripEntry( @@ -140,14 +140,38 @@ test("the metrics tail stays quiet while a run is healthy", () => { } }; - // A routine run borrows no status colour in its tail: the coloured glyph on - // the left already carries the state, and hints recede furthest of all. + // One active subagent: the glyph and its name already say what a "1 running" + // count would repeat, and hints recede furthest of all. const running = render("running"); - assert.match(running, /1 running<\/muted>/); + assert.match(running, /sa-1/); + assert.doesNotMatch(running, /1 running/); assert.match(running, /↓ to manage<\/dim>/); - assert.doesNotMatch(running, /1 running/); - // Once settled, the one count that carries the outcome takes the colour. - assert.match(render("error"), /1 failed<\/error>/); - assert.match(render("done"), /1 done<\/success>/); + // Once settled, the glyph takes the outcome's colour. + assert.match(render("error"), /✗<\/error>/); + assert.match(render("done"), /✓<\/success>/); +}); + +test("several active subagents aggregate instead of naming just one", () => { + const entry = selectSubagentStripEntry( + [ + snapshot("sa-1", "running", Date.now() - 4_000), + snapshot("sa-2", "running", Date.now() - 2_000), + ], + 0, + ); + const widget = new SubagentStripWidget( + { requestRender() {} } as unknown as TUI, + markingTheme, + new BelowEditorStripState(), + () => entry, + ); + try { + const rendered = widget.render(400)[0]!; + assert.match(rendered, /subagents/); + assert.match(rendered, /2 running<\/muted>/); + assert.doesNotMatch(rendered, /sa-1|sa-2/); + } finally { + widget.dispose(); + } }); diff --git a/extensions/subagents/navigation.ts b/extensions/subagents/navigation.ts index becf7b45..33536ad5 100644 --- a/extensions/subagents/navigation.ts +++ b/extensions/subagents/navigation.ts @@ -106,20 +106,28 @@ export class SubagentStripWidget { const glyph = this.strip.focused ? this.theme.fg("accent", "❯") : statusGlyph(snapshot, this.theme, Date.now()); - const titleText = normalizeSubagentTitle(snapshot.title, snapshot.id); - const title = this.strip.focused - ? this.theme.bold(this.theme.fg("accent", titleText)) - : this.theme.fg("text", titleText); + // A name only means something when it names the only active subagent; with + // several, an aggregate label is honest and the counts carry the detail. + const total = counts.running + counts.done + counts.failed; + const single = total === 1; + const labelText = single + ? normalizeSubagentTitle(snapshot.title, snapshot.id) + : "subagents"; + const label = this.strip.focused + ? this.theme.bold(this.theme.fg("accent", labelText)) + : this.theme.fg("text", labelText); // The footer already shows the session model; the takeover view keeps the // per-subagent model, so the one-line strip stays title-only. - const left = ` ${glyph} ${title}`; + const left = ` ${glyph} ${label}`; // Worded counts read at a glance; the selected run's own state comes - // first so the emphasis colour always lands on the matching count. + // first so the emphasis colour always lands on the matching count. A lone + // subagent needs no count — the glyph and label already say it. const donePart = counts.done > 0 ? `${counts.done} done` : undefined; const failedPart = counts.failed > 0 ? `${counts.failed} failed` : undefined; - const activity = - counts.running > 0 + const activity = single + ? [] + : counts.running > 0 ? [`${counts.running} running`] : snapshot.status === "error" ? [failedPart, donePart] @@ -133,7 +141,9 @@ export class SubagentStripWidget { percent === undefined ? undefined : `${percent}% ctx`, ], this.strip.focused ? "enter open · ↑ back" : "↓ to manage", - snapshot.status === "running" ? undefined : statusColor(snapshot.status), + single || snapshot.status === "running" + ? undefined + : statusColor(snapshot.status), ); return [fitNavigationSides(left, right, width)]; }