fix(dashboard): start engine for every registered project on first access

Tasks in projects other than the primary (cwd) project were never triaged
because only one ProjectEngine was started. When a project is accessed via
?projectId= API/SSE, getOrCreateProjectStore created a TaskStore but left
the Scheduler, TriageProcessor, and TaskExecutor unstarted.

Fix: introduce setOnProjectFirstCreated callback in project-store-resolver
so the dashboard server is notified when any new project is first accessed.
dashboard.ts creates a ProjectManager that lazily starts an InProcessRuntime
(Scheduler + TriageProcessor + TaskExecutor) for each project the first time
it is accessed — works for any number of registered projects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-12 21:58:19 -07:00
parent bb5b5266f2
commit 425f398e15
5 changed files with 568 additions and 831 deletions

View File

@@ -37,6 +37,21 @@ const pendingCreations = new Map<string, Promise<TaskStore>>();
*/
const initializedProjects = new Set<string>();
/**
* Optional callback invoked once when a new project store is first created.
* Used by the dashboard server to lazily start an engine for secondary projects.
*/
let _onProjectFirstCreated: ((projectId: string) => void) | undefined;
/**
* Register a callback to be called once when a new project is first accessed.
* The callback fires after the store is cached — exactly once per projectId.
* Pass `undefined` to clear the callback.
*/
export function setOnProjectFirstCreated(cb: ((projectId: string) => void) | undefined): void {
_onProjectFirstCreated = cb;
}
/**
* Get or create a cached TaskStore for the given projectId.
*
@@ -81,6 +96,12 @@ export async function getOrCreateProjectStore(projectId: string): Promise<TaskSt
storeCache.set(projectId, store);
pendingCreations.delete(projectId);
// Notify once that a new project was first accessed
if (_onProjectFirstCreated) {
_onProjectFirstCreated(projectId);
}
return store;
})();