fix(FN-4249): align running-agent reconciliation with taskId linkage

Fusion-Task-Id: FN-4249
Fusion-Task-Lineage: e0052fae-38e1-4d66-8240-43d26f5790bb
This commit is contained in:
Fusion
2026-05-12 22:34:30 -07:00
committed by gsxdsm
parent 53aa1e90f1
commit b536d92d9c
4 changed files with 12 additions and 12 deletions

View File

@@ -29,7 +29,7 @@ vi.mock("node:fs/promises", async (importOriginal) => {
};
});
type MutableAgent = Agent & { executionTaskId?: string | null };
type MutableAgent = Agent;
function createMockTask(overrides: Partial<Task> = {}): Task {
return {
@@ -90,10 +90,10 @@ function createAgentStore(agents: MutableAgent[]): AgentStore {
if (existing.state === state) return;
existing.state = state;
}),
syncExecutionTaskLink: vi.fn(async (id: string, taskId?: string | null) => {
syncExecutionTaskLink: vi.fn(async (id: string, taskId?: string) => {
const existing = byId.get(id);
if (!existing) return;
existing.executionTaskId = taskId ?? null;
existing.taskId = taskId;
}),
} as unknown as AgentStore;
}
@@ -134,7 +134,7 @@ describe("scheduler overlap requeue agent-state invariant (FN-4249)", () => {
await workerManager.onTaskStart(queuedCandidate);
const scheduler = new Scheduler(taskStore, { agentStore });
(scheduler as { running: boolean }).running = true;
(scheduler as any).running = true;
await scheduler.schedule();
await scheduler.schedule();
@@ -144,7 +144,7 @@ describe("scheduler overlap requeue agent-state invariant (FN-4249)", () => {
expect(task?.column).toBe("todo");
expect(task?.status).toBe("queued");
expect(agent.state).toBe("active");
expect(agent.executionTaskId ?? null).toBeNull();
expect(agent.taskId).toBeUndefined();
expect(agentStore.updateAgentState).toHaveBeenCalledWith("agent-assigned", "active");
expect(agentStore.updateAgentState).toHaveBeenCalledWith("agent-assigned", "running");
expect(agentStore.updateAgentState).toHaveBeenCalledTimes(2);

View File

@@ -876,13 +876,13 @@ describe("SelfHealingManager", () => {
{
id: "agent-recover",
state: "running",
executionTaskId: "FN-TODO",
taskId: "FN-TODO",
updatedAt: new Date(now - 120_000).toISOString(),
} as Agent,
{
id: "agent-keep",
state: "running",
executionTaskId: "FN-IP",
taskId: "FN-IP",
updatedAt: new Date(now - 120_000).toISOString(),
} as Agent,
];
@@ -902,7 +902,7 @@ describe("SelfHealingManager", () => {
}),
syncExecutionTaskLink: vi.fn(async (agentId: string, taskId?: string) => {
const agent = agents.find((candidate) => candidate.id === agentId);
if (agent) agent.executionTaskId = taskId;
if (agent) agent.taskId = taskId;
}),
} as unknown as AgentStore;

View File

@@ -493,7 +493,7 @@ export class Scheduler {
if (!agentStore) return;
const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral: true });
const linkedAgents = runningAgents.filter((agent) => agent.executionTaskId === taskId);
const linkedAgents = runningAgents.filter((agent) => agent.taskId === taskId);
for (const agent of linkedAgents) {
await agentStore.updateAgentState(agent.id, "active");

View File

@@ -2368,11 +2368,11 @@ export class SelfHealingManager {
const runningAgents = await agentStore.listAgents({ state: "running", includeEphemeral: true });
for (const agent of runningAgents) {
if (isEphemeralAgent(agent) || !agent.executionTaskId) {
if (isEphemeralAgent(agent) || !agent.taskId) {
continue;
}
const linkedTask = await this.store.getTask(agent.executionTaskId);
const linkedTask = await this.store.getTask(agent.taskId);
if (linkedTask && (linkedTask.column === "in-progress" || linkedTask.column === "in-review" || linkedTask.column === "done" || linkedTask.column === "archived")) {
continue;
}
@@ -2388,7 +2388,7 @@ export class SelfHealingManager {
await agentStore.updateAgentState(agent.id, "active");
await agentStore.syncExecutionTaskLink(agent.id, undefined);
recoveredAgentIds.add(agent.id);
log.log(`Recovered running durable agent ${agent.id} on inactive task ${agent.executionTaskId}`);
log.log(`Recovered running durable agent ${agent.id} on inactive task ${agent.taskId}`);
}
return recoveredAgentIds.size;