| ... | @@ -383,13 +383,49 @@ describe("deriveAgentPanelModel", () => { | ... | @@ -383,13 +383,49 @@ describe("deriveAgentPanelModel", () => { |
| 383 | it("counts idle deliberately and waiting as active", () => { | 383 | it("counts idle deliberately and waiting as active", () => { |
| 384 | const model = deriveAgentPanelModel({ agents: roster }); | 384 | const model = deriveAgentPanelModel({ agents: roster }); |
| 385 | expect(model.idleCount).toBe(1); | 385 | expect(model.idleCount).toBe(1); |
| 386 | // wf-1 coordinator + member 1 running. | 386 | // Member 1 is running; the wf-1 coordinator is a container, not a worker. |
| 387 | expect(model.runningCount).toBeGreaterThanOrEqual(1); | 387 | expect(model.runningCount).toBe(1); |
| | 388 | // Every agent lands in exactly one bucket, except coordinators that stand |
| | 389 | // in for their members. |
| 388 | expect(model.idleCount + model.runningCount + model.waitingCount + model.settledCount).toBe( | 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 | it("keeps direct spawns in first-seen order as their activity changes", () => { | 429 | it("keeps direct spawns in first-seen order as their activity changes", () => { |
| 394 | const directRoster = fold([ | 430 | const directRoster = fold([ |
| 395 | activity("task.started", { taskId: "direct-a", title: "First" }, "2026-08-01T11:00:00.000Z"), | 431 | activity("task.started", { taskId: "direct-a", title: "First" }, "2026-08-01T11:00:00.000Z"), |