fix(engine): wire TaskStore into runtime AgentStore so heartbeat auto-claim works

The InProcessRuntime constructed its AgentStore with only `rootDir`, leaving
task-claim/checkout/release operations unconfigured. As a result, the
heartbeat auto-claim scan logged "TaskStore not configured for task-claim
operations" whenever a relevant todo was found. Pass the runtime's TaskStore
through to the AgentStore so claimTaskForAgent succeeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-06 08:01:03 -07:00
parent 270823dcc9
commit 4dc91edfb2
3 changed files with 36 additions and 1 deletions

View File

@@ -55,6 +55,11 @@ vi.mock("@fusion/core", async () => {
self.init = vi.fn().mockResolvedValue(undefined);
self.listTasks = vi.fn().mockResolvedValue([]);
self.getTask = mockTaskStoreGetTask;
// AgentStore now receives this TaskStore (passed from the runtime),
// so methods it calls during assign/claim/checkout flows must exist.
self.logEntry = vi.fn().mockResolvedValue(undefined);
self.updateTask = vi.fn().mockImplementation(async (taskId: string, patch: Record<string, unknown>) => ({ id: taskId, ...patch }));
self.moveTask = vi.fn().mockResolvedValue(undefined);
self.getSettings = vi.fn().mockImplementation(async () => structuredClone(mockTaskStoreSettings));
self.getMissionStore = vi.fn().mockReturnValue({
listMissions: vi.fn().mockReturnValue([]),
@@ -475,6 +480,26 @@ describe("InProcessRuntime", () => {
expect(monitor).toBeDefined();
}, 30000);
// Regression: heartbeat auto-claim path was warning
// "TaskStore not configured for task-claim operations" because the
// runtime built its AgentStore without passing taskStore through.
it("wires AgentStore with TaskStore so claimTaskForAgent does not throw", async () => {
await runtime.start();
const store = getAgentStore(runtime);
const agent = await store.createAgent({
name: "claim-wiring",
role: "executor",
metadata: { agentKind: "task-worker" },
runtimeConfig: { enabled: false },
});
// taskStore.getTask is mocked to return null in this suite, so we
// expect the guarded "task_not_found" path rather than the
// unconfigured-taskStore throw.
mockTaskStoreGetTask.mockResolvedValueOnce(null);
const result = await store.claimTaskForAgent(agent.id, "FN-DOES-NOT-EXIST");
expect(result).toEqual({ ok: false, reason: "task_not_found" });
}, 30000);
it("should return TriggerScheduler after start", async () => {
await runtime.start();
const triggerScheduler = runtime.getTriggerScheduler();

View File

@@ -321,7 +321,7 @@ export class InProcessRuntime
let agentStoreForReflection: import("@fusion/core").AgentStore | undefined;
try {
const { AgentStore: AgentStoreClass } = await import("@fusion/core");
agentStoreForReflection = new AgentStoreClass({ rootDir: this.taskStore.getFusionDir() });
agentStoreForReflection = new AgentStoreClass({ rootDir: this.taskStore.getFusionDir(), taskStore: this.taskStore });
await agentStoreForReflection.init();
runtimeLog.log("AgentStore initialized for reflection service");
} catch (agentErr) {