| ... | ... | @@ -383,13 +383,49 @@ describe("deriveAgentPanelModel", () => { |
| 383 | 383 | it("counts idle deliberately and waiting as active", () => { |
| 384 | 384 | const model = deriveAgentPanelModel({ agents: roster }); |
| 385 | 385 | expect(model.idleCount).toBe(1); |
| 386 | | // wf-1 coordinator + member 1 running. |
| 387 | | expect(model.runningCount).toBeGreaterThanOrEqual(1); |
| 386 | // Member 1 is running; the wf-1 coordinator is a container, not a worker. |
| 387 | expect(model.runningCount).toBe(1); |
| 388 | // Every agent lands in exactly one bucket, except coordinators that stand |
| 389 | // in for their members. |
| 388 | 390 | expect(model.idleCount + model.runningCount + model.waitingCount + model.settledCount).toBe( |
| 389 | | roster.length, |
| 391 | roster.length - 1, |
| 390 | 392 | ); |
| 391 | 393 | }); |
| 392 | 394 | |
| 395 | it("omits a workflow coordinator from the working-agent count", () => { |
| 396 | const model = deriveAgentPanelModel({ agents: roster }); |
| 397 | // One member still running plus one idle direct spawn. The coordinator |
| 398 | // reports running for the whole workflow and must not inflate the banner. |
| 399 | expect(model.liveCount).toBe(1); |
| 400 | }); |
| 401 | |
| 402 | it("omits a finished workflow coordinator from the settled count", () => { |
| 403 | const finished = fold([ |
| 404 | activity("task.started", { taskId: "wf-2", taskType: "local_workflow", title: "sweep" }), |
| 405 | activity("task.progress", { |
| 406 | taskId: "wf-2:wf:0", |
| 407 | title: "sweep:a", |
| 408 | status: "completed", |
| 409 | parentAgentId: "wf-2", |
| 410 | agentIndex: 0, |
| 411 | phaseIndex: 0, |
| 412 | }), |
| 413 | activity("task.completed", { |
| 414 | taskId: "wf-2:wf:0", |
| 415 | status: "completed", |
| 416 | parentAgentId: "wf-2", |
| 417 | }), |
| 418 | activity("task.completed", { taskId: "wf-2", status: "completed" }), |
| 419 | ]); |
| 420 | |
| 421 | const model = deriveAgentPanelModel({ agents: finished }); |
| 422 | |
| 423 | // Only the member settled. The coordinator stands in for it, so counting |
| 424 | // both would report two finished agents where one ran. |
| 425 | expect(model.settledCount).toBe(1); |
| 426 | expect(model.liveCount).toBe(0); |
| 427 | }); |
| 428 | |
| 393 | 429 | it("keeps direct spawns in first-seen order as their activity changes", () => { |
| 394 | 430 | const directRoster = fold([ |
| 395 | 431 | activity("task.started", { taskId: "direct-a", title: "First" }, "2026-08-01T11:00:00.000Z"), |