From 0c0608fa50ec10215b801699f3e921244c22acd0 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sun, 17 May 2026 03:59:30 +0200 Subject: [PATCH] fix(swarm): recognize unit-specific completion tools as implicit complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detected via supervisory check 2026-05-17: SF stuck in degenerate reassess- roadmap loop on M010/S03 (5 iterations in 8min, all returning outcome=continue). Root cause: synthesized-checkpoint in runUnitViaSwarm only treats the generic `checkpoint` tool as a completion signal — but units routinely complete via their unit-specific tool (reassess_roadmap with verdict=roadmap-confirmed, validate_milestone, complete_milestone, complete_slice, save_summary). The LLM correctly emitted the unit's specific completion tool + assistant text "complete", but workerSignaledOutcome stayed null → synthesized checkpoint fell back to continue → solver re-iterated. Fix: recognize UNIT_COMPLETION_TOOLS = {reassess_roadmap, validate_milestone, complete_milestone, complete_slice, save_summary} as implicit "complete" signals. The check fires when those tools are called and an earlier explicit checkpoint hasn't already said "complete" or "blocked". This resolves sf-mp94lth4-ew26om and should prevent future degenerate-iteration loops on reassess-roadmap and milestone completion units. 13/13 existing M010 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/resources/extensions/sf/auto/run-unit.js | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/resources/extensions/sf/auto/run-unit.js b/src/resources/extensions/sf/auto/run-unit.js index f869469c1..57590fb17 100644 --- a/src/resources/extensions/sf/auto/run-unit.js +++ b/src/resources/extensions/sf/auto/run-unit.js @@ -562,6 +562,32 @@ async function runUnitViaSwarm(ctx, _pi, s, unitType, unitId, prompt, options) { input: toolCall.arguments ?? {}, }); + // #sf-mp94lth4-ew26om: Recognize unit-specific completion tools as + // implicit "complete" signals. Workers often call the unit's specific + // completion tool (reassess_roadmap with verdict, validate_milestone, + // complete_milestone, complete_slice, save_summary) and skip the + // generic checkpoint tool. Without this, the synthesized checkpoint + // falls back to outcome=continue and the solver iterates indefinitely + // despite the worker having genuinely finished (witnessed on + // reassess-roadmap M010/S03 — 5 iterations in 8min, all confirming + // completion in their text but never calling `checkpoint`). + const UNIT_COMPLETION_TOOLS = new Set([ + "reassess_roadmap", + "validate_milestone", + "complete_milestone", + "complete_slice", + "save_summary", + ]); + if ( + UNIT_COMPLETION_TOOLS.has(toolCall.name) && + workerSignaledOutcome !== "complete" && + workerSignaledOutcome !== "blocked" + ) { + // Mark as complete unless an earlier explicit checkpoint already + // said otherwise (don't override blocked / explicit progress). + workerSignaledOutcome = "complete"; + } + // Detect the canonical completion-signal tool: "checkpoint". // This is the same tool inspected by the legacy runUnit path's solver pass // (phases-unit.js line ~989: activeToolsAllowlist: ["checkpoint"]).