fix(FN-2098): align no-task heartbeat guidance and stabilize flaky tests

- Update HEARTBEAT_NO_TASK_SYSTEM_PROMPT copy to emphasize inbox, memory, delegation, and heartbeat_done usage
- Expand heartbeat monitor tests to assert no-task prompt/tool alignment and preserve task-scoped prompt behavior
- Harden first-run and App view tests by using a safe cwd fallback and more robust async UI waits
- Add best-effort dashboard performance reporting hooks in App and useProjects via a new reportDashboardPerf API helper
This commit is contained in:
Fusion
2026-04-19 01:30:33 -07:00
committed by gsxdsm
parent d9a806f61a
commit ca6fefd6ca
7 changed files with 115 additions and 23 deletions

View File

@@ -1361,6 +1361,33 @@ describe("HeartbeatMonitor", () => {
expect(toolNames).not.toContain("task_document_read");
});
it("no-task run system prompt uses HEARTBEAT_NO_TASK_SYSTEM_PROMPT and does not reference task-scoped tools", async () => {
const store = createStoreWithAgentForExec({ taskId: undefined, soul: "I am a coordinator" });
const mockSession = createMockAgentSession();
mockedCreateKbAgent.mockResolvedValue({ session: mockSession as any });
const monitor = new HeartbeatMonitor({ store, taskStore: mockTaskStore, rootDir: "/tmp" });
await monitor.executeHeartbeat({ agentId: "agent-001", source: "timer" });
expect(mockedCreateKbAgent).toHaveBeenCalledOnce();
const callArgs = mockedCreateKbAgent.mock.calls[0]![0]!;
const systemPrompt = callArgs.systemPrompt;
expect(systemPrompt).toContain(HEARTBEAT_NO_TASK_SYSTEM_PROMPT);
expect(systemPrompt).not.toContain("task_log");
expect(systemPrompt).not.toContain("task_document_write");
expect(systemPrompt).not.toContain("task_document_read");
expect(systemPrompt).toContain("task_create");
expect(systemPrompt).toContain("list_agents");
expect(systemPrompt).toContain("delegate_task");
expect(systemPrompt).toContain("read_messages");
expect(systemPrompt).toContain("send_message");
expect(systemPrompt).toContain("memory_search");
expect(systemPrompt).toContain("memory_append");
expect(systemPrompt).toContain("heartbeat_done");
});
it("identity agent without task receives no-task execution prompt mentioning 'no assigned task'", async () => {
const store = createStoreWithAgentForExec({ taskId: undefined, soul: "I am a coordinator" });
const mockSession = createMockAgentSession();
@@ -1373,6 +1400,7 @@ describe("HeartbeatMonitor", () => {
expect(mockedCreateKbAgent).toHaveBeenCalledOnce();
const callArgs = mockedCreateKbAgent.mock.calls[0]![0]!;
const systemPrompt = callArgs.systemPrompt;
expect(systemPrompt).toContain(HEARTBEAT_NO_TASK_SYSTEM_PROMPT);
expect(systemPrompt).not.toContain("task_log");
expect(systemPrompt).not.toContain("task_document_write");
expect(systemPrompt).not.toContain("task_document_read");
@@ -1398,7 +1426,7 @@ describe("HeartbeatMonitor", () => {
expect(executionPrompt).not.toContain("Task description:");
});
it("task-scoped heartbeat run receives full system prompt with task_log and task Documents", async () => {
it("task-scoped run system prompt uses original HEARTBEAT_SYSTEM_PROMPT", async () => {
const store = createStoreWithAgentForExec({ taskId: "FN-001" });
const mockSession = createMockAgentSession();
mockedCreateKbAgent.mockResolvedValue({ session: mockSession as any });
@@ -1411,6 +1439,7 @@ describe("HeartbeatMonitor", () => {
const callArgs = mockedCreateKbAgent.mock.calls[0]![0]!;
const systemPrompt = callArgs.systemPrompt;
expect(systemPrompt).toContain(HEARTBEAT_SYSTEM_PROMPT);
expect(systemPrompt).toContain("task_log");
expect(systemPrompt).toContain("task_document_write");
expect(systemPrompt).toContain("Task Documents:");
@@ -2220,11 +2249,19 @@ describe("HeartbeatMonitor", () => {
});
describe("execution", () => {
it("no-task heartbeat system prompt does not reference task-scoped tools", () => {
it("HEARTBEAT_NO_TASK_SYSTEM_PROMPT does not mention task_log or task_document tools", () => {
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_log");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_document_write");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_document_read");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).not.toContain("task_document");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("task_create");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("list_agents");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("delegate_task");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("read_messages");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("send_message");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("memory_search");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("memory_append");
expect(HEARTBEAT_NO_TASK_SYSTEM_PROMPT).toContain("heartbeat_done");
});
it("creates session with enriched system prompt and expected tools", async () => {

View File

@@ -169,12 +169,15 @@ When sending messages:
export const HEARTBEAT_NO_TASK_SYSTEM_PROMPT = `You are a heartbeat agent running in a short execution window.
Your job:
1. Do ONE useful action: analyze, review, create follow-up tasks, or log findings.
2. Use task_create to spawn follow-up work.
3. Call heartbeat_done when finished with an optional summary of what was accomplished.
1. Review your context — check messages, memory, and project state.
2. Do ONE useful action: analyze, create follow-up tasks, or update memory.
3. Use task_create to spawn follow-up work.
4. Use list_agents and delegate_task to assign work to other agents.
5. Call heartbeat_done when finished with an optional summary of what was accomplished.
Keep work lightweight — this is a single-pass check, not a full implementation run.
You have readonly file access plus task_create, list_agents, delegate_task, messaging, and memory tools (memory_search, memory_get, memory_append).
You have readonly file access plus task_create, list_agents, delegate_task, messaging, memory, and heartbeat_done tools.
Use read_messages and send_message for inbox processing, and memory_search, memory_get, and memory_append for memory workflows.
## Memory Boundaries
@@ -189,9 +192,9 @@ When you are woken by an incoming message (source includes "wake-on-message"), y
1. Use read_messages to check your inbox for unread messages.
2. Review each message and determine the appropriate action:
- If the message requires a response, use send_message to reply.
- If the message is informational, acknowledge it with a brief response or note it in memory.
- If the message is informational, acknowledge it and respond via send_message if appropriate.
- If the message requests work, create a follow-up task with task_create or handle it directly.
3. After processing messages, continue with your normal heartbeat duties.
3. After processing messages, continue with your ambient work.
When sending messages:
- Be concise and clear about what you need or what you've done.