diff --git a/src/trpc/routers/architect.ts b/src/trpc/routers/architect.ts index b923269..faa217e 100644 --- a/src/trpc/routers/architect.ts +++ b/src/trpc/routers/architect.ts @@ -245,6 +245,36 @@ export function architectProcedures(publicProcedure: ProcedureBuilder) { }); } + // Auto-dismiss stale decompose agents for this phase + const allAgents = await agentManager.list(); + const decomposeAgents = allAgents.filter( + (a) => a.mode === 'decompose' && !a.userDismissedAt, + ); + + // Look up tasks to find which phase each decompose agent targets + const activeForPhase: typeof decomposeAgents = []; + const staleForPhase: typeof decomposeAgents = []; + for (const agent of decomposeAgents) { + if (!agent.taskId) continue; + const agentTask = await taskRepo.findById(agent.taskId); + if (agentTask?.phaseId !== input.phaseId) continue; + + if (['crashed', 'idle'].includes(agent.status)) { + staleForPhase.push(agent); + } else if (['running', 'waiting_for_input'].includes(agent.status)) { + activeForPhase.push(agent); + } + } + for (const stale of staleForPhase) { + await agentManager.dismiss(stale.id); + } + if (activeForPhase.length > 0) { + throw new TRPCError({ + code: 'CONFLICT', + message: `A decompose agent is already running for phase "${phase.name}"`, + }); + } + const decomposeTaskName = input.taskName ?? `Decompose: ${phase.name}`; const task = await taskRepo.create({ phaseId: phase.id,