feat(P04): Add IDEATE stage to orchestrator pipeline — IDEATE-16
- Add ideation-agent to STAGE_AGENT_MAP for ideate stage - Implement ideate case in executeStage() with mechanical ideation, config-aware category filtering, idea deduplication, auto-accept, and ---ci--- commit with decision block - Add test verifying ideate position between research and plan in STAGE_ORDER - 542 tests passing
This commit is contained in:
@@ -47,6 +47,7 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
|
||||
private static readonly STAGE_AGENT_MAP: Partial<Record<PipelineStage, AgentName[]>> = {
|
||||
research: ["researcher"],
|
||||
ideate: ["ideation-agent"],
|
||||
plan: ["planner"],
|
||||
execute: ["executor", "code-reviewer", "security-auditor"],
|
||||
test: ["tester"],
|
||||
@@ -571,6 +572,64 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
break;
|
||||
}
|
||||
|
||||
case "ideate": {
|
||||
this.log("Running ideation stage...");
|
||||
const { IdeationEngine } = await import("../core/ideation.js");
|
||||
const ideationEngine = new IdeationEngine(context.project_path);
|
||||
const ideas = ideationEngine.runMechanical();
|
||||
|
||||
const ideationConfig = this.config.ideation;
|
||||
if (ideationConfig?.categories && ideationConfig.categories.length > 0) {
|
||||
const categoryIdeas = ideationEngine.runMechanical(ideationConfig.categories);
|
||||
const seenTitles = new Set(ideas.map((i) => i.title));
|
||||
for (const idea of categoryIdeas) {
|
||||
if (!seenTitles.has(idea.title)) {
|
||||
ideas.push(idea);
|
||||
seenTitles.add(idea.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ideas.sort((a, b) => b.confidence - a.confidence);
|
||||
|
||||
const maxIdeas = ideationConfig?.max_ideas || 20;
|
||||
const trimmedIdeas = ideas.slice(0, maxIdeas);
|
||||
|
||||
if (this.config.git.auto_commit && this.gitContext!.isGitRepo()) {
|
||||
const { accepted: savedIdeas, results } = ideationEngine.acceptIdeas(trimmedIdeas);
|
||||
const savedCount = results.filter((r) => r.addedToRequirements || r.addedToRoadmap).length;
|
||||
|
||||
const ideationCommit = CommitBuilder.buildDecisionCommit({
|
||||
phase: this.pipelineState!.current_phase,
|
||||
milestone: this.currentMilestone,
|
||||
subject: `ideation results — ${trimmedIdeas.length} total, ${savedCount} accepted`,
|
||||
decisions: savedIdeas.map((idea) => ({
|
||||
id: idea.id,
|
||||
decision: idea.title,
|
||||
rationale: idea.rationale,
|
||||
confidence: idea.confidence,
|
||||
alternatives: idea.actions,
|
||||
})),
|
||||
});
|
||||
|
||||
try {
|
||||
execSync(`git add -A && git commit -m "${ideationCommit.replace(/"/g, '\\"')}" --allow-empty`, {
|
||||
cwd: context.project_path,
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch (err) {
|
||||
this.warn(`Ideation commit failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
|
||||
artifactsCreated.push(".ciagent/REQUIREMENTS.md", ".ciagent/ROADMAP.md");
|
||||
decisionsMade += savedCount;
|
||||
}
|
||||
|
||||
this.pipelineState!.ideate_completed = true;
|
||||
this.log(`Ideation stage complete: ${trimmedIdeas.length} ideas generated`);
|
||||
break;
|
||||
}
|
||||
|
||||
case "plan":
|
||||
this.log("Planning phase execution...");
|
||||
|
||||
|
||||
@@ -63,3 +63,13 @@ describe("createInitialPipelineState", () => {
|
||||
expect(state.last_updated).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("STAGE_ORDER ideate position", () => {
|
||||
it("places ideate between research and plan", () => {
|
||||
const ideateIdx = STAGE_ORDER.indexOf("ideate");
|
||||
const researchIdx = STAGE_ORDER.indexOf("research");
|
||||
const planIdx = STAGE_ORDER.indexOf("plan");
|
||||
expect(ideateIdx).toBeGreaterThan(researchIdx);
|
||||
expect(ideateIdx).toBeLessThan(planIdx);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user