diff --git a/src/commands/flows.test.ts b/src/commands/flows.test.ts index f51878ae62f9..d0db45d3ac93 100644 --- a/src/commands/flows.test.ts +++ b/src/commands/flows.test.ts @@ -235,6 +235,62 @@ describe("flows commands", () => { }); }); + it.each([ + { + status: "waiting", + pressure: "0 active · 1 waiting · 0 blocked · 0 cancel-requested · 1 total", + }, + { + status: "failed", + pressure: "0 active · 0 blocked · 1 issues · 0 cancel-requested · 1 total", + }, + { + status: "lost", + pressure: "0 active · 0 blocked · 1 issues · 0 cancel-requested · 1 total", + }, + { + status: "succeeded", + pressure: "0 active · 0 blocked · 0 cancel-requested · 1 total", + }, + { + status: "cancelled", + pressure: "0 active · 0 blocked · 0 cancel-requested · 1 total", + }, + ] as const)( + "accounts for filtered $status flows in TaskFlow pressure", + async ({ status, pressure }) => { + await withTaskFlowCommandStateDir(async () => { + createManagedTaskFlow({ + ownerKey: "agent:main:main", + controllerId: `tests/flows-command-${status}`, + goal: `Inspect ${status} work`, + status, + }); + createManagedTaskFlow({ + ownerKey: "agent:main:main", + controllerId: "tests/flows-command-unrelated", + goal: "Unrelated running work", + status: "running", + }); + + const runtime = createRuntime(); + await flowsListCommand({ status }, runtime); + + expect(vi.mocked(runtime.log).mock.calls.map(([line]) => String(line))).toContain( + `TaskFlow pressure: ${pressure}`, + ); + + const jsonRuntime = createRuntime(); + await flowsListCommand({ json: true, status }, jsonRuntime); + expect(vi.mocked(jsonRuntime.writeJson).mock.calls[0]?.[0]).toMatchObject({ + count: 1, + status, + flows: [expect.objectContaining({ status })], + }); + }); + }, + ); + it("keeps truncated text rows UTF-16 well-formed", async () => { await withTaskFlowCommandStateDir(async () => { createManagedTaskFlow({ diff --git a/src/commands/flows.ts b/src/commands/flows.ts index 64a45dc46fe6..0b04e2467a37 100644 --- a/src/commands/flows.ts +++ b/src/commands/flows.ts @@ -122,14 +122,19 @@ function formatFlowRows(flows: TaskFlowRecord[], rich: boolean) { } function formatFlowListSummary(flows: TaskFlowRecord[]) { - const active = flows.filter( - (flow) => flow.status === "queued" || flow.status === "running", - ).length; - const blocked = flows.filter((flow) => flow.status === "blocked").length; - const cancelRequested = flows.filter( - (flow) => flow.cancelRequestedAt != null && !isTerminalFlowStatus(flow.status), - ).length; - return `${active} active · ${blocked} blocked · ${cancelRequested} cancel-requested · ${flows.length} total`; + const counts = { active: 0, waiting: 0, blocked: 0, issues: 0, cancelRequested: 0 }; + for (const flow of flows) { + counts.active += Number(flow.status === "queued" || flow.status === "running"); + counts.waiting += Number(flow.status === "waiting"); + counts.blocked += Number(flow.status === "blocked"); + counts.issues += Number(flow.status === "failed" || flow.status === "lost"); + counts.cancelRequested += Number( + flow.cancelRequestedAt != null && !isTerminalFlowStatus(flow.status), + ); + } + const waiting = counts.waiting ? ` · ${counts.waiting} waiting` : ""; + const issues = counts.issues ? ` · ${counts.issues} issues` : ""; + return `${counts.active} active${waiting} · ${counts.blocked} blocked${issues} · ${counts.cancelRequested} cancel-requested · ${flows.length} total`; } function summarizeWait(flow: TaskFlowRecord): string {