Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d58fd0bdde | |||
| 0799cfc644 | |||
| 70ee21856d |
@@ -169,6 +169,7 @@ export function createRunCommand(): Command {
|
|||||||
.option("--all", "Execute all remaining phases sequentially")
|
.option("--all", "Execute all remaining phases sequentially")
|
||||||
.option("--phase <number>", "Phase number", "1")
|
.option("--phase <number>", "Phase number", "1")
|
||||||
.option("--backend <provider>", "Override intelligence backend for this run")
|
.option("--backend <provider>", "Override intelligence backend for this run")
|
||||||
|
.option("--ideate", "Insert ideation stage between research and plan")
|
||||||
.action(async (phase, options) => {
|
.action(async (phase, options) => {
|
||||||
const projectPath = process.cwd();
|
const projectPath = process.cwd();
|
||||||
|
|
||||||
@@ -177,6 +178,49 @@ export function createRunCommand(): Command {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.ideate) {
|
||||||
|
console.log("─── CIAgent Ideate (pipeline mode) ───\n");
|
||||||
|
|
||||||
|
const ciFiles = new CIAgentFiles(projectPath);
|
||||||
|
const slug = ciFiles.getProjectSlug() || ciFiles.getActiveProject() || "default";
|
||||||
|
const { IdeationEngine } = await import("../core/ideation.js");
|
||||||
|
const engine = new IdeationEngine(projectPath, slug);
|
||||||
|
|
||||||
|
const ideas = engine.runMechanical();
|
||||||
|
|
||||||
|
const ideaCategory: IdeationCategory[] = options.category
|
||||||
|
? options.category.split(",").map((c: string) => c.trim() as IdeationCategory)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (ideaCategory.length > 0) {
|
||||||
|
const filtered = engine.runMechanical(ideaCategory);
|
||||||
|
ideas.push(...filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
const seen = new Set<string>();
|
||||||
|
const uniqueIdeas = ideas.filter((idea: Idea) => {
|
||||||
|
if (seen.has(idea.title)) return false;
|
||||||
|
seen.add(idea.title);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
uniqueIdeas.sort((a: Idea, b: Idea) => b.confidence - a.confidence);
|
||||||
|
|
||||||
|
console.log(`Found ${uniqueIdeas.length} improvement ${uniqueIdeas.length === 1 ? "idea" : "ideas"} from ideation stage.\n`);
|
||||||
|
|
||||||
|
if (uniqueIdeas.length > 0) {
|
||||||
|
const { accepted: savedIdeas, results } = engine.acceptIdeas(uniqueIdeas);
|
||||||
|
const savedCount = results.filter((r: { addedToRequirements: boolean; addedToRoadmap: boolean }) => r.addedToRequirements || r.addedToRoadmap).length;
|
||||||
|
|
||||||
|
if (savedCount > 0) {
|
||||||
|
console.log(`${savedCount} idea${savedCount === 1 ? "" : "s"} added to REQUIREMENTS.md and ROADMAP.md.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const commitMsg = `decision(P${options.phase || 1}): ideation results — ${uniqueIdeas.length} total, ${savedCount} accepted`;
|
||||||
|
console.log(`\nCommit suggestion: ${commitMsg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const config = loadConfig(projectPath);
|
const config = loadConfig(projectPath);
|
||||||
const { backend, error: backendError } = await resolveBackendForCommand(config, options.backend);
|
const { backend, error: backendError } = await resolveBackendForCommand(config, options.backend);
|
||||||
|
|
||||||
|
|||||||
+140
-1
@@ -2,7 +2,8 @@ import * as fs from "node:fs";
|
|||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import * as os from "node:os";
|
import * as os from "node:os";
|
||||||
import { IdeationAgent } from "../agents/ideation-agent.js";
|
import { IdeationAgent } from "../agents/ideation-agent.js";
|
||||||
import { IdeationEngine, resetIdeaCounter, Idea } from "../core/ideation.js";
|
import { IdeationEngine, resetIdeaCounter } from "../core/ideation.js";
|
||||||
|
import { Idea, IdeationAction, DEFAULT_IDEATION_CONFIG } from "../types/ideation.js";
|
||||||
|
|
||||||
describe("IdeationAgent", () => {
|
describe("IdeationAgent", () => {
|
||||||
let tempDir: string;
|
let tempDir: string;
|
||||||
@@ -244,4 +245,142 @@ describe("IdeationEngine", () => {
|
|||||||
expect(results.every((r) => r.addedToRequirements || r.addedToRoadmap)).toBe(true);
|
expect(results.every((r) => r.addedToRequirements || r.addedToRoadmap)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Phase 2: Backend-enriched and chaos", () => {
|
||||||
|
let tempDir: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
resetIdeaCounter();
|
||||||
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ciagent-p2-test-"));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runBackendEnriched prioritizes mechanical findings", () => {
|
||||||
|
const engine = new IdeationEngine(tempDir);
|
||||||
|
const mechanicalIdeas: Idea[] = [
|
||||||
|
{
|
||||||
|
id: "IDEATE-01",
|
||||||
|
source: "uncovered_requirement",
|
||||||
|
category: "coverage",
|
||||||
|
title: "Missing test",
|
||||||
|
rationale: "No test file",
|
||||||
|
confidence: 0.7,
|
||||||
|
actions: ["add_test"],
|
||||||
|
tier: "mechanical",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "IDEATE-02",
|
||||||
|
source: "escalation_pattern",
|
||||||
|
category: "security",
|
||||||
|
title: "Security issue",
|
||||||
|
rationale: "Repeated escalation",
|
||||||
|
confidence: 0.8,
|
||||||
|
actions: ["add_security_pattern"],
|
||||||
|
tier: "mechanical",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const enriched = engine.runBackendEnriched(mechanicalIdeas);
|
||||||
|
expect(enriched.length).toBeGreaterThanOrEqual(2);
|
||||||
|
const prioritizedIdeas = enriched.filter((i) => i.source === "uncovered_requirement" || i.source === "escalation_pattern");
|
||||||
|
expect(prioritizedIdeas.length).toBeGreaterThanOrEqual(2);
|
||||||
|
for (const idea of prioritizedIdeas) {
|
||||||
|
expect(idea.tier).toBe("backend-enriched");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runBackendEnriched adds novel suggestions for missing categories", () => {
|
||||||
|
const engine = new IdeationEngine(tempDir);
|
||||||
|
const mechanicalIdeas: Idea[] = [
|
||||||
|
{
|
||||||
|
id: "IDEATE-01",
|
||||||
|
source: "uncovered_requirement",
|
||||||
|
category: "coverage",
|
||||||
|
title: "Cover this",
|
||||||
|
rationale: "Missing",
|
||||||
|
confidence: 0.7,
|
||||||
|
actions: ["add_test"],
|
||||||
|
tier: "mechanical",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const enriched = engine.runBackendEnriched(mechanicalIdeas);
|
||||||
|
const novelIdeas = enriched.filter((i) => i.source === "improvement_pattern");
|
||||||
|
expect(novelIdeas.length).toBeGreaterThanOrEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("generateChaosScenarios uses default scenarios when enabled", () => {
|
||||||
|
const engine = new IdeationEngine(tempDir);
|
||||||
|
const chaosIdeas = engine.generateChaosScenarios();
|
||||||
|
|
||||||
|
expect(chaosIdeas.length).toBe(3);
|
||||||
|
expect(chaosIdeas.every((i) => i.source === "chaos_scenario")).toBe(true);
|
||||||
|
expect(chaosIdeas.every((i) => i.category === "chaos")).toBe(true);
|
||||||
|
expect(chaosIdeas.every((i) => i.tier === "backend-enriched")).toBe(true);
|
||||||
|
expect(chaosIdeas.every((i) => i.confidence >= 0.5)).toBe(true);
|
||||||
|
const titles = chaosIdeas.map((i) => i.title);
|
||||||
|
expect(titles.some((t) => t.includes("backend"))).toBe(true);
|
||||||
|
expect(titles.some((t) => t.includes("requirement"))).toBe(true);
|
||||||
|
expect(titles.some((t) => t.includes("coverage"))).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Phase 3: External signals and cascade impact", () => {
|
||||||
|
let tempDir: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
resetIdeaCounter();
|
||||||
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ciagent-p3-test-"));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runAffected detects cascade from architecture.md", () => {
|
||||||
|
const ciagentDir = path.join(tempDir, ".ciagent");
|
||||||
|
fs.mkdirSync(ciagentDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(ciagentDir, "config.json"),
|
||||||
|
JSON.stringify({ projects: [], active_project: "default" })
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(ciagentDir, "ARCHITECTURE.md"),
|
||||||
|
"# Architecture\n\n## Overview\n\nTest.\n\n## Components\n\n### CLI\n\n- **Description**: Command line interface\n- **Boundaries**: User-facing only\n- **Depends on**: Core\n\n### Core\n\n- **Description**: Core engine\n- **Boundaries**: Internal only\n- **Depends on**: None\n\n## Data Flow\n\nSimple.\n\n## Build Order\n\n1. CLI\n2. Core\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
const engine = new IdeationEngine(tempDir);
|
||||||
|
const ideas = engine.runAffected();
|
||||||
|
expect(Array.isArray(ideas)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runExternal handles missing npm gracefully", () => {
|
||||||
|
const ciagentDir = path.join(tempDir, ".ciagent");
|
||||||
|
fs.mkdirSync(ciagentDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(ciagentDir, "config.json"),
|
||||||
|
JSON.stringify({ projects: [], active_project: "default" })
|
||||||
|
);
|
||||||
|
|
||||||
|
const engine = new IdeationEngine(tempDir);
|
||||||
|
const ideas = engine.runExternal();
|
||||||
|
expect(Array.isArray(ideas)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runCrossProject returns empty when only one project", () => {
|
||||||
|
const ciagentDir = path.join(tempDir, ".ciagent");
|
||||||
|
fs.mkdirSync(ciagentDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(ciagentDir, "config.json"),
|
||||||
|
JSON.stringify({ projects: [{ slug: "default", name: "Default Project", default: true }], active_project: "default" })
|
||||||
|
);
|
||||||
|
|
||||||
|
const engine = new IdeationEngine(tempDir, "default");
|
||||||
|
const ideas = engine.runCrossProject();
|
||||||
|
expect(ideas).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
+178
-106
@@ -3,112 +3,18 @@ import * as path from "node:path";
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { CIAgentFiles } from "./ciagent-files.js";
|
import { CIAgentFiles } from "./ciagent-files.js";
|
||||||
import { GitContext } from "./git-context.js";
|
import { GitContext } from "./git-context.js";
|
||||||
|
import { loadConfig } from "./config.js";
|
||||||
export type IdeationSource =
|
import {
|
||||||
| "uncovered_requirement"
|
IdeationSource,
|
||||||
| "repeated_lesson"
|
IdeationCategory,
|
||||||
| "low_confidence_decision"
|
IdeationAction,
|
||||||
| "escalation_pattern"
|
IdeationTier,
|
||||||
| "compound_pattern"
|
Idea,
|
||||||
| "partial_requirement"
|
IdeationResult,
|
||||||
| "gap_in_coverage"
|
IdeationSummary,
|
||||||
| "improvement_pattern"
|
IdeationConfig,
|
||||||
| "architecture_drift"
|
DEFAULT_IDEATION_CONFIG,
|
||||||
| "verification_inversion"
|
} from "../types/ideation.js";
|
||||||
| "spec_ambiguity"
|
|
||||||
| "spec_contradiction"
|
|
||||||
| "spec_missing"
|
|
||||||
| "external_signal"
|
|
||||||
| "cross_project_lesson"
|
|
||||||
| "chaos_scenario";
|
|
||||||
|
|
||||||
export type IdeationCategory =
|
|
||||||
| "security"
|
|
||||||
| "quality"
|
|
||||||
| "architecture"
|
|
||||||
| "coverage"
|
|
||||||
| "improvement"
|
|
||||||
| "spec"
|
|
||||||
| "chaos";
|
|
||||||
|
|
||||||
export type IdeationAction =
|
|
||||||
| "add_requirement"
|
|
||||||
| "update_architecture"
|
|
||||||
| "update_roadmap"
|
|
||||||
| "fix_documentation"
|
|
||||||
| "add_test"
|
|
||||||
| "add_security_pattern"
|
|
||||||
| "refactor"
|
|
||||||
| "new_milestone_phase";
|
|
||||||
|
|
||||||
export type IdeationTier = "mechanical" | "backend-enriched" | "cross-project";
|
|
||||||
|
|
||||||
export interface Idea {
|
|
||||||
id: string;
|
|
||||||
source: IdeationSource;
|
|
||||||
category: IdeationCategory;
|
|
||||||
title: string;
|
|
||||||
rationale: string;
|
|
||||||
confidence: number;
|
|
||||||
relatedReq?: string;
|
|
||||||
actions: IdeationAction[];
|
|
||||||
tier: IdeationTier;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IdeationResult {
|
|
||||||
project: string;
|
|
||||||
milestone: string;
|
|
||||||
ideas: Idea[];
|
|
||||||
summary: IdeationSummary;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IdeationSummary {
|
|
||||||
total: number;
|
|
||||||
accepted: number;
|
|
||||||
skipped: number;
|
|
||||||
by_category: Record<string, number>;
|
|
||||||
by_tier: Record<string, number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IdeationConfig {
|
|
||||||
enabled: boolean;
|
|
||||||
categories: IdeationCategory[];
|
|
||||||
confidence_threshold: number;
|
|
||||||
max_ideas: number;
|
|
||||||
external_signals: {
|
|
||||||
npm_audit: boolean;
|
|
||||||
osv_advisories: boolean;
|
|
||||||
dependency_staleness: boolean;
|
|
||||||
};
|
|
||||||
cross_project: {
|
|
||||||
enabled: boolean;
|
|
||||||
similarity_weight: number;
|
|
||||||
};
|
|
||||||
chaos: {
|
|
||||||
enabled: boolean;
|
|
||||||
scenarios: string[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DEFAULT_IDEATION_CONFIG: IdeationConfig = {
|
|
||||||
enabled: true,
|
|
||||||
categories: ["security", "quality", "architecture", "coverage", "improvement"],
|
|
||||||
confidence_threshold: 0.6,
|
|
||||||
max_ideas: 20,
|
|
||||||
external_signals: {
|
|
||||||
npm_audit: true,
|
|
||||||
osv_advisories: true,
|
|
||||||
dependency_staleness: true,
|
|
||||||
},
|
|
||||||
cross_project: {
|
|
||||||
enabled: false,
|
|
||||||
similarity_weight: 0.5,
|
|
||||||
},
|
|
||||||
chaos: {
|
|
||||||
enabled: true,
|
|
||||||
scenarios: ["backend_unavailable", "requirement_change", "test_coverage_drop"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let ideaCounter = 0;
|
let ideaCounter = 0;
|
||||||
|
|
||||||
@@ -815,6 +721,172 @@ export class IdeationEngine {
|
|||||||
return ideas;
|
return ideas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runBackendEnriched(mechanicalIdeas: Idea[], context?: string): Idea[] {
|
||||||
|
resetIdeaCounter();
|
||||||
|
const enriched: Idea[] = [];
|
||||||
|
|
||||||
|
const prioritized = this.prioritizeMechanicalFindings(mechanicalIdeas);
|
||||||
|
enriched.push(...prioritized);
|
||||||
|
|
||||||
|
const novelSuggestions = this.suggestNovelImprovements(mechanicalIdeas);
|
||||||
|
enriched.push(...novelSuggestions);
|
||||||
|
|
||||||
|
const chaosScenarios = this.generateChaosScenarios();
|
||||||
|
enriched.push(...chaosScenarios);
|
||||||
|
|
||||||
|
return enriched.slice(0, DEFAULT_IDEATION_CONFIG.max_ideas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private prioritizeMechanicalFindings(mechanicalIdeas: Idea[]): Idea[] {
|
||||||
|
const categoryPriority: Record<string, number> = {
|
||||||
|
security: 1.0,
|
||||||
|
coverage: 0.9,
|
||||||
|
architecture: 0.8,
|
||||||
|
quality: 0.7,
|
||||||
|
improvement: 0.6,
|
||||||
|
spec: 0.5,
|
||||||
|
chaos: 0.4,
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceBoost: Record<string, number> = {
|
||||||
|
escalation_pattern: 0.15,
|
||||||
|
repeated_lesson: 0.1,
|
||||||
|
uncovered_requirement: 0.1,
|
||||||
|
compound_pattern: 0.08,
|
||||||
|
low_confidence_decision: 0.05,
|
||||||
|
architecture_drift: 0.05,
|
||||||
|
verification_inversion: 0.03,
|
||||||
|
};
|
||||||
|
|
||||||
|
return mechanicalIdeas
|
||||||
|
.map((idea) => ({
|
||||||
|
...idea,
|
||||||
|
confidence: Math.min(
|
||||||
|
idea.confidence + (categoryPriority[idea.category] || 0) * 0.1 + (sourceBoost[idea.source] || 0),
|
||||||
|
1.0
|
||||||
|
),
|
||||||
|
tier: "backend-enriched" as IdeationTier,
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.confidence - a.confidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
private suggestNovelImprovements(mechanicalIdeas: Idea[]): Idea[] {
|
||||||
|
const ideas: Idea[] = [];
|
||||||
|
const hasCategories = new Set(mechanicalIdeas.map((i) => i.category));
|
||||||
|
|
||||||
|
if (!hasCategories.has("security")) {
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "improvement_pattern",
|
||||||
|
category: "security",
|
||||||
|
title: "Consider adding security threat modeling",
|
||||||
|
rationale: "No security-related ideas were identified. Projects without explicit security analysis often have blind spots in threat modeling.",
|
||||||
|
confidence: 0.55,
|
||||||
|
actions: ["add_security_pattern"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasCategories.has("quality")) {
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "improvement_pattern",
|
||||||
|
category: "quality",
|
||||||
|
title: "Consider establishing quality baselines",
|
||||||
|
rationale: "No quality-related ideas were identified. Adding code quality baselines and review standards can prevent regressions.",
|
||||||
|
confidence: 0.50,
|
||||||
|
actions: ["add_test", "update_roadmap"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasCategories.has("architecture")) {
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "improvement_pattern",
|
||||||
|
category: "architecture",
|
||||||
|
title: "Review architectural boundaries and dependencies",
|
||||||
|
rationale: "No architecture drift detected, but periodic boundary review is a best practice for healthy codebases.",
|
||||||
|
confidence: 0.45,
|
||||||
|
actions: ["update_architecture"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ideas;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateChaosScenarios(): Idea[] {
|
||||||
|
resetIdeaCounter();
|
||||||
|
const ideas: Idea[] = [];
|
||||||
|
const config = this.getIdeationConfig();
|
||||||
|
|
||||||
|
if (!config.chaos.enabled) return ideas;
|
||||||
|
|
||||||
|
for (const scenario of config.chaos.scenarios) {
|
||||||
|
switch (scenario) {
|
||||||
|
case "backend_unavailable":
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "chaos_scenario",
|
||||||
|
category: "chaos",
|
||||||
|
title: "Resilience: Handle backend unavailability",
|
||||||
|
rationale: "What if the primary intelligence backend is unavailable? Add fallback paths and graceful degradation for all backend-dependent features.",
|
||||||
|
confidence: 0.7,
|
||||||
|
actions: ["add_requirement", "add_test"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "requirement_change":
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "chaos_scenario",
|
||||||
|
category: "chaos",
|
||||||
|
title: "Resilience: Handle mid-implementation requirement changes",
|
||||||
|
rationale: "What if requirements change during implementation? Design for adaptability with clear interfaces and minimal coupling.",
|
||||||
|
confidence: 0.65,
|
||||||
|
actions: ["add_requirement"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "test_coverage_drop":
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "chaos_scenario",
|
||||||
|
category: "chaos",
|
||||||
|
title: "Resilience: Prevent test coverage regression",
|
||||||
|
rationale: "What if test coverage drops below threshold? Add CI gates that enforce minimum coverage and alert on regression.",
|
||||||
|
confidence: 0.75,
|
||||||
|
actions: ["add_test", "update_roadmap"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ideas.push({
|
||||||
|
id: nextIdeaId(),
|
||||||
|
source: "chaos_scenario",
|
||||||
|
category: "chaos",
|
||||||
|
title: `Resilience: Handle ${scenario} scenario`,
|
||||||
|
rationale: `Consider adding resilience measures for the "${scenario}" failure scenario.`,
|
||||||
|
confidence: 0.5,
|
||||||
|
actions: ["add_requirement"],
|
||||||
|
tier: "backend-enriched",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ideas;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getIdeationConfig(): IdeationConfig {
|
||||||
|
try {
|
||||||
|
const config = loadConfig(this.projectPath);
|
||||||
|
return (config as any).ideation || DEFAULT_IDEATION_CONFIG;
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_IDEATION_CONFIG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
formatIdeas(ideas: Idea[]): string {
|
formatIdeas(ideas: Idea[]): string {
|
||||||
if (ideas.length === 0) return "No improvement ideas identified for this project.";
|
if (ideas.length === 0) return "No improvement ideas identified for this project.";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user