feat: Show detailing status in initiative overview and phase sidebar
Add 'detailing' activity state derived from active detail agents (mode=detail, status running/waiting_for_input). Initiative cards show pulsing "Detailing" indicator. Phase sidebar items show spinner during active detailing and "Review changes" when the agent finishes.
This commit is contained in:
@@ -5,7 +5,17 @@
|
||||
import type { Initiative, Phase } from '../../db/schema.js';
|
||||
import type { InitiativeActivity, InitiativeActivityState } from '@codewalk-district/shared';
|
||||
|
||||
export function deriveInitiativeActivity(initiative: Initiative, phases: Phase[]): InitiativeActivity {
|
||||
export interface ActiveDetailAgent {
|
||||
initiativeId: string;
|
||||
mode: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export function deriveInitiativeActivity(
|
||||
initiative: Initiative,
|
||||
phases: Phase[],
|
||||
activeDetailAgents?: ActiveDetailAgent[],
|
||||
): InitiativeActivity {
|
||||
const phasesTotal = phases.length;
|
||||
const phasesCompleted = phases.filter(p => p.status === 'completed').length;
|
||||
const base = { phasesTotal, phasesCompleted };
|
||||
@@ -43,5 +53,15 @@ export function deriveInitiativeActivity(initiative: Initiative, phases: Phase[]
|
||||
return { ...base, state: 'ready', activePhase: { id: approved.id, name: approved.name } };
|
||||
}
|
||||
|
||||
// Check for active detail agents (detailing trumps planning)
|
||||
const detailing = activeDetailAgents?.some(
|
||||
a => a.initiativeId === initiative.id
|
||||
&& a.mode === 'detail'
|
||||
&& (a.status === 'running' || a.status === 'waiting_for_input'),
|
||||
);
|
||||
if (detailing) {
|
||||
return { ...base, state: 'detailing' };
|
||||
}
|
||||
|
||||
return { ...base, state: 'planning' };
|
||||
}
|
||||
|
||||
@@ -68,17 +68,27 @@ export function initiativeProcedures(publicProcedure: ProcedureBuilder) {
|
||||
? await repo.findByStatus(input.status)
|
||||
: await repo.findAll();
|
||||
|
||||
// Fetch active detail agents once for all initiatives
|
||||
const allAgents = ctx.agentManager ? await ctx.agentManager.list() : [];
|
||||
const activeDetailAgents = allAgents
|
||||
.filter(a =>
|
||||
a.mode === 'detail'
|
||||
&& (a.status === 'running' || a.status === 'waiting_for_input')
|
||||
&& !a.userDismissedAt,
|
||||
)
|
||||
.map(a => ({ initiativeId: a.initiativeId ?? '', mode: a.mode ?? '', status: a.status }));
|
||||
|
||||
if (ctx.phaseRepository) {
|
||||
const phaseRepo = ctx.phaseRepository;
|
||||
return Promise.all(initiatives.map(async (init) => {
|
||||
const phases = await phaseRepo.findByInitiativeId(init.id);
|
||||
return { ...init, activity: deriveInitiativeActivity(init, phases) };
|
||||
return { ...init, activity: deriveInitiativeActivity(init, phases, activeDetailAgents) };
|
||||
}));
|
||||
}
|
||||
|
||||
return initiatives.map(init => ({
|
||||
...init,
|
||||
activity: deriveInitiativeActivity(init, []),
|
||||
activity: deriveInitiativeActivity(init, [], activeDetailAgents),
|
||||
}));
|
||||
}),
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { TRPCError } from '@trpc/server';
|
||||
import { z } from 'zod';
|
||||
import type { Task } from '../../db/schema.js';
|
||||
import type { ProcedureBuilder } from '../trpc.js';
|
||||
import { requirePhaseDispatchManager, requireTaskRepository } from './_helpers.js';
|
||||
import { requirePhaseDispatchManager, requirePhaseRepository, requireTaskRepository } from './_helpers.js';
|
||||
|
||||
export function phaseDispatchProcedures(publicProcedure: ProcedureBuilder) {
|
||||
return {
|
||||
@@ -18,6 +18,22 @@ export function phaseDispatchProcedures(publicProcedure: ProcedureBuilder) {
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
queueAllPhases: publicProcedure
|
||||
.input(z.object({ initiativeId: z.string().min(1) }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const phaseDispatchManager = requirePhaseDispatchManager(ctx);
|
||||
const phaseRepo = requirePhaseRepository(ctx);
|
||||
const phases = await phaseRepo.findByInitiativeId(input.initiativeId);
|
||||
let queued = 0;
|
||||
for (const phase of phases) {
|
||||
if (phase.status === 'approved') {
|
||||
await phaseDispatchManager.queuePhase(phase.id);
|
||||
queued++;
|
||||
}
|
||||
}
|
||||
return { success: true, queued };
|
||||
}),
|
||||
|
||||
dispatchNextPhase: publicProcedure
|
||||
.mutation(async ({ ctx }) => {
|
||||
const phaseDispatchManager = requirePhaseDispatchManager(ctx);
|
||||
|
||||
Reference in New Issue
Block a user