From ec7898163c6fe8308b0743503ccb472bd9de6c56 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 15 Jul 2026 12:15:37 -0700 Subject: [PATCH] FN-7962: back off idle triage patrol task creation during model outages Teach idle heartbeat patrol prompts to skip fn_task_create when recent model-availability failures are visible, and to base progress claims only on board state fetched in the current heartbeat. - Add standard and concise triage heartbeat guidance to check for model-availability, fallback exhaustion, 429/rate-limit, and 404/model-unavailable failures before creating work - Require existing-task progress/status claims to come from fn_task_list or fn_task_show results in the current heartbeat run - Extend agent-prompts tests to cover both template variants - Add patch changeset for the published package Files changed: .changeset/fn-7962-idle-heartbeat-patrol-backoff.md | 7 +++++++ packages/core/src/__tests__/agent-prompts.test.ts | 14 ++++++++++++++ packages/core/src/agent-prompts.ts | 8 ++++++++ 3 files changed, 29 insertions(+) Fusion-Task-Id: FN-7962 Fusion-Task-Lineage: 0a233b5d-00d8-4dda-b627-95d4b8122a55 Co-authored-by: Fusion (runfusion.ai) --- .../fn-7962-idle-heartbeat-patrol-backoff.md | 7 +++++++ packages/core/src/__tests__/agent-prompts.test.ts | 14 ++++++++++++++ packages/core/src/agent-prompts.ts | 8 ++++++++ 3 files changed, 29 insertions(+) create mode 100644 .changeset/fn-7962-idle-heartbeat-patrol-backoff.md diff --git a/.changeset/fn-7962-idle-heartbeat-patrol-backoff.md b/.changeset/fn-7962-idle-heartbeat-patrol-backoff.md new file mode 100644 index 0000000000..00f07b4e49 --- /dev/null +++ b/.changeset/fn-7962-idle-heartbeat-patrol-backoff.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Make idle triage patrol back off during model outages. +category: fix +dev: Updates built-in standard and concise triage heartbeat prompt guidance. diff --git a/packages/core/src/__tests__/agent-prompts.test.ts b/packages/core/src/__tests__/agent-prompts.test.ts index 64d9300cec..e37b7d51cb 100644 --- a/packages/core/src/__tests__/agent-prompts.test.ts +++ b/packages/core/src/__tests__/agent-prompts.test.ts @@ -473,6 +473,13 @@ describe("resolveAgentPrompt", () => { it("triage heartbeat guidance is customized for standard and concise templates", () => { const defaultTriage = resolveAgentPrompt("triage"); expect(defaultTriage).toContain("Patrol for vague requests"); + expect(defaultTriage).toContain("Before calling `fn_task_create` during a no-task heartbeat"); + expect(defaultTriage).toContain("recent triage or model-availability failures"); + expect(defaultTriage).toContain("model fallback exhaustion"); + expect(defaultTriage).toContain("429/rate-limit"); + expect(defaultTriage).toContain("404/model-unavailable"); + expect(defaultTriage).toContain("skip creating new work rather than adding load"); + expect(defaultTriage).toContain("`fn_task_list` or `fn_task_show` result fetched during this heartbeat run"); const conciseConfig: AgentPromptsConfig = { roleAssignments: { @@ -482,6 +489,13 @@ describe("resolveAgentPrompt", () => { const conciseTriage = resolveAgentPrompt("triage", conciseConfig); expect(conciseTriage).toContain("Keep heartbeat output lean and useful"); expect(conciseTriage).toContain("minimum complete PROMPT.md"); + expect(conciseTriage).toContain("Before `fn_task_create`"); + expect(conciseTriage).toContain("recent model-availability"); + expect(conciseTriage).toContain("model fallback exhaustion"); + expect(conciseTriage).toContain("429/rate-limit"); + expect(conciseTriage).toContain("404/model-unavailable"); + expect(conciseTriage).toContain("back off instead of adding load"); + expect(conciseTriage).toContain("`fn_task_list`/`fn_task_show` results fetched in this heartbeat run"); }); it("merger and senior-engineer heartbeat guidance is role-specific", () => { diff --git a/packages/core/src/agent-prompts.ts b/packages/core/src/agent-prompts.ts index 76e4a5e59b..8a05f3a97b 100644 --- a/packages/core/src/agent-prompts.ts +++ b/packages/core/src/agent-prompts.ts @@ -1224,12 +1224,18 @@ Treat each heartbeat as a short autonomous execution cycle. - If no task is assigned: execute your standing instructions. Review unread messages, scan for blocked or failing engineering work, create narrowly scoped follow-up tasks, and capture durable implementation notes other agents will need later. - Do not idle simply because no task is linked. Use heartbeat time to reduce engineering risk, unblock work, and keep execution moving in small, concrete increments.`; +/* +FNXC:HeartbeatPatrol 2026-07-14-00:00: +Idle planning patrol should keep queues actionable, but must not amplify provider outages by creating more work while recent model-availability, fallback-exhaustion, rate-limit, or model-unavailable failures are visible. Progress claims must come from board state fetched in the current heartbeat so stale context does not misreport task status. +*/ const TRIAGE_HEARTBEAT_GUIDANCE = `## Heartbeat Run Behavior Use heartbeat runs to keep the planning pipeline healthy. - If a task is assigned: turn the rough request into a complete, execution-ready PROMPT.md with clear scope, steps, dependencies, and verification criteria. - If no task is assigned: execute your planning instructions. Patrol for vague requests, blocked tasks that need better specification, review follow-ups that should become new tasks, and dependency gaps that are slowing executors down. +- Before calling \`fn_task_create\` during a no-task heartbeat, check fresh board/tool evidence for recent triage or model-availability failures such as \`unable to select a usable model\`, model fallback exhaustion, 429/rate-limit, or 404/model-unavailable errors. If that condition is present, skip creating new work rather than adding load. +- Any claim about an existing task's progress, step completion, blocker, or status must be based on a \`fn_task_list\` or \`fn_task_show\` result fetched during this heartbeat run, not memory or assumptions from previous context. - Favor ambiguity reduction over busywork. Every heartbeat should leave the queue more actionable than you found it.`; const REVIEWER_HEARTBEAT_GUIDANCE = `## Heartbeat Run Behavior @@ -1270,6 +1276,8 @@ Keep heartbeat output lean and useful. - If a task is assigned: produce the minimum complete PROMPT.md needed for an executor to act safely. - If no task is assigned: execute your planning instructions, scan for underspecified or blocked work, and turn it into short, actionable task specs or follow-up tickets. +- Before \`fn_task_create\`, check fresh board/tool evidence for recent model-availability, model fallback exhaustion, 429/rate-limit, or 404/model-unavailable failures; if present, back off instead of adding load. +- State existing-task progress, blockers, or status only from \`fn_task_list\`/\`fn_task_show\` results fetched in this heartbeat run. - Prefer crisp decisions, clear file scope, and concrete verification steps over narrative detail.`; // ---------------------------------------------------------------------------