feat(P04): pipeline stage delegation — EXECUTE=3 agents, TEST=tester, VERIFY=verifier, COMPLETE=doc-writer+ship

---ci---
phase: 4
milestone: v1.0
plan: 04
task: PIPE-01-04
status: execute
---/ci---
This commit is contained in:
Jon Chery
2026-05-29 18:13:39 +00:00
parent 6902c37ced
commit 4de1f65c10
+96 -18
View File
@@ -44,12 +44,13 @@ export class OrchestratorAgent extends BaseAgent {
private phaseResults: PhaseResult[] = []; private phaseResults: PhaseResult[] = [];
private totalPhases: number = 1; private totalPhases: number = 1;
private static readonly STAGE_AGENT_MAP: Partial<Record<PipelineStage, AgentName>> = { private static readonly STAGE_AGENT_MAP: Partial<Record<PipelineStage, AgentName[]>> = {
research: "researcher", research: ["researcher"],
plan: "planner", plan: ["planner"],
execute: "executor", execute: ["executor", "code-reviewer", "security-auditor"],
test: "tester", test: ["tester"],
verify: "verifier", verify: ["verifier"],
complete: ["doc-writer"],
}; };
constructor(config?: CIAgentConfig) { constructor(config?: CIAgentConfig) {
@@ -331,29 +332,82 @@ export class OrchestratorAgent extends BaseAgent {
context: AgentContext context: AgentContext
): Promise<PhaseResult> { ): Promise<PhaseResult> {
const stageStart = Date.now(); const stageStart = Date.now();
const agentName = OrchestratorAgent.STAGE_AGENT_MAP[stage]; const agentNames = OrchestratorAgent.STAGE_AGENT_MAP[stage];
if (agentName && context.backend) { if (agentNames && agentNames.length > 0 && context.backend) {
this.log(`Delegating ${stage} to ${agentName} agent via backend...`); this.log(`Delegating ${stage} to ${agentNames.join(", ")} agent(s) via backend...`);
try { try {
const agent = getAgent(agentName); let primaryResult: AgentResult | null = null;
const gitContext = this.buildGitAgentContext(context); const allArtifacts: string[] = [];
const result = await agent.execute(gitContext); 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 { return {
phase: this.pipelineState!.current_phase, phase: this.pipelineState!.current_phase,
stage, stage,
success: result.success, success: primaryResult?.success ?? false,
artifacts_created: Array.isArray(result.artifacts_created) ? result.artifacts_created : [], artifacts_created: allArtifacts,
decisions_made: result.decisions, decisions_made: totalDecisions,
escalations_raised: result.escalations, escalations_raised: totalEscalations,
duration_ms: Date.now() - stageStart, duration_ms: Date.now() - stageStart,
error: result.error, error: lastError,
}; };
} catch (err) { } catch (err) {
if (err instanceof BackendUnavailableError) { if (err instanceof BackendUnavailableError) {
this.warn(`Backend unavailable for ${stage}, falling back to mechanical execution`); this.warn(`Backend unavailable for ${stage}, falling back to mechanical execution`);
} else { } 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; break;
} }
} }