diff --git a/packages/engine/src/__tests__/self-healing.test.ts b/packages/engine/src/__tests__/self-healing.test.ts index e699b5ebe1..e6653e839d 100644 --- a/packages/engine/src/__tests__/self-healing.test.ts +++ b/packages/engine/src/__tests__/self-healing.test.ts @@ -1952,6 +1952,43 @@ describe("SelfHealingManager", () => { managerWithAgents.stop(); }); + it("continues after task-gone lookup races and recovers later agents", async () => { + const agents: Agent[] = [ + { id: "agent-deleted", state: "running", taskId: "FN-DELETED", updatedAt: new Date(Date.now() - 120_000).toISOString() } as Agent, + { id: "agent-missing", state: "running", taskId: "FN-MISSING", updatedAt: new Date(Date.now() - 120_000).toISOString() } as Agent, + { id: "agent-error", state: "running", taskId: "FN-ERROR", updatedAt: new Date(Date.now() - 120_000).toISOString() } as Agent, + { id: "agent-later", state: "running", taskId: "FN-LATER", updatedAt: new Date(Date.now() - 120_000).toISOString() } as Agent, + ]; + const getTask = vi.fn(async (taskId: string) => { + if (taskId === "FN-DELETED") throw new TaskDeletedError(taskId, new Date().toISOString()); + if (taskId === "FN-MISSING") throw new TaskNotFoundError(taskId); + if (taskId === "FN-ERROR") throw new Error("database unavailable"); + return { id: taskId, column: "todo" } as Task; + }); + const agentStore = { + listAgents: vi.fn(async () => agents), + getActiveHeartbeatRun: vi.fn(async () => null), + updateAgentState: vi.fn(async (agentId: string, state: Agent["state"]) => { + const agent = agents.find((candidate) => candidate.id === agentId); + if (agent) agent.state = state; + }), + syncExecutionTaskLink: vi.fn(async (agentId: string, taskId?: string) => { + const agent = agents.find((candidate) => candidate.id === agentId); + if (agent) agent.taskId = taskId; + }), + } as unknown as AgentStore; + const managerWithAgents = new SelfHealingManager( + createMockStore({ getTask }), + { rootDir: "/tmp/test-project", agentStore }, + ); + + const recovered = await managerWithAgents.recoverAgentsRunningOnInactiveTasks(); + + expect(recovered).toBe(3); + expect(agentStore.syncExecutionTaskLink).toHaveBeenCalledWith("agent-later", undefined); + managerWithAgents.stop(); + }); + /* FNXC:WorkflowResolvedColumns 2026-07-31-23:55: `agentLinkTerminalColumns` was UNCOVERED on the #3115 map. The case above uses `todo` and