refactor: replace primary/secondary engine pattern with uniform ProjectEngineManager

Remove the anti-pattern where the cwd project was treated as "primary" with a
special engine, and other projects got "secondary" engines through a separate
code path. Every project now gets an identical ProjectEngine created through
ProjectEngineManager.

Key changes:
- Add ProjectEngineManager class to @fusion/engine for uniform engine lifecycle
- Replace manual engine maps in dashboard.ts and serve.ts with engineManager
- Add engineManager to ServerOptions for per-project engine resolution
- Add getProjectContext() helper in routes.ts (replaces 199 getScopedStore calls)
- Merge and automation routes now resolve engine subsystems per-request
- SSE endpoint uses engine's store when available (same EventEmitter)
- Fix tsx not found in dev-with-memory.mjs startup script
- Add invalidateAllGlobalSettingsCaches for cross-project settings sync

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-13 13:32:38 -07:00
parent 6d414f7b59
commit 1383b8d7d6
12 changed files with 520 additions and 500 deletions

View File

@@ -3,7 +3,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
// Mock ProjectEngine before importing the manager
vi.mock("../project-engine.js", () => {
return {
ProjectEngine: vi.fn().mockImplementation((config, _centralCore, _options) => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ProjectEngine: vi.fn().mockImplementation((config: any) => ({
start: vi.fn().mockResolvedValue(undefined),
stop: vi.fn().mockResolvedValue(undefined),
getTaskStore: vi.fn().mockReturnValue({ projectId: config.projectId }),
@@ -127,6 +128,7 @@ describe("ProjectEngineManager", () => {
// Make the first start fail
let callCount = 0;
(ProjectEngine as unknown as ReturnType<typeof vi.fn>).mockImplementation(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(config: any) => ({
start: vi.fn().mockImplementation(async () => {
callCount++;
@@ -260,6 +262,7 @@ describe("ProjectEngineManager", () => {
const store = manager.getStore("proj_aaa");
expect(store).toBeDefined();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect((store as any).projectId).toBe("proj_aaa");
});