Multiple coordinated fixes for the perceived "dashboard takes forever to load" complaint. Per-page-load HTTP requests drop from ~177 to ~101 and duplicate per-project InProcessRuntime creation is eliminated. - engine: shouldUseHybridExecutor no longer auto-enables for local-only multi-project setups (set FUSION_HYBRID_EXECUTOR=1 to force). The duplicate-runtime path was running self-healing twice per project and contending on the same SQLite file. ProjectEngineManager already handles N local projects with one InProcessRuntime each. - dashboard cli: parallelized independent store inits, started CentralCore.init early in background, ran plugin loading concurrently with extension resolution. Sequenced SQLite store inits to avoid a TOCTOU race in addColumnIfMissing migrations across TaskStore / AutomationStore / PluginStore / AgentStore (all open the same .fusion/fusion.db). Restored try/catch around HybridExecutor.initialize and engineManager.ensureEngine so a paused or broken cwd project no longer aborts dashboard startup. - dashboard client: added in-flight request dedupe wrapped around the top API offenders. /api/plugins/ui-slots drops from 17x to 1x per load. dedupe.forceFresh redirects ALL in-flight waiters to receive the fresh post-mutation response, not just the forcing caller. Generation counters in useAgents and AgentListModal protect against slow polls overwriting fresh state. - dashboard SSE: agent event handler now debounces 250ms with a trailing-edge guard so multi-agent activity bursts coalesce to at most 2 refetches per burst. - dashboard route: PATCH /api/projects/:id with isolationMode change returns 503 with actionable guidance when HybridExecutor is unavailable, instead of silently persisting a config the live runtime won't honor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
78 lines
3.0 KiB
TypeScript
78 lines
3.0 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { CentralCore } from "@fusion/core";
|
|
import { shouldUseHybridExecutor } from "../hybrid-executor-gate.js";
|
|
|
|
function createMockCentralCore(overrides?: {
|
|
listNodes?: () => Promise<Array<{ id: string; type: "local" | "remote" }>>;
|
|
listProjects?: () => Promise<Array<{ status: "active" | "initializing" | "paused" | "errored" }>>;
|
|
}): CentralCore {
|
|
return {
|
|
listNodes: overrides?.listNodes ?? (async () => [{ id: "local", type: "local" }]),
|
|
listProjects: overrides?.listProjects ?? (async () => [{ status: "active" }]),
|
|
} as unknown as CentralCore;
|
|
}
|
|
|
|
describe("shouldUseHybridExecutor", () => {
|
|
const originalEnv = process.env.FUSION_HYBRID_EXECUTOR;
|
|
|
|
afterEach(() => {
|
|
if (originalEnv === undefined) {
|
|
delete process.env.FUSION_HYBRID_EXECUTOR;
|
|
} else {
|
|
process.env.FUSION_HYBRID_EXECUTOR = originalEnv;
|
|
}
|
|
});
|
|
|
|
it("enables via env override=1", async () => {
|
|
process.env.FUSION_HYBRID_EXECUTOR = "1";
|
|
const decision = await shouldUseHybridExecutor(createMockCentralCore());
|
|
expect(decision).toEqual({ enabled: true, reason: "env-override" });
|
|
});
|
|
|
|
it("disables via env override=0", async () => {
|
|
process.env.FUSION_HYBRID_EXECUTOR = "0";
|
|
const decision = await shouldUseHybridExecutor(createMockCentralCore());
|
|
expect(decision).toEqual({ enabled: false, reason: "env-override" });
|
|
});
|
|
|
|
it("enables for multi-node", async () => {
|
|
delete process.env.FUSION_HYBRID_EXECUTOR;
|
|
const decision = await shouldUseHybridExecutor(
|
|
createMockCentralCore({
|
|
listNodes: async () => [
|
|
{ id: "local", type: "local" },
|
|
{ id: "remote", type: "remote" },
|
|
],
|
|
}),
|
|
);
|
|
expect(decision).toEqual({ enabled: true, reason: "multi-node" });
|
|
});
|
|
|
|
it("does NOT enable for local-only multi-project (ProjectEngineManager handles it)", async () => {
|
|
delete process.env.FUSION_HYBRID_EXECUTOR;
|
|
const decision = await shouldUseHybridExecutor(
|
|
createMockCentralCore({
|
|
listProjects: async () => [{ status: "active" }, { status: "initializing" }],
|
|
}),
|
|
);
|
|
// HybridExecutor's value is cross-node routing. Local-only N-project
|
|
// setups don't need it — running it duplicates InProcessRuntime creation.
|
|
expect(decision).toEqual({ enabled: false, reason: "single-node-local-only" });
|
|
});
|
|
|
|
it("disables for local-only single-node setup", async () => {
|
|
delete process.env.FUSION_HYBRID_EXECUTOR;
|
|
const decision = await shouldUseHybridExecutor(createMockCentralCore());
|
|
expect(decision).toEqual({ enabled: false, reason: "single-node-local-only" });
|
|
});
|
|
|
|
it("disables when central APIs throw", async () => {
|
|
delete process.env.FUSION_HYBRID_EXECUTOR;
|
|
const centralCore = createMockCentralCore({
|
|
listNodes: vi.fn().mockRejectedValue(new Error("boom")),
|
|
});
|
|
const decision = await shouldUseHybridExecutor(centralCore);
|
|
expect(decision).toEqual({ enabled: false, reason: "central-unavailable" });
|
|
});
|
|
});
|