From 4de1f65c103244c228f3a0b40747677768566c98 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Fri, 29 May 2026 18:13:39 +0000 Subject: [PATCH] =?UTF-8?q?feat(P04):=20pipeline=20stage=20delegation=20?= =?UTF-8?q?=E2=80=94=20EXECUTE=3D3=20agents,=20TEST=3Dtester,=20VERIFY=3Dv?= =?UTF-8?q?erifier,=20COMPLETE=3Ddoc-writer+ship?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- phase: 4 milestone: v1.0 plan: 04 task: PIPE-01-04 status: execute ---/ci--- --- src/agents/orchestrator.ts | 114 +++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 18 deletions(-) diff --git a/src/agents/orchestrator.ts b/src/agents/orchestrator.ts index 771cada..9cda11c 100644 --- a/src/agents/orchestrator.ts +++ b/src/agents/orchestrator.ts @@ -44,12 +44,13 @@ export class OrchestratorAgent extends BaseAgent { private phaseResults: PhaseResult[] = []; private totalPhases: number = 1; - private static readonly STAGE_AGENT_MAP: Partial> = { - research: "researcher", - plan: "planner", - execute: "executor", - test: "tester", - verify: "verifier", + private static readonly STAGE_AGENT_MAP: Partial> = { + research: ["researcher"], + plan: ["planner"], + execute: ["executor", "code-reviewer", "security-auditor"], + test: ["tester"], + verify: ["verifier"], + complete: ["doc-writer"], }; constructor(config?: CIAgentConfig) { @@ -331,29 +332,82 @@ export class OrchestratorAgent extends BaseAgent { context: AgentContext ): Promise { const stageStart = Date.now(); - const agentName = OrchestratorAgent.STAGE_AGENT_MAP[stage]; + const agentNames = OrchestratorAgent.STAGE_AGENT_MAP[stage]; - if (agentName && context.backend) { - this.log(`Delegating ${stage} to ${agentName} agent via backend...`); + if (agentNames && agentNames.length > 0 && context.backend) { + this.log(`Delegating ${stage} to ${agentNames.join(", ")} agent(s) via backend...`); try { - const agent = getAgent(agentName); - const gitContext = this.buildGitAgentContext(context); - const result = await agent.execute(gitContext); + let primaryResult: AgentResult | null = null; + const allArtifacts: string[] = []; + let totalDecisions = 0; + let totalEscalations = 0; + let lastError: string | undefined; + + for (let i = 0; i < agentNames.length; i++) { + const agentName = agentNames[i]; + const agent = getAgent(agentName); + const gitContext = this.buildGitAgentContext(context); + + if (i === 0) { + const result = await agent.execute(gitContext); + primaryResult = result; + if (Array.isArray(result.artifacts_created)) { + allArtifacts.push(...result.artifacts_created); + } + totalDecisions += result.decisions; + totalEscalations += result.escalations; + + if (!result.success) { + this.warn(`Primary agent ${agentName} failed for ${stage}`); + return { + phase: this.pipelineState!.current_phase, + stage, + success: false, + artifacts_created: allArtifacts, + decisions_made: totalDecisions, + escalations_raised: totalEscalations, + duration_ms: Date.now() - stageStart, + error: result.error || `Primary agent ${agentName} failed`, + }; + } + } else { + try { + const reviewContext: AgentContext = { + ...gitContext, + specification: `${context.specification}\n\nPrimary agent (${agentNames[0]}) completed. Review context:\n- Success: ${primaryResult!.success}\n- Output: ${primaryResult!.output}\n- Artifacts: ${Array.isArray(primaryResult!.artifacts_created) ? primaryResult!.artifacts_created.join(", ") : String(primaryResult!.artifacts_created)}`, + }; + const result = await agent.execute(reviewContext); + if (Array.isArray(result.artifacts_created)) { + allArtifacts.push(...result.artifacts_created); + } + totalDecisions += result.decisions; + totalEscalations += result.escalations; + + if (!result.success) { + this.warn(`Review agent ${agentName} reported issues for ${stage}: ${result.error || "unspecified"}`); + lastError = result.error; + } + } catch (err) { + this.warn(`Review agent ${agentName} failed for ${stage}: ${err instanceof Error ? err.message : String(err)}`); + } + } + } + return { phase: this.pipelineState!.current_phase, stage, - success: result.success, - artifacts_created: Array.isArray(result.artifacts_created) ? result.artifacts_created : [], - decisions_made: result.decisions, - escalations_raised: result.escalations, + success: primaryResult?.success ?? false, + artifacts_created: allArtifacts, + decisions_made: totalDecisions, + escalations_raised: totalEscalations, duration_ms: Date.now() - stageStart, - error: result.error, + error: lastError, }; } catch (err) { if (err instanceof BackendUnavailableError) { this.warn(`Backend unavailable for ${stage}, falling back to mechanical execution`); } else { - this.warn(`Agent ${agentName} failed for ${stage}: ${err instanceof Error ? err.message : String(err)}`); + this.warn(`Agents failed for ${stage}: ${err instanceof Error ? err.message : String(err)}`); } } } @@ -609,6 +663,30 @@ export class OrchestratorAgent extends BaseAgent { } } + const versionTag = `${this.currentMilestone}-P${String(this.pipelineState!.current_phase).padStart(2, "0")}`; + try { + execSync(`git tag "${versionTag}"`, { + cwd: context.project_path, + stdio: "pipe", + }); + this.log(`Created version tag: ${versionTag}`); + artifactsCreated.push(`tag:${versionTag}`); + } catch (err) { + this.warn(`Version tag creation failed: ${err instanceof Error ? err.message : String(err)}`); + } + + if (this.config.git.auto_push && this.gitContext!.isGitRepo()) { + try { + execSync(`git push origin ${versionTag}`, { + cwd: context.project_path, + stdio: "pipe", + }); + this.log(`Pushed version tag: ${versionTag}`); + } catch (err) { + this.warn(`Version tag push failed: ${err instanceof Error ? err.message : String(err)}`); + } + } + break; } }