feat(FN-987): wire AgentStore to TaskExecutor in dashboard command

- Wire AgentStore into TaskExecutor initialization within dashboard.ts startup
- Add AgentStore mock to dashboard command tests for proper test coverage
- Update test mocks to reflect new AgentStore dependency injection
This commit is contained in:
gsxdsm
2026-04-05 15:55:05 -07:00
parent 18c8fd9858
commit 11dfcaf7e2
3 changed files with 29 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ function makeMockStore() {
openrouterModelSync: true,
}),
listTasks: vi.fn().mockResolvedValue([]),
getFusionDir: vi.fn().mockReturnValue("/tmp/test/.fusion"),
on: vi.fn((event: string, handler: (...args: unknown[]) => void) => {
emitter.on(event, handler);
}),
@@ -38,6 +39,14 @@ vi.mock("@fusion/core", () => ({
listSchedules: vi.fn().mockResolvedValue([]),
getDueSchedules: vi.fn().mockResolvedValue([]),
})),
AgentStore: vi.fn().mockImplementation(() => ({
init: vi.fn().mockResolvedValue(undefined),
createAgent: vi.fn(),
updateAgentState: vi.fn(),
listAgents: vi.fn().mockResolvedValue([]),
getAgent: vi.fn().mockResolvedValue(null),
deleteAgent: vi.fn(),
})),
}));
// ── Mock @fusion/dashboard ─────────────────────────────────────────────

View File

@@ -51,6 +51,7 @@ function makeMockStore() {
updatePrInfo: vi.fn().mockResolvedValue({}),
logEntry: vi.fn().mockResolvedValue(undefined),
updateTask: vi.fn().mockResolvedValue({}),
getFusionDir: vi.fn().mockReturnValue("/tmp/test/.fusion"),
close: vi.fn(),
on: vi.fn((event: string, handler: (...args: unknown[]) => void) => {
emitter.on(event, handler);
@@ -76,6 +77,14 @@ vi.mock("@fusion/core", () => ({
recordRun: vi.fn().mockResolvedValue({}),
getDueSchedules: vi.fn().mockResolvedValue([]),
})),
AgentStore: vi.fn().mockImplementation(() => ({
init: vi.fn().mockResolvedValue(undefined),
createAgent: vi.fn(),
updateAgentState: vi.fn(),
listAgents: vi.fn().mockResolvedValue([]),
getAgent: vi.fn().mockResolvedValue(null),
deleteAgent: vi.fn(),
})),
getTaskMergeBlocker: vi.fn((task: any) => {
if (task.column !== "in-review") return `task is in '${task.column}', must be in 'in-review'`;
if (task.paused) return "task is paused";

View File

@@ -1,7 +1,7 @@
import { execSync } from "node:child_process";
import type { AddressInfo } from "node:net";
import { createInterface } from "node:readline";
import { TaskStore, AutomationStore, CentralCore, getTaskMergeBlocker } from "@fusion/core";
import { TaskStore, AutomationStore, CentralCore, AgentStore, getTaskMergeBlocker } from "@fusion/core";
import type { Settings, TaskDetail, PrInfo } from "@fusion/core";
import { createServer, GitHubClient } from "@fusion/dashboard";
import { TriageProcessor, TaskExecutor, Scheduler, AgentSemaphore, WorktreePool, aiMergeTask, UsageLimitPauser, PRIORITY_MERGE, scanIdleWorktrees, cleanupOrphanedWorktrees, NtfyNotifier, PrMonitor, PrCommentHandler, CronRunner, StuckTaskDetector, SelfHealingManager } from "@fusion/engine";
@@ -207,6 +207,15 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
const automationStore = new AutomationStore(cwd);
await automationStore.init();
// ── AgentStore: agent lifecycle tracking ──────────────────────────
//
// Tracks spawned agents so they appear in the dashboard's Agents view
// and are properly managed throughout their lifecycle (creation, state
// transitions, termination). Passed to TaskExecutor for agent spawning.
//
const agentStore = new AgentStore({ rootDir: store.getFusionDir() });
await agentStore.init();
// ── NtfyNotifier: push notifications for task completion and failures ─
//
// Resolve the project ID from the central registry so that notification
@@ -598,6 +607,7 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
pool,
usageLimitPauser,
stuckTaskDetector,
agentStore,
onStart: (t, p) => console.log(`[engine] Executing ${t.id} in ${p}`),
onComplete: (t) => console.log(`[engine] ✓ ${t.id} → in-review`),
onError: (t, e) => console.log(`[engine] ✗ ${t.id}: ${e.message}`),