From f65364759ffb697bfb5c2400f2da4eb28932f0f8 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 18 Oct 2026 02:34:42 +0200 Subject: [PATCH] swarm-ui: retrying a broadcast shows the agents that failed, even ones the filter hides --- docs/swarm/ui.md | 4 ++-- .../swarm-ui/src/pages/broadcast/BroadcastPage.tsx | 6 +++++- .../src/pages/broadcast/broadcast-types.ts | 14 ++++++++++++++ .../swarm-ui/test/broadcast-types.test.mjs | 12 ++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md index 0b4a77de..33eec06f 100644 --- a/docs/swarm/ui.md +++ b/docs/swarm/ui.md @@ -307,8 +307,8 @@ seconds to answer, and the button reads **sending…** meanwhile. The page then lists what happened to each agent. An agent that wasn't listening shows as not sent, with the reason, and the others still got the -message. **select the N that failed** ticks just those so you can send the same -message again. When every agent got the message the box clears; otherwise the +message. **select the N that failed** ticks just those, switching to **all** if the +filter hid any, so you can send the same message again. When every agent got the message the box clears; otherwise the text stays. ### Inference settings diff --git a/frontend/packages/swarm-ui/src/pages/broadcast/BroadcastPage.tsx b/frontend/packages/swarm-ui/src/pages/broadcast/BroadcastPage.tsx index fb8f3443..1db66ea3 100644 --- a/frontend/packages/swarm-ui/src/pages/broadcast/BroadcastPage.tsx +++ b/frontend/packages/swarm-ui/src/pages/broadcast/BroadcastPage.tsx @@ -25,6 +25,7 @@ import { import { TextAreaField } from "../../ui/text-area-field/TextAreaField.js"; import { failedAgents, + filterShowing, isRunning, parseResults, selectedVisible, @@ -130,7 +131,10 @@ export function BroadcastPage() { } function retryFailed(results: BroadcastResult[]) { - setTicked(failedAgents(results)); + const failed = failedAgents(results); + // Only this page's view changes; the saved choice stays as it was. + setFilterState(filterShowing(filter, all, failed)); + setTicked(failed); setSend({ status: "idle" }); } diff --git a/frontend/packages/swarm-ui/src/pages/broadcast/broadcast-types.ts b/frontend/packages/swarm-ui/src/pages/broadcast/broadcast-types.ts index 3d8b503b..99b083b7 100644 --- a/frontend/packages/swarm-ui/src/pages/broadcast/broadcast-types.ts +++ b/frontend/packages/swarm-ui/src/pages/broadcast/broadcast-types.ts @@ -38,6 +38,20 @@ export function selectedVisible( return ticked.filter((name) => shown.has(name)); } +/** + * The filter to show `names` under: `filter` itself when it shows them all, + * otherwise `all`. A resend to agents that failed must show them ticked, and + * the failures are often the agents the `running` filter hides. + */ +export function filterShowing( + filter: AgentFilter, + agents: BroadcastAgent[], + names: string[], +): AgentFilter { + const shown = new Set(visibleAgents(agents, filter).map((a) => a.name)); + return names.every((n) => shown.has(n)) ? filter : "all"; +} + /** `ticked` with `name` flipped. */ export function toggled(ticked: string[], name: string): string[] { return ticked.includes(name) diff --git a/frontend/packages/swarm-ui/test/broadcast-types.test.mjs b/frontend/packages/swarm-ui/test/broadcast-types.test.mjs index 82e01526..c2a93cca 100644 --- a/frontend/packages/swarm-ui/test/broadcast-types.test.mjs +++ b/frontend/packages/swarm-ui/test/broadcast-types.test.mjs @@ -5,6 +5,7 @@ import assert from "node:assert/strict"; import { test } from "node:test"; import { failedAgents, + filterShowing, MAX_BROADCAST, isRunning, parseResults, @@ -94,3 +95,14 @@ test("an answer that is not the agreed shape is null, not an empty list", () => test("one broadcast may name at most 64 agents", () => { assert.equal(MAX_BROADCAST, 64); }); + +test("a retry shows the failed agents even when the filter hides them", () => { + const agents = [ + { name: "a", snapshot: { running: true } }, + { name: "b", snapshot: { running: false } }, + ]; + assert.equal(filterShowing("running", agents, ["a"]), "running"); + assert.equal(filterShowing("running", agents, ["a", "b"]), "all"); + assert.equal(filterShowing("running", agents, ["b"]), "all"); + assert.equal(filterShowing("all", agents, ["b"]), "all"); +});