Repository navigation
fix(swarm): stop treating silent idle agents as heartbeat failures - #4320
Conversation
UnifiedSwarmCoordinator.checkHeartbeats() marked every agent error after 3 heartbeat intervals when no message arrived. Idle agents have no heartbeat producer and no in-flight work, so healthy workers flapped to error (and back through auto-recovery), and getAvailableAgents() stopped offering them. Skip idle agents with no currentTask; busy agents keep the existing liveness timeout.
|
Verified the mechanism directly rather than taking the write-up at face value: the old `checkHeartbeats()` loop flagged any non-terminated agent as `error` purely on heartbeat silence, with no distinction for agents that have no heartbeat producer to begin with (idle, no current task). The fix's skip condition (`agent.status === 'terminated' || (agent.status === 'idle' && !agent.currentTask)`) is correctly scoped — it only exempts truly-idle agents, and a busy agent that goes silent still hits the existing 3-interval timeout path unchanged (confirmed in the diff: the busy-agent branch is untouched below the new early-`continue`). The two new tests precisely isolate the two cases (idle+silent stays healthy; busy+silent still flips to `error`), which is exactly what's needed to pin this behavior down. Small, surgical, low blast radius — no public API/signature changes, one private liveness check. Will merge once CI is green. |
Minor release (new backward-compatible flags: Grok Build CLI host surface ruvnet#3512, memory list/search tag filter ruvnet#3526) bundled with a round of correctness fixes merged since 3.58.1: memory delete-sync (ruvnet#4138), real AgentDB HNSWIndex replacing the dead getController('hnsw') path with safe exact-search fallback (ruvnet#4249/ruvnet#4322), harness live-process reporting (ruvnet#3521), RaBitQ on-demand index build (ruvnet#4318/ruvnet#4319), metaharness ADR-152 cost-dimension and verdict-band fix (ruvnet#3520), swarm idle-agent heartbeat false-positive (ruvnet#4320), ephemeral-agent termination idempotency (ruvnet#4323), status/task_list field-mapping and total-count fixes (ruvnet#4321/ruvnet#4324). Also fixes the actual cause of broken images/links on npmjs.com/package/ruflo: `ruflo/package.json`'s `repository.directory: "ruflo"` field added an extra path layer on top of the README content (the root README.md, copied in verbatim by `prepublishOnly` at publish time, which is already written with repo-root-relative paths) — every relative image/link resolved to e.g. `raw/main/ruflo/ruflo/assets/...` instead of `raw/main/ruflo/assets/...`. Removed the `directory` field to match claude-flow's own (directory-less) repository entry. A prior PR (ruvnet#4325) fixed `ruflo/README.md`'s own relative paths for anyone browsing that file directly on GitHub, but that file is overwritten by the root README at publish time and was never the actual npm-rendered content. Published to npm: @claude-flow/cli@3.59.0, claude-flow@3.59.0, ruflo@3.59.0, and standalone @claude-flow/memory@3.0.8 (the ruvnet#4249 fix — not part of the 3-package train, published separately per its own leaf's npm presence). All three umbrella packages' latest/alpha/v3alpha dist-tags verified pointing at 3.59.0. Co-Authored-By: RuFlo <ruv@ruv.net>
Problem and change
UnifiedSwarmCoordinator.checkHeartbeats()marks every agenterroroncenow - agent.lastHeartbeat > heartbeatIntervalMs * 3(15s with the default 5s interval). Idle agents have no heartbeat producer and no in-flight work, so after registration they receive no messages and never refreshlastHeartbeat. Healthy idle workers therefore:errorafter ~15s;getAvailableAgents()(which only returnsstatus === 'idle'), so generic task dispatch stops using them;autoRecoverydecays health to 0.2 (~75s) and resets them, after which the cycle repeats.The coordinator only treats
terminatedas a non-live state. This change skipsidleagents that also have nocurrentTask:Busy agents keep the existing liveness timeout: an agent that is actually working must still report a heartbeat or completion. The behavior for
error/completed/other states is unchanged.Validation
v3/@claude-flow/swarm/__tests__/idle-agent-heartbeat.test.ts:idlewith health 1 after a simulated timeout;error.19fb5c6e7: 1/2 fail (the idle case becomeserror; the busy control passes).@claude-flow/swarmsuite 346/346, 31/31 files;tsc --noEmit0 errors;git diff --checkclean.Scope / known limitations
AgentRegistry(the deprecatedSwarmHubpath) has its own heartbeat loop and is unchanged.Compatibility