From 70ee21856d3bbbd3dc50e539c787f8b90b412eaf Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Sat, 30 May 2026 20:50:29 +0000 Subject: [PATCH] =?UTF-8?q?feat(P02):=20backend-enriched=20tier,=20chaos?= =?UTF-8?q?=20engineering,=20prioritization=20=E2=80=94=20IDEATE-04,05,06,?= =?UTF-8?q?09,10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- phase: 2 milestone: v0.10 status: execute decisions: - id: D-087 decision: All 6 innovative features in v1 (pattern mining, drift detection, layer inversion, cross-project, chaos, spec) rationale: User wants bleeding-edge; all uniquely differentiated confidence: 0.82 requirements: covered: - IDEATE-04 - IDEATE-05 - IDEATE-06 - IDEATE-09 - IDEATE-10 ---/ci--- - IDEATE-04: Verification layer inversion (structural, behavioral, security, quality missing detection) - IDEATE-05: Architectural drift detection (documented vs actual component comparison) - IDEATE-06: Spec-driven improvement (ambiguity detection, missing category detection) - IDEATE-09: Backend-enriched analysis (prioritization, novel suggestions, action plans) - IDEATE-10: Chaos engineering ideation (backend unavailable, requirement change, coverage drop) - Deduplicated type exports: IdeationSource/Idea/etc now in types/ideation.ts - 538 tests passing --- src/core/ideation.test.ts | 85 +++++++++++- src/core/ideation.ts | 284 ++++++++++++++++++++++++-------------- 2 files changed, 262 insertions(+), 107 deletions(-) diff --git a/src/core/ideation.test.ts b/src/core/ideation.test.ts index d04ff42..a43c9ce 100644 --- a/src/core/ideation.test.ts +++ b/src/core/ideation.test.ts @@ -2,7 +2,8 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; 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", () => { let tempDir: string; @@ -244,4 +245,86 @@ describe("IdeationEngine", () => { 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); + }); + }); }); \ No newline at end of file diff --git a/src/core/ideation.ts b/src/core/ideation.ts index 1124ef7..25417d4 100644 --- a/src/core/ideation.ts +++ b/src/core/ideation.ts @@ -3,112 +3,18 @@ import * as path from "node:path"; import { execSync } from "node:child_process"; import { CIAgentFiles } from "./ciagent-files.js"; import { GitContext } from "./git-context.js"; - -export type IdeationSource = - | "uncovered_requirement" - | "repeated_lesson" - | "low_confidence_decision" - | "escalation_pattern" - | "compound_pattern" - | "partial_requirement" - | "gap_in_coverage" - | "improvement_pattern" - | "architecture_drift" - | "verification_inversion" - | "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; - by_tier: Record; -} - -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"], - }, -}; +import { loadConfig } from "./config.js"; +import { + IdeationSource, + IdeationCategory, + IdeationAction, + IdeationTier, + Idea, + IdeationResult, + IdeationSummary, + IdeationConfig, + DEFAULT_IDEATION_CONFIG, +} from "../types/ideation.js"; let ideaCounter = 0; @@ -815,6 +721,172 @@ export class IdeationEngine { 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 = { + security: 1.0, + coverage: 0.9, + architecture: 0.8, + quality: 0.7, + improvement: 0.6, + spec: 0.5, + chaos: 0.4, + }; + + const sourceBoost: Record = { + 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 { if (ideas.length === 0) return "No improvement ideas identified for this project.";