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.
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
/**
|
|
* 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' };
|
|
}
|