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) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-15 12:15:37 -07:00
parent 37db66965c
commit ec7898163c
3 changed files with 29 additions and 0 deletions

View File

@@ -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.

View File

@@ -473,6 +473,13 @@ describe("resolveAgentPrompt", () => {
it("triage heartbeat guidance is customized for standard and concise templates", () => { it("triage heartbeat guidance is customized for standard and concise templates", () => {
const defaultTriage = resolveAgentPrompt("triage"); const defaultTriage = resolveAgentPrompt("triage");
expect(defaultTriage).toContain("Patrol for vague requests"); 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 = { const conciseConfig: AgentPromptsConfig = {
roleAssignments: { roleAssignments: {
@@ -482,6 +489,13 @@ describe("resolveAgentPrompt", () => {
const conciseTriage = resolveAgentPrompt("triage", conciseConfig); const conciseTriage = resolveAgentPrompt("triage", conciseConfig);
expect(conciseTriage).toContain("Keep heartbeat output lean and useful"); expect(conciseTriage).toContain("Keep heartbeat output lean and useful");
expect(conciseTriage).toContain("minimum complete PROMPT.md"); 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", () => { it("merger and senior-engineer heartbeat guidance is role-specific", () => {

View File

@@ -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. - 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.`; - 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 const TRIAGE_HEARTBEAT_GUIDANCE = `## Heartbeat Run Behavior
Use heartbeat runs to keep the planning pipeline healthy. 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 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. - 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.`; - Favor ambiguity reduction over busywork. Every heartbeat should leave the queue more actionable than you found it.`;
const REVIEWER_HEARTBEAT_GUIDANCE = `## Heartbeat Run Behavior 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 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. - 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.`; - Prefer crisp decisions, clear file scope, and concrete verification steps over narrative detail.`;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------