/** * Initiative Activity — derives current activity state from initiative + phases. */ import type { Initiative, Phase } from '../../db/schema.js'; import type { InitiativeActivity, InitiativeActivityState } from '@codewalk-district/shared'; 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 }; if (initiative.status === 'archived') { return { ...base, state: 'archived' }; } if (initiative.status === 'completed') { return { ...base, state: 'complete' }; } if (phasesTotal === 0) { return { ...base, state: 'idle' }; } // Priority-ordered state detection (first match wins) const priorities: Array<{ status: Phase['status']; state: InitiativeActivityState }> = [ { status: 'pending_review', state: 'pending_review' }, { status: 'in_progress', state: 'executing' }, { status: 'blocked', state: 'blocked' }, ]; for (const { status, state } of priorities) { const match = phases.find(p => p.status === status); if (match) { return { ...base, state, activePhase: { id: match.id, name: match.name } }; } } if (phasesCompleted === phasesTotal) { return { ...base, state: 'complete' }; } const approved = phases.find(p => p.status === 'approved'); if (approved) { 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' }; }