feat(FN-3630): document lastError recovery semantics in agents.md

Documents `lastError` recovery semantics in the agents reference, clarifying how the system handles and recovers from error states.

Fusion-Task-Id: FN-3630
This commit is contained in:
Fusion
2026-05-07 02:03:24 -07:00
committed by gsxdsm
parent c3c9f49ca2
commit d68d598a4f
3 changed files with 25 additions and 3 deletions

View File

@@ -325,6 +325,27 @@ describe("executeHeartbeat", () => {
expect(store.endHeartbeatRun).toHaveBeenCalledWith(run.id, "terminated");
});
it("clears stale lastError after a subsequent successful heartbeat run", async () => {
const store = createStoreWithAgentForExec({ state: "running" });
const monitor = new HeartbeatMonitor({ store, taskStore: mockTaskStore, rootDir: "/tmp" });
const failedRun = await monitor.startRun("agent-001", { source: "on_demand" });
await monitor.completeRun("agent-001", failedRun.id, {
status: "failed",
stderrExcerpt: "Prompt failed",
});
const successfulRun = await monitor.startRun("agent-001", { source: "on_demand" });
await monitor.completeRun("agent-001", successfulRun.id, {
status: "completed",
});
expect(store.updateAgentState).toHaveBeenCalledWith("agent-001", "error");
expect(store.updateAgent).toHaveBeenCalledWith("agent-001", { lastError: "Prompt failed" });
expect(store.updateAgentState).toHaveBeenCalledWith("agent-001", "active");
expect(store.updateAgent).toHaveBeenCalledWith("agent-001", { lastError: undefined });
});
it("completes as failed when agent not found in store", async () => {
const store = createStoreWithAgentForExec();
(store.getAgent as ReturnType<typeof vi.fn>).mockResolvedValue(null);

View File

@@ -904,8 +904,9 @@ export class HeartbeatMonitor {
} else if (completionResult.status === "terminated") {
await this.store.updateAgentState(agentId, "paused");
} else {
// Completed successfully - back to active
// Completed successfully - back to active and clear any stale failure marker.
await this.store.updateAgentState(agentId, "active");
await this.store.updateAgent(agentId, { lastError: undefined });
}
} catch (stateTransErr) {
heartbeatLog.warn(`Agent ${agentId} state transition failed: ${stateTransErr instanceof Error ? stateTransErr.message : String(stateTransErr)} — continuing`);