From 2e584fde19b35a2ea92f41074255971c6285249a Mon Sep 17 00:00:00 2001 From: Mikkel Georgsen Date: Wed, 1 Apr 2026 00:19:07 +0200 Subject: [PATCH] feat(08-02): add ensureGeneralistAgents startup migration for existing workspaces - Import agentService and agents table into server/src/index.ts - ensureGeneralistAgents() queries all companies, skips any that already have a general-role agent (idempotent), creates Generalist via agentService.create() - metadata includes pendingSkillGroups: [Creative] and backfilled: true flag - Called with fire-and-forget void pattern after ensureLocalTrustedBoardPrincipal - Existing workspaces get Generalist on next server upgrade without user action --- server/src/index.ts | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/server/src/index.ts b/server/src/index.ts index 91298f52..bdacf038 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -19,6 +19,7 @@ import { formatDatabaseBackupResult, runDatabaseBackup, authUsers, + agents, companies, companyMemberships, instanceUserRoles, @@ -251,6 +252,32 @@ export async function startServer(): Promise { } } + // [nexus] Backfill Generalist agent for existing workspaces that pre-date Phase 8 + async function ensureGeneralistAgents(db: any): Promise<{ backfilled: number }> { + const companyRows = await db.select({ id: companies.id }).from(companies); + let backfilled = 0; + for (const company of companyRows) { + const existing = await db + .select({ id: agents.id }) + .from(agents) + .where(and(eq(agents.companyId, company.id), eq(agents.role, "general"))) + .then((rows: Array<{ id: string }>) => rows[0] ?? null); + if (existing) continue; + const agentSvc = agentService(db); + await agentSvc.create(company.id, { + name: "Generalist", + role: "general", + adapterType: "claude_local", + adapterConfig: {}, + runtimeConfig: {}, + metadata: { pendingSkillGroups: ["Creative"], backfilled: true }, + }); + logger.info({ companyId: company.id }, "backfilled Generalist agent for existing workspace"); + backfilled++; + } + return { backfilled }; + } + let db; let embeddedPostgres: EmbeddedPostgresInstance | null = null; let embeddedPostgresStartedByThisProcess = false; @@ -467,6 +494,18 @@ export async function startServer(): Promise { if (config.deploymentMode === "local_trusted") { await ensureLocalTrustedBoardPrincipal(db as any); } + + // [nexus] Backfill Generalist agents for any workspace that pre-dates Phase 8 + void ensureGeneralistAgents(db as any) + .then((result) => { + if (result.backfilled > 0) { + logger.info({ backfilled: result.backfilled }, "backfilled Generalist agents for existing workspaces"); + } + }) + .catch((err) => { + logger.error({ err }, "failed to backfill Generalist agents"); + }); + if (config.deploymentMode === "authenticated") { const { createBetterAuthHandler,