When all phases complete, the initiative now transitions to pending_review status instead of silently stopping. The user reviews the full initiative diff and chooses: - Push Branch: push cw/<name> to remote for PR workflows - Merge & Push: merge into default branch and push Changes: - Schema: Add pending_review to initiative status enum - BranchManager: Add pushBranch port + SimpleGit adapter - Events: initiative:pending_review, initiative:review_approved - Orchestrator: checkInitiativeCompletion + approveInitiative - tRPC: getInitiativeReviewDiff, getInitiativeReviewCommits, getInitiativeCommitDiff, approveInitiativeReview - Frontend: InitiativeReview component in ReviewTab - Subscriptions: Add initiative events + missing preview/conversation event types and subscription procedures
80 lines
2.4 KiB
TypeScript
80 lines
2.4 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 ActiveArchitectAgent {
|
|
initiativeId: string;
|
|
mode: string;
|
|
status: string;
|
|
}
|
|
|
|
const MODE_TO_STATE: Record<string, InitiativeActivityState> = {
|
|
discuss: 'discussing',
|
|
plan: 'detailing',
|
|
detail: 'detailing',
|
|
refine: 'refining',
|
|
};
|
|
|
|
export function deriveInitiativeActivity(
|
|
initiative: Initiative,
|
|
phases: Phase[],
|
|
activeArchitectAgents?: ActiveArchitectAgent[],
|
|
): 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 === 'pending_review') {
|
|
return { ...base, state: 'pending_review' };
|
|
}
|
|
if (initiative.status === 'completed') {
|
|
return { ...base, state: 'complete' };
|
|
}
|
|
|
|
// Check for active architect agents BEFORE zero-phases check
|
|
// so architect agents (discuss/plan/detail/refine) surface activity
|
|
const activeAgent = activeArchitectAgents?.find(
|
|
a => a.initiativeId === initiative.id
|
|
&& (a.status === 'running' || a.status === 'waiting_for_input'),
|
|
);
|
|
if (activeAgent) {
|
|
const state = MODE_TO_STATE[activeAgent.mode] ?? 'detailing';
|
|
return { ...base, state };
|
|
}
|
|
|
|
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 } };
|
|
}
|
|
|
|
return { ...base, state: 'planning' };
|
|
}
|