feat: add Orchestrator Loop — phased plan execution with team agents

Adds a new autonomous loop that accepts high-level goals, generates
phased execution plans via AI, and executes them step-by-step with
verification gates between phases.

Core components:
- OrchestratorLoop: state machine (idle→planning→approval→executing→verifying→completed)
- OrchestratorPlanner: plan generation via PlanOrchestrator, Kahn's algorithm phase grouping
- OrchestratorVerifier: phase verification (strict/moderate/lenient modes)
- Prompt templates for phase execution, team delegation, verification, replanning

API (10 endpoints):
- POST start/approve/reject/pause/resume/stop
- GET status/plan
- POST phase/:id/skip, phase/:id/retry

Frontend: orchestrator-panel.js with SSE-driven state, phase progress, task tracking

Tests: 22 tests (18 route + 4 unit), all passing. Typecheck/lint/format clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-03-21 07:20:18 +01:00
co-authored by Claude Opus 4.6
parent 497ca4891a
commit 61b5ec095c
27 changed files with 4718 additions and 1 deletions
+2
View File
@@ -109,6 +109,8 @@ export interface AppState {
globalStats?: GlobalStats;
/** Daily token usage statistics */
tokenStats?: TokenStats;
/** Orchestrator Loop state (phased plan execution) */
orchestrator?: import('./orchestrator.js').OrchestratorPersistState;
}
// ========== Default Configuration ==========
+2
View File
@@ -26,6 +26,7 @@
* | teams | TeamConfig, TeamMember, TeamTask, InboxMessage, PaneInfo | `~/.claude/teams/`, `~/.claude/tasks/` → `GET /api/teams` |
* | push | PushSubscriptionRecord, VapidKeys | `~/.codeman/push-keys.json`, `~/.codeman/push-subscriptions.json` |
* | plan | PlanItem, PlanTaskStatus, TddPhase | In-memory → `GET /api/sessions/:id/plan/tasks` |
* | orchestrator | OrchestratorState, OrchestratorPlan, OrchestratorConfig, OrchestratorPersistState | `~/.codeman/state.json` → `GET /api/orchestrator/status` |
*
* ## Cross-domain relationship map
*
@@ -64,3 +65,4 @@ export * from './tools.js';
export * from './teams.js';
export * from './push.js';
export * from './plan.js';
export * from './orchestrator.js';
+285
View File
@@ -0,0 +1,285 @@
/**
* @fileoverview Orchestrator Loop type definitions.
*
* Types for the phased plan execution system: state machine, plan structure,
* phase grouping, team strategies, verification, configuration, and persistence.
*
* Key exports:
* - OrchestratorState — state machine states (idle → planning → approval → executing → verifying → ...)
* - OrchestratorPlan / OrchestratorPhase / OrchestratorTask — hierarchical plan structure
* - TeamStrategy — how agents are coordinated per phase (single, parallel, team)
* - VerificationResult / VerificationCheck — phase verification output
* - OrchestratorConfig — user-configurable options
* - OrchestratorPersistState / OrchestratorStats — persistence and metrics
*
* Cross-domain relationships:
* - OrchestratorTask.queueTaskId links to TaskState.id (task domain)
* - OrchestratorTask.assignedSessionId links to SessionState.id (session domain)
* - OrchestratorPersistState is embedded in AppState.orchestrator (app-state domain)
*
* Served at `GET /api/orchestrator/status` and `GET /api/orchestrator/plan`.
* No dependencies on other domain modules.
*/
// ═══════════════════════════════════════════════════════════════
// State Machine
// ═══════════════════════════════════════════════════════════════
/** Orchestrator loop states */
export type OrchestratorState =
| 'idle'
| 'planning'
| 'approval'
| 'executing'
| 'verifying'
| 'replanning'
| 'completed'
| 'failed'
| 'paused';
// ═══════════════════════════════════════════════════════════════
// Plan Structure
// ═══════════════════════════════════════════════════════════════
/** Top-level orchestrator plan generated from a user goal */
export interface OrchestratorPlan {
/** Unique plan identifier */
id: string;
/** Original user goal/task description */
goal: string;
/** When the plan was generated */
createdAt: number;
/** Ordered list of execution phases */
phases: OrchestratorPhase[];
/** Plan generation metadata */
metadata: OrchestratorPlanMetadata;
}
/** Metadata from plan generation */
export interface OrchestratorPlanMetadata {
/** Total tasks across all phases */
totalTasks: number;
/** Estimated overall complexity */
estimatedComplexity: 'low' | 'medium' | 'high';
/** Model used for plan generation */
modelUsed: string;
/** Time taken to generate the plan */
planDurationMs: number;
}
/** A sequential execution phase containing parallel tasks */
export interface OrchestratorPhase {
/** Phase identifier (e.g., "phase-1") */
id: string;
/** Human-readable phase name */
name: string;
/** Detailed description of what this phase accomplishes */
description: string;
/** Execution order (0-based) */
order: number;
/** Current phase status */
status: PhaseStatus;
/** Tasks within this phase */
tasks: OrchestratorTask[];
/** Criteria to verify after phase completion */
verificationCriteria: string[];
/** Shell commands to run for verification */
testCommands: string[];
/** Maximum retry attempts for this phase */
maxAttempts: number;
/** Current attempt count */
attempts: number;
/** When execution started */
startedAt: number | null;
/** When phase completed (passed or failed) */
completedAt: number | null;
/** Total execution duration */
durationMs: number | null;
/** How to coordinate agents for this phase */
teamStrategy: TeamStrategy;
}
/** Phase execution status */
export type PhaseStatus = 'pending' | 'executing' | 'verifying' | 'passed' | 'failed' | 'skipped';
/** A single executable task within a phase */
export interface OrchestratorTask {
/** Task identifier (e.g., "phase-1-task-1") */
id: string;
/** Parent phase identifier */
phaseId: string;
/** Single-line prompt to send to Claude */
prompt: string;
/** Current task status */
status: 'pending' | 'running' | 'completed' | 'failed';
/** Session running this task */
assignedSessionId: string | null;
/** Links to TaskQueue task ID (for completion tracking) */
queueTaskId: string | null;
/** Whether this task can run in parallel with siblings */
parallel: boolean;
/** Unique phrase for completion detection */
completionPhrase: string;
/** Timeout in milliseconds */
timeoutMs: number;
/** When task started executing */
startedAt: number | null;
/** When task completed */
completedAt: number | null;
/** Error message if task failed */
error: string | null;
/** Number of retry attempts */
retries: number;
}
// ═══════════════════════════════════════════════════════════════
// Team Strategy
// ═══════════════════════════════════════════════════════════════
/** How agents are coordinated for a phase */
export type TeamStrategy =
| { type: 'single' }
| { type: 'parallel'; maxSessions: number }
| { type: 'team'; config: TeamSetup };
/** Configuration for team-based phase execution */
export interface TeamSetup {
/** Prompt to send to the team lead */
leadPrompt: string;
/** Suggested teammate role descriptions */
suggestedTeammates: string[];
/** Maximum number of teammates to create */
maxTeammates: number;
}
// ═══════════════════════════════════════════════════════════════
// Verification
// ═══════════════════════════════════════════════════════════════
/** Result of phase verification */
export interface VerificationResult {
/** Whether all checks passed */
passed: boolean;
/** Individual verification checks */
checks: VerificationCheck[];
/** Human-readable summary */
summary: string;
/** Suggestions for replanning if verification failed */
suggestions: string[];
}
/** A single verification check result */
export interface VerificationCheck {
/** Type of check performed */
type: 'test_command' | 'ai_review' | 'file_check';
/** What was checked */
description: string;
/** Whether this check passed */
passed: boolean;
/** Command output or review text */
output?: string;
}
// ═══════════════════════════════════════════════════════════════
// Configuration
// ═══════════════════════════════════════════════════════════════
/** User-configurable orchestrator options */
export interface OrchestratorConfig {
/** Model to use for plan generation (default: 'opus') */
plannerModel: string;
/** Whether to run research agent before planning (default: true) */
researchEnabled: boolean;
/** Auto-approve generated plans without user review (default: false) */
autoApprove: boolean;
/** Maximum retry attempts per phase (default: 3) */
maxPhaseRetries: number;
/** Phase execution timeout in ms (default: 1800000 = 30min) */
phaseTimeoutMs: number;
/** Enable Claude Code agent teams for parallel phases (default: true) */
enableTeamAgents: boolean;
/** Maximum parallel sessions for task execution (default: 3) */
maxParallelSessions: number;
/** Verification strictness (default: 'moderate') */
verificationMode: 'strict' | 'moderate' | 'lenient';
/** Run /compact between phases to manage context (default: true) */
compactBetweenPhases: boolean;
}
/** Default orchestrator configuration */
export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
plannerModel: 'opus',
researchEnabled: true,
autoApprove: false,
maxPhaseRetries: 3,
phaseTimeoutMs: 30 * 60 * 1000, // 30 minutes
enableTeamAgents: true,
maxParallelSessions: 3,
verificationMode: 'moderate',
compactBetweenPhases: true,
};
// ═══════════════════════════════════════════════════════════════
// Persistence
// ═══════════════════════════════════════════════════════════════
/** Orchestrator state persisted to ~/.codeman/state.json */
export interface OrchestratorPersistState {
/** Current state machine state */
state: OrchestratorState;
/** Generated plan (null before planning) */
plan: OrchestratorPlan | null;
/** Index of currently executing phase */
currentPhaseIndex: number;
/** When orchestration started */
startedAt: number | null;
/** When orchestration completed */
completedAt: number | null;
/** User configuration */
config: OrchestratorConfig;
/** Execution statistics */
stats: OrchestratorStats;
}
/** Orchestrator execution statistics */
export interface OrchestratorStats {
/** Number of phases completed successfully */
phasesCompleted: number;
/** Number of phases that failed (after all retries) */
phasesFailed: number;
/** Total individual tasks completed */
totalTasksCompleted: number;
/** Total individual tasks failed */
totalTasksFailed: number;
/** Total time spent executing (ms) */
totalDurationMs: number;
/** Number of times replanning was triggered */
replanCount: number;
}
/** Factory function for initial orchestrator stats */
export function createInitialOrchestratorStats(): OrchestratorStats {
return {
phasesCompleted: 0,
phasesFailed: 0,
totalTasksCompleted: 0,
totalTasksFailed: 0,
totalDurationMs: 0,
replanCount: 0,
};
}
/** Factory function for initial orchestrator persist state */
export function createInitialOrchestratorPersistState(
config: OrchestratorConfig = DEFAULT_ORCHESTRATOR_CONFIG
): OrchestratorPersistState {
return {
state: 'idle',
plan: null,
currentPhaseIndex: 0,
startedAt: null,
completedAt: null,
config,
stats: createInitialOrchestratorStats(),
};
}