feat(FN-4394): complete Step 2 — add strict/lite/off heartbeat procedures

Fusion-Task-Id: FN-4394
Fusion-Task-Lineage: 1e7ad660-2cd2-40ba-b7b8-7ef552c6fa13
This commit is contained in:
Fusion
2026-05-14 02:39:11 -07:00
committed by gsxdsm
parent 304b533cb6
commit 97777ea12a
3 changed files with 312 additions and 2 deletions

View File

@@ -0,0 +1,181 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`agent-heartbeat procedure templates > keeps lite no-task procedure stable 1`] = `
"## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
you expect, and surface any anomalies in your first text output before
doing anything else. The full content is in the Custom Instructions
section of your system prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and
process unread/pending messages before any other action; reply with
reply_to_message_id when answering.
3. **Wake delta** — read the Wake Delta block above. The wake reason is the
highest-priority change for this heartbeat. If you were woken by a comment
or a message, acknowledge it before doing anything else.
4. **Ambient review** — since you have no assigned task, review board/project
signals and recent memory context before acting.
5. **Classify scope before acting** — label the next action as either:
- **Board-scope execution:** work that can be completed now with ambient
tools (coordination, delegation, messaging, memory updates).
- **Implementation-scope discovery:** code/product work that needs a task;
create a focused task instead of attempting unscheduled implementation.
6. **Pick the next concrete action** — exactly ONE useful action this heartbeat:
create a focused task, delegate work, send/reply to a message, or append
durable memory.
7. **Persist progress** — use available ambient tools only:
fn_task_create, fn_delegate_task, fn_send_message, fn_memory_append.
8. **Exit** — call fn_heartbeat_done with a one-line summary of what changed
this tick. If you took no action, say so and explain why.
Critical: a heartbeat without observable progress (a created task, delegation,
message reply, memory append, or explicit "no-op with reason") is a bug. Do
not loop on the same plan across heartbeats without recording why."
`;
exports[`agent-heartbeat procedure templates > keeps lite task procedure stable 1`] = `
"## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
you expect, and surface any anomalies in your first text output before
doing anything else. The full content is in the Custom Instructions
section of your system prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and
process unread/pending messages before any other action; reply with
reply_to_message_id when answering.
3. **Wake delta** — read the Wake Delta block above. The wake reason is the
highest-priority change for this heartbeat. If you were woken by a comment
or a message, acknowledge it before doing anything else.
4. **Assignment review** — if you have an assigned task, re-read its current
description, latest comments, and any task documents. Decide whether the
prior plan is still valid given the wake delta. Do not assume yesterday's
plan is still correct.
5. **Classify scope before acting** — label the next action as either:
- **In-scope execution:** directly advances the assigned task's current
acceptance criteria.
- **Out-of-scope discovery:** useful but separate work; capture it as a
focused follow-up task instead of expanding the current task silently.
6. **Pick the next concrete action** — exactly ONE useful action this heartbeat:
advance the task, create a follow-up, log findings, delegate, or update
memory. Don't stop at planning unless the task is a planning task.
7. **Persist progress** — fn_task_log for observations, fn_task_document_write
for durable findings, status updates only when the work warrants it.
8. **Exit** — call fn_heartbeat_done with a one-line summary of what changed
this tick. If you took no action, say so and explain why.
Critical: a heartbeat without observable progress (a log, a document write, a
status change, a comment, a delegation, or an explicit "no-op with reason") is
a bug. Do not loop on the same plan across heartbeats without recording why."
`;
exports[`agent-heartbeat procedure templates > keeps off no-task procedure stable 1`] = `
"## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of this prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and process unread/pending messages.
3. **Wake delta** — read the Wake Delta block above and handle the highest-priority change first.
4. **Pick one concrete action** — do exactly one useful thing this tick.
5. **Persist progress** — use available ambient tools only.
6. **Exit** — call fn_heartbeat_done with a one-line summary.
Critical: a heartbeat without observable progress (or an explicit no-op reason) is a bug."
`;
exports[`agent-heartbeat procedure templates > keeps off task procedure stable 1`] = `
"## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of this prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and process unread/pending messages.
3. **Wake delta** — read the Wake Delta block above and handle the highest-priority change first.
4. **Pick one concrete action** — do exactly one useful thing this tick.
5. **Persist progress** — record the action via available task/memory tools.
6. **Exit** — call fn_heartbeat_done with a one-line summary.
Critical: a heartbeat without observable progress (or an explicit no-op reason) is a bug."
`;
exports[`agent-heartbeat procedure templates > keeps strict no-task procedure stable 1`] = `
"## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
you expect, and surface any anomalies in your first text output before
doing anything else. The full content is in the Custom Instructions
section of your system prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and
process unread/pending messages before any other action; reply with
reply_to_message_id when answering. If Pending Room Messages are present,
review them in the prompt and use fn_post_room_message only when relevant.
3. **Wake delta** — read the Wake Delta block above. The wake reason is the
highest-priority change for this heartbeat. If you were woken by a comment
or a message, acknowledge it before doing anything else.
4. **Ambient review** — since you have no assigned task, review board/project
signals and recent memory context before acting. No-task heartbeat runs are
inherently coordination-class because no bound task exists to classify.
5. **Classify scope before acting** — label the next action as either:
- **Board-scope execution:** work that can be completed now with ambient
tools (coordination, delegation, messaging, memory updates).
- **Implementation-scope discovery:** code/product work that needs a task;
create a focused task instead of attempting unscheduled implementation.
6. **Pick the next concrete action** — exactly ONE useful action this heartbeat:
create a focused task, delegate work, send/reply to a message, or append
durable memory.
7. **Persist progress** — use available ambient tools only:
fn_task_create, fn_delegate_task, fn_send_message, fn_memory_append.
8. **Exit** — call fn_heartbeat_done with a one-line summary of what changed
this tick. If you took no action, say so and explain why.
Critical: a heartbeat without observable progress (a created task, delegation,
message reply, memory append, or explicit "no-op with reason") is a bug. Do
not loop on the same plan across heartbeats without recording why."
`;
exports[`agent-heartbeat procedure templates > keeps strict task procedure stable 1`] = `
"## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
you expect, and surface any anomalies in your first text output before
doing anything else. The full content is in the Custom Instructions
section of your system prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and
process unread/pending messages before any other action; reply with
reply_to_message_id when answering. If Pending Room Messages are present,
review them in the prompt and use fn_post_room_message only when relevant.
3. **Wake delta** — read the Wake Delta block above. The wake reason is the
highest-priority change for this heartbeat. If you were woken by a comment
or a message, acknowledge it before doing anything else.
4. **Classify the bound task** — if you have an assigned task, classify it as
exactly one of:
- **executor-class** — implementation work: writing code, tests,
documentation prose, or running build/lint/typecheck.
- **blocked** — task has blockedBy set, or is waiting on a peer / dependency
/ external input.
- **coordination-class** — planning, triage, routing, decision-making, or
review.
Then branch:
- If the bound task is **executor-class** or **blocked**, skim it once for
blocker risk, do not re-read PROMPT.md to advance it, and pivot this
heartbeat to broader board signals (in-progress risk scan, stale in-review
queue, idle direct reports, and strategic themes in memory). Inbox is
already handled in step 2.
- If the bound task is **coordination-class**, engage directly with the
bound task.
5. **Pick the next concrete action** — exactly ONE useful action this heartbeat:
advance the task, create a follow-up, log findings, delegate, or update
memory. Don't stop at planning unless the task is a planning task.
6. **Persist progress** — fn_task_log for observations, fn_task_document_write
for durable findings, status updates only when the work warrants it.
7. **Per-tick self-check** — before exiting, verify all three:
- Was the inbox processed?
- Is the chosen action on a coordination-shaped lever?
- If the bound task was executor-class, did I avoid re-planning it?
8. **Exit** — call fn_heartbeat_done with a one-line summary of what changed
this tick. If you took no action, say so and explain why.
Critical: a heartbeat without observable progress (a log, a document write, a
status change, a comment, a delegation, or an explicit "no-op with reason") is
a bug. Do not loop on the same plan across heartbeats without recording why."
`;

View File

@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import {
HEARTBEAT_NO_TASK_PROCEDURE_LITE,
HEARTBEAT_NO_TASK_PROCEDURE_OFF,
HEARTBEAT_NO_TASK_PROCEDURE_STRICT,
HEARTBEAT_PROCEDURE_LITE,
HEARTBEAT_PROCEDURE_OFF,
HEARTBEAT_PROCEDURE_STRICT,
} from "../agent-heartbeat.js";
describe("agent-heartbeat procedure templates", () => {
it("keeps strict task procedure stable", () => {
expect(HEARTBEAT_PROCEDURE_STRICT).toMatchSnapshot();
});
it("keeps lite task procedure stable", () => {
expect(HEARTBEAT_PROCEDURE_LITE).toMatchSnapshot();
});
it("keeps off task procedure stable", () => {
expect(HEARTBEAT_PROCEDURE_OFF).toMatchSnapshot();
});
it("keeps strict no-task procedure stable", () => {
expect(HEARTBEAT_NO_TASK_PROCEDURE_STRICT).toMatchSnapshot();
});
it("keeps lite no-task procedure stable", () => {
expect(HEARTBEAT_NO_TASK_PROCEDURE_LITE).toMatchSnapshot();
});
it("keeps off no-task procedure stable", () => {
expect(HEARTBEAT_NO_TASK_PROCEDURE_OFF).toMatchSnapshot();
});
});

View File

@@ -478,7 +478,7 @@ export const HEARTBEAT_SYSTEM_PROMPT_NO_TASK = HEARTBEAT_NO_TASK_SYSTEM_PROMPT;
* agent to re-anchor on its own operating procedure each wake instead of
* silently grinding on a previously assigned task.
*/
export const HEARTBEAT_PROCEDURE = `## Heartbeat Procedure (run every tick, in order)
export const HEARTBEAT_PROCEDURE_STRICT = `## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
@@ -524,11 +524,59 @@ Critical: a heartbeat without observable progress (a log, a document write, a
status change, a comment, a delegation, or an explicit "no-op with reason") is
a bug. Do not loop on the same plan across heartbeats without recording why.`;
export const HEARTBEAT_PROCEDURE_LITE = `## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
you expect, and surface any anomalies in your first text output before
doing anything else. The full content is in the Custom Instructions
section of your system prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and
process unread/pending messages before any other action; reply with
reply_to_message_id when answering.
3. **Wake delta** — read the Wake Delta block above. The wake reason is the
highest-priority change for this heartbeat. If you were woken by a comment
or a message, acknowledge it before doing anything else.
4. **Assignment review** — if you have an assigned task, re-read its current
description, latest comments, and any task documents. Decide whether the
prior plan is still valid given the wake delta. Do not assume yesterday's
plan is still correct.
5. **Classify scope before acting** — label the next action as either:
- **In-scope execution:** directly advances the assigned task's current
acceptance criteria.
- **Out-of-scope discovery:** useful but separate work; capture it as a
focused follow-up task instead of expanding the current task silently.
6. **Pick the next concrete action** — exactly ONE useful action this heartbeat:
advance the task, create a follow-up, log findings, delegate, or update
memory. Don't stop at planning unless the task is a planning task.
7. **Persist progress** — fn_task_log for observations, fn_task_document_write
for durable findings, status updates only when the work warrants it.
8. **Exit** — call fn_heartbeat_done with a one-line summary of what changed
this tick. If you took no action, say so and explain why.
Critical: a heartbeat without observable progress (a log, a document write, a
status change, a comment, a delegation, or an explicit "no-op with reason") is
a bug. Do not loop on the same plan across heartbeats without recording why.`;
export const HEARTBEAT_PROCEDURE_OFF = `## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of this prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and process unread/pending messages.
3. **Wake delta** — read the Wake Delta block above and handle the highest-priority change first.
4. **Pick one concrete action** — do exactly one useful thing this tick.
5. **Persist progress** — record the action via available task/memory tools.
6. **Exit** — call fn_heartbeat_done with a one-line summary.
Critical: a heartbeat without observable progress (or an explicit no-op reason) is a bug.`;
// Backward-compatible alias; prefer HEARTBEAT_PROCEDURE_STRICT.
export const HEARTBEAT_PROCEDURE = HEARTBEAT_PROCEDURE_STRICT;
/**
* No-task variant of HEARTBEAT_PROCEDURE. Keep this aligned with the ambient
* tool set (no fn_task_log / fn_task_document_* in no-task runs).
*/
export const HEARTBEAT_NO_TASK_PROCEDURE = `## Heartbeat Procedure (run every tick, in order)
export const HEARTBEAT_NO_TASK_PROCEDURE_STRICT = `## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
@@ -562,6 +610,52 @@ Critical: a heartbeat without observable progress (a created task, delegation,
message reply, memory append, or explicit "no-op with reason") is a bug. Do
not loop on the same plan across heartbeats without recording why.`;
export const HEARTBEAT_NO_TASK_PROCEDURE_LITE = `## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of
this prompt. Confirm your role, soul, instructions, and memory match what
you expect, and surface any anomalies in your first text output before
doing anything else. The full content is in the Custom Instructions
section of your system prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and
process unread/pending messages before any other action; reply with
reply_to_message_id when answering.
3. **Wake delta** — read the Wake Delta block above. The wake reason is the
highest-priority change for this heartbeat. If you were woken by a comment
or a message, acknowledge it before doing anything else.
4. **Ambient review** — since you have no assigned task, review board/project
signals and recent memory context before acting.
5. **Classify scope before acting** — label the next action as either:
- **Board-scope execution:** work that can be completed now with ambient
tools (coordination, delegation, messaging, memory updates).
- **Implementation-scope discovery:** code/product work that needs a task;
create a focused task instead of attempting unscheduled implementation.
6. **Pick the next concrete action** — exactly ONE useful action this heartbeat:
create a focused task, delegate work, send/reply to a message, or append
durable memory.
7. **Persist progress** — use available ambient tools only:
fn_task_create, fn_delegate_task, fn_send_message, fn_memory_append.
8. **Exit** — call fn_heartbeat_done with a one-line summary of what changed
this tick. If you took no action, say so and explain why.
Critical: a heartbeat without observable progress (a created task, delegation,
message reply, memory append, or explicit "no-op with reason") is a bug. Do
not loop on the same plan across heartbeats without recording why.`;
export const HEARTBEAT_NO_TASK_PROCEDURE_OFF = `## Heartbeat Procedure (run every tick, in order)
1. **Identity & context** — review the **Identity Snapshot** at the top of this prompt.
2. **Inbox** — when fn_read_messages is available, call it immediately and process unread/pending messages.
3. **Wake delta** — read the Wake Delta block above and handle the highest-priority change first.
4. **Pick one concrete action** — do exactly one useful thing this tick.
5. **Persist progress** — use available ambient tools only.
6. **Exit** — call fn_heartbeat_done with a one-line summary.
Critical: a heartbeat without observable progress (or an explicit no-op reason) is a bug.`;
// Backward-compatible alias; prefer HEARTBEAT_NO_TASK_PROCEDURE_STRICT.
export const HEARTBEAT_NO_TASK_PROCEDURE = HEARTBEAT_NO_TASK_PROCEDURE_STRICT;
/** Parameter schema for the fn_heartbeat_done tool */
const heartbeatDoneParams = Type.Object({
summary: Type.Optional(Type.String({ description: "Summary of what was accomplished this heartbeat" })),