/**
 * Phase 2 Pipeline -- Stage 3: SPARC Specification
 *
 * Generates all 5 SPARC phases (Specification, Pseudocode, Architecture,
 * Refinement, Completion) as natural-language markdown documents following
 * the ruvnet/sparc methodology (https://github.com/ruvnet/sparc).
 *
 * Content is WRITTEN dynamically from the user's original prompt and
 * Phase 1/2 artifacts — NOT pattern-matched from hardcoded templates.
 *
 * The markdown documents are the primary output. A sparc-combined.json
 * compatibility shim is also written for downstream Phase 4/5 consumers.
 *
 * Error codes:
 *   ECLI-P2-010  Research dossier not available on context
 *   ECLI-P2-011  SPARC file write failure
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { createSpan, endSpan, emitSpan } from '../telemetry.js';
import { normalizeContextName } from './ddd-generator.js';
import { createClaudeCodeRunner, isClaudeCodeRunnerAvailable } from '../../../runtime/claude-code-runner.js';
// ============================================================================
// Constants
// ============================================================================
const DIR_MODE = 0o700;
const FILE_MODE = 0o600;
const SPARC_SUBDIR = 'sparc';
// ============================================================================
// Intelligence / ML Detection
// ============================================================================
/**
 * Detect whether the user's query requires intelligence/ML/analytics contexts.
 * Returns keywords found and suggested intelligence context names.
 */
export function detectIntelligenceRequirements(query) {
    const lowerQuery = query.toLowerCase();
    const keywords = [];
    const patterns = [
        [/\b(?:ai|artificial[\s-]?intelligence|machine[\s-]?learning|ml)\b/i, 'AI/ML'],
        [/\b(?:predict|prediction|predictive)\b/i, 'prediction'],
        [/\b(?:anomal|outlier|deviation[\s-]?detect)\b/i, 'anomaly-detection'],
        [/\b(?:pattern[\s-]?recogni|pattern[\s-]?detect|pattern[\s-]?analys)\b/i, 'pattern-recognition'],
        [/\b(?:forecast|forecasting|time[\s-]?series)\b/i, 'forecasting'],
        [/\b(?:classif|categoriz|cluster)\b/i, 'classification'],
        [/\b(?:neural[\s-]?net|deep[\s-]?learn|transformer|llm|nlp)\b/i, 'deep-learning'],
        [/\b(?:recommend|suggestion[\s-]?engine|collaborative[\s-]?filter)\b/i, 'recommendation'],
        [/\b(?:sentiment[\s-]?analy|text[\s-]?mining|natural[\s-]?language)\b/i, 'nlp'],
        [/\b(?:computer[\s-]?vision|image[\s-]?recogni|object[\s-]?detect)\b/i, 'vision'],
        [/\b(?:drift|model[\s-]?degradat|concept[\s-]?drift)\b/i, 'drift-detection'],
        [/\b(?:feature[\s-]?engineer|feature[\s-]?extract|feature[\s-]?store)\b/i, 'feature-engineering'],
        [/\b(?:train|training[\s-]?data|training[\s-]?pipeline|model[\s-]?train)\b/i, 'model-training'],
        [/\b(?:data[\s-]?pipeline|etl|data[\s-]?ingest|data[\s-]?lake)\b/i, 'data-pipeline'],
        [/\b(?:analytics|analytic|statistical[\s-]?analys|data[\s-]?analys)\b/i, 'analytics'],
        [/\b(?:historical[\s-]?data|historical[\s-]?pattern|trend[\s-]?analys)\b/i, 'historical-analysis'],
    ];
    for (const [pat, keyword] of patterns) {
        if (pat.test(lowerQuery))
            keywords.push(keyword);
    }
    if (keywords.length === 0)
        return { hasIntelligence: false, keywords: [], suggestedContexts: [] };
    // Build suggested contexts based on detected keywords
    const suggestedContexts = [];
    if (keywords.some(k => ['anomaly-detection', 'drift-detection', 'pattern-recognition'].includes(k))) {
        suggestedContexts.push({
            name: 'anomaly-detection-engine',
            type: 'anomaly-detection',
            commands: ['DetectAnomalies', 'TrainDetectionModel', 'UpdateBaseline', 'GetAnomalyReport', 'ConfigureThresholds'],
            description: 'Anomaly detection and pattern recognition engine using statistical and ML models',
        });
    }
    if (keywords.some(k => ['prediction', 'forecasting', 'time-series'].includes(k))) {
        suggestedContexts.push({
            name: 'forecasting-engine',
            type: 'forecasting',
            commands: ['RunForecast', 'TrainForecastModel', 'EvaluateModelAccuracy', 'GetForecastResults', 'UpdateHistoricalData'],
            description: 'Time-series forecasting and predictive analytics engine',
        });
    }
    if (keywords.some(k => ['AI/ML', 'deep-learning', 'classification', 'model-training'].includes(k))) {
        suggestedContexts.push({
            name: 'ml-inference-engine',
            type: 'ml-inference',
            commands: ['RunInference', 'TrainModel', 'DeployModel', 'EvaluateModel', 'GetModelMetrics', 'RetireModel'],
            description: 'Machine learning model lifecycle management and inference engine',
        });
    }
    if (keywords.some(k => ['analytics', 'historical-analysis'].includes(k))) {
        suggestedContexts.push({
            name: 'analytics-engine',
            type: 'analytics',
            commands: ['RunAnalysis', 'GenerateReport', 'ComputeMetrics', 'QueryHistoricalData', 'BuildDashboard'],
            description: 'Analytics and business intelligence engine for historical data analysis',
        });
    }
    if (keywords.some(k => ['data-pipeline', 'feature-engineering'].includes(k))) {
        suggestedContexts.push({
            name: 'data-pipeline',
            type: 'data-pipeline',
            commands: ['IngestData', 'TransformData', 'ExtractFeatures', 'ValidateData', 'SchedulePipeline'],
            description: 'Data ingestion, transformation, and feature engineering pipeline',
        });
    }
    if (keywords.some(k => ['recommendation', 'nlp', 'vision'].includes(k))) {
        suggestedContexts.push({
            name: 'ml-inference-engine',
            type: 'ml-inference',
            commands: ['RunInference', 'TrainModel', 'DeployModel', 'EvaluateModel', 'GetModelMetrics', 'RetireModel'],
            description: 'Machine learning model lifecycle management and inference engine',
        });
    }
    // If we detected intelligence keywords but no specific context matched, add a generic one
    if (suggestedContexts.length === 0) {
        suggestedContexts.push({
            name: 'intelligence-engine',
            type: 'analytics',
            commands: ['AnalyzeData', 'DetectPatterns', 'GenerateInsights', 'RunModel', 'GetResults'],
            description: 'Intelligence and analytics engine for data-driven insights',
        });
    }
    // Deduplicate by name
    const seen = new Set();
    const deduped = suggestedContexts.filter(c => {
        if (seen.has(c.name))
            return false;
        seen.add(c.name);
        return true;
    });
    return { hasIntelligence: true, keywords, suggestedContexts: deduped };
}
// ============================================================================
// Helpers
// ============================================================================
/**
 * Load agentics fleet report from phase2Dir and format for LLM context.
 * Returns a string summary of agent findings, or empty string if unavailable.
 */
function loadAgenticsFleetContext(phase2Dir) {
    try {
        // Check phase dir first, then .pre-phaseN (written before phase command runs)
        let reportPath = path.join(phase2Dir, 'agentics-agents-report.json');
        if (!fs.existsSync(reportPath)) {
            const prePhaseDir = path.join(path.dirname(phase2Dir), `.pre-${path.basename(phase2Dir)}`);
            reportPath = path.join(prePhaseDir, 'agentics-agents-report.json');
        }
        if (!fs.existsSync(reportPath))
            return '';
        const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
        if (!report.results || report.results.length === 0)
            return '';
        const successResults = report.results.filter(r => r.status === 200);
        if (successResults.length === 0)
            return '';
        const agentSummaries = successResults.slice(0, 15).map(r => {
            const resp = r.response;
            const data = resp?.['data'];
            // Extract meaningful content from agent response
            let finding = '';
            if (data) {
                // Common agent response shapes
                const plan = data['plan'];
                const analysis = data['analysis'];
                const hypothesis = data['hypothesis'];
                const metrics = data['metrics'];
                const recommendations = data['recommendations'];
                if (plan?.['steps']) {
                    const steps = plan['steps'].slice(0, 3);
                    finding = steps.map(s => s['title'] || s['description'] || '').join('; ');
                }
                else if (analysis?.['summary'] || analysis?.['findings']) {
                    finding = String(analysis['summary'] || analysis['findings'] || '').slice(0, 200);
                }
                else if (hypothesis?.['statement']) {
                    finding = `Hypothesis: ${hypothesis['statement']}`;
                }
                else if (metrics?.['kpis']) {
                    const kpis = metrics['kpis'].slice(0, 3);
                    finding = kpis.map(k => `${k['name']}: ${k['value']} ${k['unit'] || ''}`).join('; ');
                }
                else if (recommendations) {
                    finding = recommendations.slice(0, 3)
                        .map(r2 => String(r2['recommendation'] || r2['title'] || r2)).join('; ');
                }
                else {
                    // Generic extraction: take first few string values
                    const vals = Object.values(data).filter(v => typeof v === 'string').slice(0, 2);
                    finding = vals.map(v => String(v).slice(0, 100)).join('; ');
                }
            }
            return `- [${r.domain}/${r.agent}] ${r.role}: ${finding || 'analysis completed'}`;
        }).join('\n');
        return `\nAGENTICS FLEET ANALYSIS (${successResults.length} agents from 27 domains):\n${agentSummaries}\n`;
    }
    catch {
        return '';
    }
}
function pad3(n) {
    return String(n).padStart(3, '0');
}
// ============================================================================
// Complete SPARC Generation (single LLM call)
//
// SPARC is a high-level architecture document with 5 sections:
//   Specification, Pseudocode, Architecture, Refinement, Completion
// Each section should be deep, domain-specific, and cohesive — not
// disconnected template fragments. See docs/SPARC-reference.md for examples.
// ============================================================================
/**
 * Generate the ENTIRE SPARC document in one LLM call.
 * This produces a cohesive architecture document like the PowerInfer example
 * in docs/SPARC-reference.md — deep domain-specific content, not templates.
 */
function tryGenerateCompleteSPARC(query, projectContext, dossier, artifacts, phase2Dir) {
    if (!isClaudeCodeRunnerAvailable())
        return null;
    try {
        const runner = createClaudeCodeRunner();
        // Build rich context from dossier + agents
        const dossierItems = dossier.items.slice(0, 40)
            .map(i => `- [${i.category}] ${i.title}: ${i.content.slice(0, 200)}`)
            .join('\n');
        const agentContext = loadAgenticsFleetContext(phase2Dir);
        // Extract domain services for the prompt
        const domainServices = extractDomainServices(query, artifacts, dossier);
        const servicesList = domainServices.map(svc => `- ${svc.name}: ${svc.responsibility} (capabilities: ${svc.capabilities.join(', ')}; systems: ${svc.relatedSystems.join(', ') || 'none'})`).join('\n');
        // Detect tech stack
        const allText = (query + ' ' + projectContext).toLowerCase();
        const techStack = [];
        const techPatterns = [
            [/\bsap\b/i, 'SAP'], [/\bsnowflake\b/i, 'Snowflake'], [/\bsalesforce\b/i, 'Salesforce'],
            [/\baws\b/i, 'AWS'], [/\bazure\b/i, 'Azure'], [/\bgcp\b|google cloud/i, 'GCP'],
            [/\bkafka\b/i, 'Kafka'], [/\bpostgres/i, 'PostgreSQL'], [/\bmongodb\b/i, 'MongoDB'],
            [/\bredis\b/i, 'Redis'], [/\bkubernetes\b|\bk8s\b/i, 'Kubernetes'],
            [/\boracle\b/i, 'Oracle'], [/\bnetsuite\b/i, 'NetSuite'], [/\bworkday\b/i, 'Workday'],
        ];
        for (const [pat, name] of techPatterns) {
            if (pat.test(allText))
                techStack.push(name);
        }
        // Detect intelligence/ML requirements
        const intelligenceReqs = detectIntelligenceRequirements(query);
        let intelligenceGuidance = '';
        if (intelligenceReqs.hasIntelligence) {
            intelligenceGuidance = '\n=== INTELLIGENCE/ML REQUIREMENTS ===\n' +
                `Detected keywords: ${intelligenceReqs.keywords.join(', ')}\n` +
                'The following intelligence contexts MUST be included in the architecture:\n' +
                intelligenceReqs.suggestedContexts.map(c => `- ${c.name} (${c.type}): ${c.description}\n  Commands: ${c.commands.join(', ')}\n  NOTE: This is NOT a CRUD context. It must contain ML/analytics logic, not Create/Update/Delete operations.`).join('\n') +
                '\n\nCRITICAL: Intelligence contexts must have commands like TrainModel, RunInference, DetectAnomalies — NOT generic Create*, Update*, Delete*.\n';
        }
        const prompt = `You are a senior enterprise architect creating a complete SPARC architecture document.\n` +
            `\nSPARC is a high-level architecture methodology with 5 sections: Specification, Pseudocode, Architecture, Refinement, Completion.\n` +
            `Each section must be DEEPLY domain-specific — not generic templates.\n` +
            `\n=== PROJECT ===\n${query}\n` +
            `\n=== TECHNOLOGY STACK ===\n${techStack.join(', ') || 'To be determined based on domain requirements'}\n` +
            `\n=== PROJECT CONTEXT ===\n${projectContext.slice(0, 3000)}\n` +
            `\n=== RESEARCH FINDINGS (${dossier.items.length} items from 110 agents) ===\n${dossierItems}\n` +
            (agentContext ? `\n${agentContext}` : '') +
            (intelligenceGuidance ? `\n${intelligenceGuidance}` : '') +
            `\n=== DOMAIN SERVICES ===\n${servicesList}\n` +
            `\nWrite the COMPLETE SPARC document with these 5 sections. Use the section headers EXACTLY as shown:\n` +
            `\n# Specification\n` +
            `Deep domain analysis: goals and motivation, supported features/model classes, specific performance/latency/memory targets with numbers, ` +
            `compatibility and integration requirements with actual system APIs (e.g. real SAP API names, real Snowflake SQL interfaces). ` +
            `Include functional requirements with acceptance criteria, non-functional requirements with specific numbers, ` +
            `stakeholder roles specific to this domain (not generic "Employees"), and system integration details.\n` +
            `\n# Pseudocode\n` +
            `High-level pseudocode showing the ACTUAL algorithms, data structures, and control flow. ` +
            `Include typed struct/class definitions with real field names and types. ` +
            `Show function signatures with typed parameters. Show the REAL business logic — calculations, validations, decision trees, state machines. ` +
            `NOT generic "validate_input → process → persist" templates. ` +
            `Use \`\`\`pseudocode blocks for code.\n` +
            `\n# Architecture\n` +
            `System architecture: component design, data flow diagrams, module interactions, technology stack decisions with rationale. ` +
            `Include crate/package organization, backend implementations, data models and schemas, ` +
            `key architectural patterns (why microservices vs monolith, why event sourcing vs CRUD, etc.), ` +
            `scalability strategy, security architecture, and integration architecture with actual external system APIs.\n` +
            `\n# Refinement\n` +
            `Optimization and hardening: quantization/performance support, format compatibility details, ` +
            `fallback strategies, SIMD/parallel optimizations, memory layout, cache locality, ` +
            `quality vs performance trade-offs with specific tuning parameters, security hardening, ` +
            `edge case handling specific to this domain.\n` +
            `\n# Completion\n` +
            `Integration plan: how components connect end-to-end, testing plan (unit/integration/e2e with domain-specific test cases), ` +
            `benchmark targets with specific numbers, deployment strategy, monitoring and observability, ` +
            `model/system-specific adjustments, post-deployment validation.\n` +
            `\nCRITICAL RULES:\n` +
            `1. Every section must be DEEPLY domain-specific to "${query.slice(0, 100)}"\n` +
            `2. Include REAL API names, REAL data structures, REAL algorithms — not placeholders\n` +
            `3. Pseudocode must have typed structs and real function logic, not "validate→execute→persist"\n` +
            `4. Architecture must describe actual component interactions, not "User → API → DB"\n` +
            `5. Use the research findings and agent analysis above as your primary source of domain knowledge\n` +
            `6. Each section should be 500-2000 words of substantive content\n` +
            `7. This is a HIGH-LEVEL ARCHITECTURE document — not a process description`;
        const result = runner.invoke(prompt, {
            runId: `sparc-complete-${Date.now()}`,
            timeoutMs: 60_000, // 60s — LLM SPARC is much better than template fallback, worth waiting for
        });
        const data = result.data;
        const content = typeof data.content === 'string' ? data.content : '';
        // Validate: must have all 5 sections and substantial content
        if (content.length < 3000) {
            process.stderr.write(`[sparc] Using deterministic SPARC generator\n`);
            return null;
        }
        const hasSections = ['# Specification', '# Pseudocode', '# Architecture', '# Refinement', '# Completion']
            .every(h => content.includes(h));
        if (!hasSections) {
            process.stderr.write('[sparc] Using deterministic SPARC generator\n');
            return null;
        }
        process.stderr.write(`[sparc] LLM-generated complete SPARC: ${content.length} chars in ${result.durationMs}ms\n`);
        return content;
    }
    catch (err) {
        process.stderr.write(`[sparc] Complete SPARC generation failed: ${err instanceof Error ? err.message : String(err)}\n`);
        return null;
    }
}
/**
 * Split a complete SPARC document into its 5 sections.
 * Looks for "# Specification", "# Pseudocode", "# Architecture", "# Refinement", "# Completion"
 * as top-level headers.
 */
function splitSPARCSections(fullDoc) {
    const sectionPattern = /^# (Specification|Pseudocode|Architecture|Refinement|Completion)\b/gm;
    const positions = [];
    let match;
    while ((match = sectionPattern.exec(fullDoc)) !== null) {
        const sectionName = match[1];
        if (sectionName) {
            positions.push({ name: sectionName.toLowerCase(), start: match.index });
        }
    }
    const extract = (name) => {
        const idx = positions.findIndex(p => p.name === name);
        if (idx === -1)
            return `# ${name.charAt(0).toUpperCase() + name.slice(1)}\n\n(Section not generated)`;
        const pos = positions[idx];
        const nextPos = idx + 1 < positions.length ? positions[idx + 1] : undefined;
        const end = nextPos ? nextPos.start : fullDoc.length;
        return fullDoc.slice(pos.start, end).trim();
    };
    return {
        specification: extract('specification'),
        pseudocode: extract('pseudocode'),
        architecture: extract('architecture'),
        refinement: extract('refinement'),
        completion: extract('completion'),
    };
}
/** Gather all textual content from Phase 1 artifacts into a single context blob. */
function gatherProjectContext(artifacts) {
    const parts = [];
    const query = artifacts.scenario?.query ?? artifacts.manifest?.query ?? '';
    if (query)
        parts.push(`## User Query\n${query}`);
    if (artifacts.executiveSummary) {
        parts.push(`## Executive Summary\n${artifacts.executiveSummary}`);
    }
    if (artifacts.decisionMemo) {
        parts.push(`## Decision Memo\n${artifacts.decisionMemo}`);
    }
    // Roadmap phases
    const roadmap = artifacts.roadmap;
    if (roadmap) {
        const phases = roadmap['phases'];
        if (phases && phases.length > 0) {
            const roadmapLines = phases.map((p, i) => {
                const name = p['name'] ?? `Phase ${i + 1}`;
                const desc = p['description'] ?? '';
                const deliverables = Array.isArray(p['deliverables'])
                    ? p['deliverables'].join(', ')
                    : '';
                return `${i + 1}. **${name}**: ${desc}${deliverables ? ` (Deliverables: ${deliverables})` : ''}`;
            });
            parts.push(`## Roadmap\n${roadmapLines.join('\n')}`);
        }
    }
    // Risk assessment
    const risk = artifacts.riskAssessment;
    if (risk) {
        const factors = risk['risk_factors'];
        if (factors && factors.length > 0) {
            const riskLines = factors.slice(0, 15).map((f) => {
                const title = f['title'] ?? f['name'] ?? 'Risk';
                const severity = f['severity'] ?? f['overall_risk_level'] ?? '';
                const mitigation = f['mitigation'] ?? '';
                return `- **${title}**${severity ? ` [${severity}]` : ''}${mitigation ? `: ${mitigation}` : ''}`;
            });
            parts.push(`## Risk Factors\n${riskLines.join('\n')}`);
        }
    }
    // Scenario fields
    const scenario = artifacts.scenario;
    if (scenario) {
        for (const key of ['stakeholders', 'constraints', 'success_criteria', 'systems', 'domain_entities', 'capabilities']) {
            const val = scenario[key];
            if (Array.isArray(val) && val.length > 0) {
                parts.push(`## ${key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}\n${val.map((v) => `- ${v}`).join('\n')}`);
            }
        }
    }
    return parts.join('\n\n');
}
// ============================================================================
// SPARC Template Generators
//
// Fast deterministic generators (<1s) that produce valid structured output
// from the dossier and Phase 1 artifacts. Used when the complete SPARC LLM
// call fails/times out — no per-section LLM fallbacks to avoid timeout stacking.
// ============================================================================
/** Deterministic specification from dossier items. */
function writeTemplateSpecification(query, projectContext, dossier) {
    const requirements = dossier.items.filter(i => i.category === 'requirement' || i.category === 'recommendation');
    const constraints = dossier.items.filter(i => i.category === 'constraint');
    const risks = dossier.items.filter(i => i.category === 'risk');
    const successCriteria = dossier.items.filter(i => i.category === 'success-criterion');
    const stakeholders = dossier.items.filter(i => i.category === 'stakeholder');
    const decisions = dossier.items.filter(i => i.category === 'decision-rationale');
    const lines = [];
    lines.push('# Specification');
    lines.push('');
    lines.push('## Objective');
    lines.push(`Develop a comprehensive specification for: **${query}**`);
    lines.push('');
    lines.push('## Project Overview');
    lines.push(projectContext);
    lines.push('');
    if (requirements.length > 0) {
        lines.push('## Functional Requirements');
        for (const r of requirements) {
            lines.push(`- **[${r.id}]** ${r.content}`);
        }
        lines.push('');
    }
    if (constraints.length > 0) {
        lines.push('## Non-Functional Requirements & Constraints');
        for (const c of constraints) {
            lines.push(`- **[${c.id}]** ${c.content}`);
        }
        lines.push('');
    }
    if (stakeholders.length > 0) {
        lines.push('## Stakeholders & Actors');
        for (const s of stakeholders) {
            lines.push(`- **${s.title}**: ${s.content}`);
        }
        lines.push('');
    }
    if (risks.length > 0) {
        lines.push('## Risk Factors');
        for (const r of risks) {
            const severity = r.severity ? ` [${r.severity}]` : '';
            lines.push(`- **[${r.id}]${severity}** ${r.content}`);
        }
        lines.push('');
    }
    if (successCriteria.length > 0) {
        lines.push('## Success Criteria');
        for (const sc of successCriteria) {
            lines.push(`- **[${sc.id}]** ${sc.content}`);
        }
        lines.push('');
    }
    if (decisions.length > 0) {
        lines.push('## Key Decisions');
        for (const d of decisions) {
            lines.push(`- **[${d.id}]** ${d.content}`);
        }
        lines.push('');
    }
    lines.push('## Assumptions');
    lines.push('- Phase 1 agent analysis across 27 domains is the primary input for this specification.');
    lines.push('- All requirements are derived from the research dossier and executive summary.');
    lines.push('- Architecture and technology choices will be refined in subsequent SPARC phases.');
    lines.push('');
    lines.push('## Reflection');
    lines.push(`This specification captures ${dossier.items.length} items from the research dossier across ${new Set(dossier.items.map(i => i.source)).size} sources. `);
    lines.push(`${requirements.length} functional requirements, ${constraints.length} constraints, and ${risks.length} risk factors drive the design.`);
    lines.push('');
    return lines.join('\n');
}
/** Deterministic pseudocode template. */
function writeTemplatePseudocode(query, domainServices) {
    const lines = [];
    lines.push('# Pseudocode');
    lines.push('');
    lines.push('## Objective');
    lines.push(`Create a pseudocode outline for each domain service: **${query}**`);
    lines.push('');
    for (const svc of domainServices) {
        const title = svc.name.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
        lines.push(`## ${title} Service`);
        lines.push('');
        lines.push(svc.responsibility);
        lines.push('');
        lines.push('```pseudocode');
        lines.push(`// Service: ${svc.name}`);
        lines.push(`// ${svc.responsibility}`);
        lines.push('');
        for (const cap of svc.capabilities) {
            const funcName = cap.replace(/[^a-zA-Z0-9\s]/g, '').replace(/\s+/g, '_').toLowerCase();
            lines.push(`function ${funcName}(input):`);
            lines.push(`    // Capability: ${cap}`);
            lines.push('    validate_preconditions(input)');
            lines.push('    aggregate = load_or_create_aggregate(input.id)');
            lines.push('    result = aggregate.execute(input)');
            lines.push('    if result.error:');
            lines.push('        return ErrorResponse(result.error)');
            lines.push(`    emit_event("${svc.name}.${funcName}.completed", result)`);
            lines.push('    persist(aggregate)');
            lines.push('    return SuccessResponse(result.data)');
            lines.push('');
        }
        lines.push('```');
        lines.push('');
    }
    lines.push('## Reflection');
    lines.push('- Pseudocode models the business domain — each module is a domain service.');
    lines.push(`- ${domainServices.length} domain services identified from scenario entities and capabilities.`);
    lines.push('- Error handling and validation are included at every entry point.');
    lines.push('- TDD anchors: each function boundary is a test seam for London School outside-in testing.');
    lines.push('');
    return lines.join('\n');
}
function writeArchitectureMarkdown(query, projectContext, dossier, artifacts) {
    const archConstraints = dossier.items.filter(i => i.category === 'constraint');
    const risks = dossier.items.filter(i => i.category === 'risk');
    const requirements = dossier.items.filter(i => i.category === 'requirement' || i.category === 'recommendation');
    const lines = [];
    lines.push('# Architecture');
    lines.push('');
    lines.push('## Objective');
    lines.push(`Define the system architecture and technical design for: **${query}**`);
    lines.push('');
    // Detect systems from scenario/context
    const allText = (projectContext + ' ' + query).toLowerCase();
    const detectedSystems = [];
    const systemKeywords = [
        ['workday', 'Workday HCM'], ['sap', 'SAP ERP'], ['netsuite', 'NetSuite'],
        ['oracle', 'Oracle'], ['salesforce', 'Salesforce'], ['servicenow', 'ServiceNow'],
        ['google cloud', 'Google Cloud Platform'], ['aws', 'Amazon Web Services'],
        ['azure', 'Microsoft Azure'], ['kafka', 'Apache Kafka'], ['redis', 'Redis'],
        ['postgres', 'PostgreSQL'], ['mongodb', 'MongoDB'], ['elasticsearch', 'Elasticsearch'],
        ['snowflake', 'Snowflake'], ['bigquery', 'BigQuery'], ['kubernetes', 'Kubernetes'],
        ['docker', 'Docker'], ['terraform', 'Terraform'],
    ];
    for (const [kw, name] of systemKeywords) {
        if (allText.includes(kw))
            detectedSystems.push(name);
    }
    lines.push('## Architectural Style');
    if (requirements.length > 10 || detectedSystems.length > 3) {
        lines.push('- **Microservices Architecture**: Selected due to the complexity and number of integration points.');
        lines.push('- Each bounded context operates as an independent service with its own data store.');
    }
    else {
        lines.push('- **Modular Monolith**: Selected for initial implementation simplicity with clear module boundaries.');
        lines.push('- Can evolve to microservices as scale demands grow.');
    }
    lines.push('');
    if (detectedSystems.length > 0) {
        lines.push('## Technology Stack');
        lines.push('### External Systems');
        for (const sys of detectedSystems) {
            lines.push(`- ${sys}`);
        }
        lines.push('');
    }
    // Derive components from DOMAIN services (not roadmap phases)
    const domainServices = extractDomainServices(query, artifacts, dossier);
    lines.push('## Key Components (Domain Services)');
    lines.push('');
    lines.push('| Service | Responsibility | Capabilities | Dependencies |');
    lines.push('|---------|---------------|-------------|-------------|');
    for (const svc of domainServices) {
        const caps = svc.capabilities.slice(0, 3).join(', ');
        const deps = svc.relatedSystems.length > 0 ? svc.relatedSystems.join(', ') : '-';
        lines.push(`| ${svc.name} | ${svc.responsibility.slice(0, 60)} | ${caps} | ${deps} |`);
    }
    lines.push('');
    lines.push('## Data Flow');
    lines.push('```');
    lines.push('User Request → API Gateway → Service Layer → Data Store');
    if (detectedSystems.length > 0) {
        lines.push(`              → Integration Layer → [${detectedSystems.slice(0, 3).join(', ')}]`);
    }
    lines.push('              → Event Bus → Notification Service → User');
    lines.push('```');
    lines.push('');
    if (archConstraints.length > 0) {
        lines.push('## Scalability, Security, and Performance');
        lines.push('### Constraints');
        for (const c of archConstraints.slice(0, 10)) {
            lines.push(`- ${c.content}`);
        }
        lines.push('');
    }
    if (risks.length > 0) {
        lines.push('### Risk Mitigations');
        for (const r of risks.slice(0, 10)) {
            lines.push(`- **${r.title}**: ${r.content.slice(0, 120)}`);
        }
        lines.push('');
    }
    lines.push('## Reflection');
    lines.push(`- Architecture addresses ${archConstraints.length} constraints and ${risks.length} risk factors from the research dossier.`);
    if (detectedSystems.length > 0) {
        lines.push(`- Integration with ${detectedSystems.length} external systems requires adapter pattern and circuit breakers.`);
    }
    lines.push('- Security boundaries align with bounded contexts — each service manages its own auth scope.');
    lines.push('');
    return lines.join('\n');
}
function writeRefinementMarkdown(query, _context, dossier) {
    const risks = dossier.items.filter(i => i.category === 'risk');
    const lines = [];
    lines.push('# Refinement');
    lines.push('');
    lines.push('## Objective');
    lines.push(`Iteratively improve the architecture and pseudocode for: **${query}**`);
    lines.push('');
    lines.push('## Security Considerations');
    const securityItems = dossier.items.filter(i => i.content.toLowerCase().match(/security|auth|access|pii|encrypt|credential|token|rbac|iam/));
    if (securityItems.length > 0) {
        for (const item of securityItems.slice(0, 10)) {
            lines.push(`- **[${item.id}]** ${item.content.slice(0, 150)}`);
        }
    }
    else {
        lines.push('- Implement authentication and authorization at all API endpoints.');
        lines.push('- Encrypt data at rest and in transit.');
        lines.push('- Apply principle of least privilege for service accounts.');
    }
    lines.push('');
    lines.push('## Performance Targets');
    const perfItems = dossier.items.filter(i => i.content.toLowerCase().match(/performance|latency|throughput|response time|scalab|p99|sla/));
    if (perfItems.length > 0) {
        for (const item of perfItems.slice(0, 10)) {
            lines.push(`- **[${item.id}]** ${item.content.slice(0, 150)}`);
        }
    }
    else {
        lines.push('- API response time < 200ms at p95.');
        lines.push('- System handles 1000 concurrent users.');
        lines.push('- Data pipeline processes within SLA window.');
    }
    lines.push('');
    lines.push('## Test Strategy');
    lines.push('- **London School TDD**: Outside-in approach starting from acceptance tests.');
    lines.push('- **Test Pyramid**: Unit (70%) → Integration (20%) → E2E (10%).');
    lines.push('- **Mock Contracts**: Each service boundary gets a mock contract for isolated testing.');
    lines.push('- **Seams**: Service boundaries, adapters, and gateways are primary test seams.');
    lines.push('');
    if (risks.length > 0) {
        lines.push('## Edge Cases & Risk Mitigations');
        for (const r of risks.slice(0, 8)) {
            lines.push(`- **${r.title}**: ${r.content.slice(0, 120)}`);
        }
        lines.push('');
    }
    lines.push('## Reflection');
    lines.push(`- ${securityItems.length} security-related items identified from research dossier.`);
    lines.push(`- ${perfItems.length} performance-related items drive the targets.`);
    lines.push(`- ${risks.length} risk factors inform edge case handling.`);
    lines.push('- Test strategy uses London School TDD for clean separation of concerns.');
    lines.push('');
    return lines.join('\n');
}
function writeCompletionMarkdown(query, _context, _dossier, _artifacts) {
    const lines = [];
    lines.push('# Completion');
    lines.push('');
    lines.push('## Objective');
    lines.push(`Finalize the project, ensuring it is ready for deployment: **${query}**`);
    lines.push('');
    lines.push('## Integration Plan');
    lines.push('- Integrate all bounded contexts via event-driven messaging.');
    lines.push('- Wire external system adapters with circuit breakers and retry policies.');
    lines.push('- End-to-end smoke tests validate the full integration path.');
    lines.push('');
    lines.push('## Testing Checklist');
    lines.push('- [ ] Unit tests pass for all modules (>80% coverage).');
    lines.push('- [ ] Integration tests validate service boundaries.');
    lines.push('- [ ] E2E tests cover primary user flows.');
    lines.push('- [ ] Security audit completed (no critical/high vulnerabilities).');
    lines.push('- [ ] Performance benchmarks meet targets from Refinement phase.');
    lines.push('');
    lines.push('## Documentation');
    lines.push('- API documentation (OpenAPI/Swagger).');
    lines.push('- Architecture Decision Records (ADRs) — generated in Phase 4.');
    lines.push('- Domain model documentation (DDD bounded contexts) — generated in Phase 4.');
    lines.push('- Deployment runbook and rollback procedures.');
    lines.push('- User guides and onboarding documentation.');
    lines.push('');
    lines.push('## Deployment & Monitoring');
    lines.push('- CI/CD pipeline with automated testing gates.');
    lines.push('- Health check endpoints for all services.');
    lines.push('- Structured logging with correlation IDs.');
    lines.push('- Alerting on SLO violations.');
    lines.push('- Dashboard for key business metrics.');
    lines.push('');
    lines.push('## Post-Deployment');
    lines.push('- Monitor error rates and latency for 48 hours post-deploy.');
    lines.push('- Gather user feedback via analytics and support channels.');
    lines.push('- Plan iteration based on production telemetry.');
    lines.push('');
    lines.push('## Reflection');
    lines.push('- This SPARC specification was generated from Phase 1 simulation (109 agents) and Phase 2 research dossier.');
    lines.push('- ADRs and DDDs will be generated in Phase 4 using ruflo coding agents, building on this SPARC output.');
    lines.push('- Phase 5 will produce the actual implementation code.');
    lines.push('');
    return lines.join('\n');
}
/**
 * Extract domain services from scenario entities, capabilities, systems,
 * dossier requirements, and NLP analysis of the query itself.
 *
 * This is the critical function that ensures SPARC services model the
 * BUSINESS DOMAIN (inventory-management, demand-forecasting) not project
 * management phases (discovery-planning, prototype-development).
 */
/**
 * Extract domain concepts directly from the user's prompt.
 * Looks for what the user says the system should DO — "model", "simulate", "map",
 * "quantify", "generate" — and extracts the noun phrases that follow.
 */
function extractDomainConceptsFromQuery(query) {
    const concepts = [];
    const seen = new Set();
    // Words that indicate a phrase is a behavioral constraint, NOT a domain entity
    const NEGATION_WORDS = /\b(not|no|never|without|cannot|don't|doesn't|won't)\b/i;
    const BEHAVIORAL_WORDS = new Set([
        'reviewed', 'approved', 'structured', 'maintained', 'produced', 'generated',
        'evaluated', 'demonstrated', 'disrupting', 'existing', 'viable', 'working',
        'strict', 'full', 'automatically', 'optionally', 'manually',
    ]);
    // Only extract from bullet points (- lines) — these are the clearest signal
    const bulletPattern = /^[-•]\s+(.{10,150})$/gm;
    let bulletMatch;
    while ((bulletMatch = bulletPattern.exec(query)) !== null) {
        const raw = (bulletMatch[1] ?? '').trim();
        // Skip negation phrases — "should not automatically change sourcing"
        if (NEGATION_WORDS.test(raw))
            continue;
        // Extract verb + object: "construct traceability graph", "simulate sourcing shifts"
        const verbMatch = raw.match(/^(\w+)\s+(.+)/i);
        if (!verbMatch)
            continue;
        const verb = verbMatch[1].toLowerCase();
        const rest = verbMatch[2];
        // Skip verbs that describe output format, not domain capability
        if (['produce', 'provide', 'ensure', 'maintain', 'support', 'allow', 'enable', 'include'].includes(verb))
            continue;
        // Extract meaningful noun words from the object
        const words = rest.toLowerCase()
            .replace(/[^a-z0-9\s-]/g, ' ')
            .split(/\s+/)
            .filter(w => w.length > 2
            && !BEHAVIORAL_WORDS.has(w)
            && !['the', 'and', 'for', 'how', 'such', 'our', 'can', 'that', 'this', 'from',
                'into', 'each', 'whether', 'across', 'under', 'with', 'using', 'based',
                'different', 'multiple', 'various', 'decision', 'grade', 'outputs',
                'scenarios', 'leadership', 'teams', 'engineering', 'product'].includes(w));
        if (words.length >= 2) {
            const key = words.slice(0, 3).join('-');
            if (!seen.has(key)) {
                seen.add(key);
                concepts.push(words.slice(0, 3).join(' '));
            }
        }
    }
    return concepts.slice(0, 6);
}
function extractDomainServices(query, artifacts, dossier) {
    const scenario = artifacts.scenario;
    const services = new Map();
    // ── Stop-words: common English words that never form domain names ──
    const STOP_WORDS = new Set([
        'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
        'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'be',
        'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will',
        'would', 'could', 'should', 'may', 'might', 'shall', 'can', 'that',
        'this', 'these', 'those', 'it', 'its', 'we', 'our', 'they', 'their',
        'them', 'you', 'your', 'i', 'my', 'me', 'he', 'she', 'his', 'her',
        'if', 'when', 'where', 'how', 'what', 'which', 'who', 'whom', 'why',
        'not', 'no', 'nor', 'so', 'very', 'too', 'also', 'just', 'about',
        'into', 'through', 'during', 'before', 'after', 'above', 'below',
        'between', 'under', 'over', 'again', 'further', 'then', 'once',
        'here', 'there', 'all', 'each', 'every', 'both', 'few', 'more',
        'most', 'other', 'some', 'such', 'any', 'only', 'same', 'than',
        'because', 'until', 'while', 'although', 'since', 'unless',
        // Project/task verbs that aren't domain nouns
        'build', 'create', 'make', 'develop', 'design', 'implement',
        'prototype', 'evaluate', 'demonstrate', 'deploy', 'run', 'use',
        'need', 'want', 'like', 'look', 'help', 'show', 'get', 'set',
        'currently', 'typically', 'roughly', 'ideally', 'basically',
        'across', 'within', 'several', 'enough', 'early', 'certain',
        // Action verbs — valid in capabilities but not in service names
        'analyze', 'analyse', 'detect', 'identify', 'predict', 'monitor',
        'track', 'measure', 'notify', 'trigger', 'generate', 'produce',
        'integrate', 'connect', 'interface', 'manage', 'handle',
        'check', 'verify', 'validate', 'ensure', 'maintain', 'support',
        // Adjectives and adverbs that don't belong in service names
        'inconsistent', 'unusual', 'historical', 'internal', 'external',
        'operational', 'financial', 'regulatory', 'automatic', 'manual',
        'potential', 'existing', 'current', 'previous', 'expected',
        'significant', 'relevant', 'specific', 'general', 'overall',
        'situations', 'indicators', 'patterns', 'signals', 'issues',
        // Generic tech/business words too vague for a service name
        'system', 'application', 'platform', 'solution', 'service',
        'process', 'data', 'information', 'operations', 'management',
        'existing', 'working', 'possible', 'potential', 'based',
    ]);
    /**
     * Extract the core domain noun(s) from a raw phrase.
     *
     * Strategy: strip stop-words, keep only meaningful domain terms (1-3 words),
     * return a clean kebab-case name DIRECTLY from the user's language.
     *
     * DO NOT map through DOMAIN_VOCABULARY — that converts domain-specific terms
     * like "biodiversity mapping" into generic ERP terms like "procurement-automation"
     * because the vocabulary matches on keywords like "supplier" and "procurement"
     * that appear as CONTEXT in the user's prompt, not as the SERVICE they want built.
     */
    const toServiceName = (raw) => {
        // Strip stop words, keep the user's actual domain terms
        const words = raw.toLowerCase()
            .replace(/[^a-z0-9\s-]/g, ' ')
            .split(/\s+/)
            .filter(w => w.length > 1 && !STOP_WORDS.has(w));
        if (words.length === 0)
            return '';
        // Use the user's actual domain terms as the service name (max 3 words)
        const slug = words.slice(0, 3).join('-');
        if (slug.length <= 40)
            return slug;
        const truncated = slug.slice(0, 40);
        const lastDash = truncated.lastIndexOf('-');
        return lastDash > 0 ? truncated.slice(0, lastDash) : truncated;
    };
    // 0. Extract domain concepts from the user's ACTUAL PROMPT (highest priority).
    //    The user describes what they want to build — extract those concepts directly.
    const promptConcepts = extractDomainConceptsFromQuery(query);
    for (const concept of promptConcepts) {
        const name = toServiceName(concept);
        if (!name || name.length < 2 || services.has(name))
            continue;
        services.set(name, {
            name,
            responsibility: `${concept} — core domain capability described in the project requirements`,
            capabilities: [],
            relatedSystems: [],
            dossierItemRefs: [],
        });
    }
    // 1. Extract from scenario.domain_entities (secondary source — Phase 1 simulation output)
    const domainEntities = scenario?.['domain_entities'] ?? [];
    for (const entity of domainEntities) {
        const name = toServiceName(entity);
        if (!name || name.length < 2)
            continue;
        if (!services.has(name)) {
            services.set(name, {
                name,
                responsibility: `${entity} — domain service for the ${entity.toLowerCase()} capability as described in the project requirements`,
                capabilities: [],
                relatedSystems: [],
                dossierItemRefs: [],
            });
        }
    }
    // 2. Extract from scenario.capabilities (functional capabilities become services)
    //    Use only the core domain noun(s) from each capability — never full sentences.
    //    Standard CRUD verbs are used for capabilities instead of sentence fragments.
    const capabilities = scenario?.['capabilities'] ?? [];
    for (const cap of capabilities) {
        const name = toServiceName(cap);
        if (!name || name.length < 2)
            continue;
        // Merge into existing service if similar, otherwise create new
        const existing = services.get(name);
        if (existing) {
            // Add clean CRUD capabilities, not the raw sentence
            if (!existing.capabilities.includes(`manage-${name}`)) {
                existing.capabilities.push(`manage-${name}`);
            }
        }
        else {
            services.set(name, {
                name,
                responsibility: `${cap} — business capability implementation with validation, processing, and event emission`,
                capabilities: [`create-${name}`, `update-${name}`, `query-${name}`],
                relatedSystems: [],
                dossierItemRefs: [],
            });
        }
    }
    // 3. Extract from scenario.systems (external system integrations become adapter services)
    const systems = scenario?.['systems'] ?? [];
    for (const sys of systems) {
        const baseName = toServiceName(sys);
        const name = `${baseName}-integration`;
        if (!name || name.length < 4)
            continue;
        if (!services.has(name)) {
            services.set(name, {
                name,
                responsibility: `Integration adapter for ${sys} — API client, data mapping, retry/circuit-breaker, event bridge`,
                capabilities: [`connect-${baseName}`, `sync-${baseName}`, `query-${baseName}`],
                relatedSystems: [sys],
                dossierItemRefs: [],
            });
        }
    }
    // 4. Research-first: extract domain services from the dossier content.
    //    The deep research (ruflo swarm + 110 agents) already identified the key
    //    domain concepts. We extract service names from dossier item titles and
    //    content, then normalize through the domain vocabulary.
    //    This replaces the old approach of hardcoded NLP regex patterns per industry.
    for (const item of dossier.items) {
        if (item.category !== 'requirement' && item.category !== 'recommendation'
            && item.category !== 'decision-rationale')
            continue;
        // Try to derive a service name from the item title
        const candidateName = normalizeContextName(item.title);
        if (candidateName && candidateName !== 'default-context' && !services.has(candidateName)) {
            services.set(candidateName, {
                name: candidateName,
                responsibility: item.content.slice(0, 200),
                capabilities: [`manage-${candidateName}`, `query-${candidateName}`],
                relatedSystems: [],
                dossierItemRefs: [item.id],
            });
        }
    }
    // 5. Fallback: if dossier produced fewer than 3 services, scan query+entities
    //    through normalizeContextName (from ddd-generator's domain vocabulary)
    if (services.size < 3) {
        const allText = query + ' ' + domainEntities.join(' ') + ' ' + capabilities.join(' ');
        // Split into overlapping 3-5 word windows and try to normalize each
        const words = allText.toLowerCase().replace(/[^a-z0-9\s-]/g, ' ').split(/\s+/).filter(w => w.length > 2);
        for (let windowSize = 3; windowSize >= 1; windowSize--) {
            for (let i = 0; i <= words.length - windowSize; i++) {
                const phrase = words.slice(i, i + windowSize).join(' ');
                const name = normalizeContextName(phrase);
                if (name && name !== 'default-context' && !services.has(name) && services.size < 12) {
                    services.set(name, {
                        name,
                        responsibility: `${name.replace(/-/g, ' ')} — domain service derived from project scope`,
                        capabilities: [`manage-${name}`, `query-${name}`],
                        relatedSystems: [],
                        dossierItemRefs: [],
                    });
                }
            }
        }
    }
    // 6. Enrich services with dossier item references
    for (const item of dossier.items) {
        if (item.category !== 'requirement' && item.category !== 'recommendation')
            continue;
        const itemText = item.content.toLowerCase();
        for (const svc of services.values()) {
            const nameWords = svc.name.split('-');
            if (nameWords.some(w => w.length > 2 && itemText.includes(w))) {
                svc.dossierItemRefs.push(item.id);
            }
        }
    }
    // 7. Always ensure at least an API gateway service
    if (!services.has('api-gateway')) {
        services.set('api-gateway', {
            name: 'api-gateway',
            responsibility: 'API gateway — request routing, authentication, rate limiting, request/response transformation',
            capabilities: ['route-requests', 'authenticate', 'rate-limit'],
            relatedSystems: [],
            dossierItemRefs: [],
        });
    }
    // 8. If no domain services were extracted, create from the query directly
    //    Use stop-word-filtered nouns, not adjacent word pairs
    if (services.size <= 1) {
        const queryNouns = query.toLowerCase()
            .replace(/[^a-z0-9\s]/g, ' ')
            .split(/\s+/)
            .filter(w => w.length > 3 && !STOP_WORDS.has(w));
        // Each remaining noun becomes a candidate service (deduplicated)
        for (const noun of queryNouns) {
            if (!services.has(noun) && services.size < 8) {
                services.set(noun, {
                    name: noun,
                    responsibility: `${noun} — domain service for business logic, data management, and event processing`,
                    capabilities: [`manage-${noun}`, `query-${noun}`],
                    relatedSystems: [],
                    dossierItemRefs: [],
                });
            }
        }
    }
    // 9. ADR-029 Fix 4: Add intelligence/ML services when query requires them
    const intelligenceReqs = detectIntelligenceRequirements(query);
    if (intelligenceReqs.hasIntelligence) {
        for (const ctx of intelligenceReqs.suggestedContexts) {
            // Only add if not already covered by an existing service
            const alreadyExists = Array.from(services.values()).some(s => s.name.toLowerCase().includes(ctx.name.split('-')[0]) ||
                ctx.name.includes(s.name.toLowerCase()));
            if (!alreadyExists) {
                services.set(ctx.name, {
                    name: ctx.name,
                    responsibility: ctx.description,
                    capabilities: ctx.commands,
                    relatedSystems: [],
                    dossierItemRefs: [],
                });
            }
        }
    }
    return Array.from(services.values()).slice(0, 12);
}
// ============================================================================
// JSON Compatibility Shim
//
// Downstream consumers (Phase 4 ADR/DDD generators, Phase 5 build, TDD plan)
// expect Phase2SPARCOutput JSON. We populate it FROM the dossier/artifacts
// with minimal structured data — the markdown is the real output.
// ============================================================================
function buildCompatJSON(dossier, artifacts) {
    const query = artifacts.scenario?.query ?? artifacts.manifest?.query ?? '';
    // --- Specification ---
    const requirements = [];
    const constraints = [];
    const successCriteria = [];
    const referencedIds = new Set();
    let reqIdx = 1, conIdx = 1, scIdx = 1;
    for (const item of dossier.items) {
        if (item.category === 'requirement' || item.category === 'recommendation') {
            requirements.push({
                id: `SPEC-REQ-${pad3(reqIdx)}`,
                text: item.content,
                dossierItemRefs: [item.id],
                priority: item.source === 'risk-assessment' || item.source === 'decision-memo' ? 'must' : 'should',
            });
            referencedIds.add(item.id);
            reqIdx++;
        }
        else if (item.category === 'constraint') {
            constraints.push({
                id: `SPEC-CON-${pad3(conIdx)}`,
                text: item.content,
                dossierItemRefs: [item.id],
            });
            referencedIds.add(item.id);
            conIdx++;
        }
        else if (item.category === 'success-criterion') {
            successCriteria.push({
                id: `SPEC-SC-${pad3(scIdx)}`,
                text: item.content,
                dossierItemRefs: [item.id],
                measurable: /\d+%|>=?\s*\d+|<\s*\d+/.test(item.content),
            });
            referencedIds.add(item.id);
            scIdx++;
        }
    }
    const dossierCoverage = dossier.items.length > 0
        ? Math.round((referencedIds.size / dossier.items.length) * 10000) / 100
        : 0;
    const specification = {
        requirements,
        constraints,
        successCriteria,
        scope: `Full implementation scope for: ${query}`,
        dossierCoverage,
    };
    // --- Extract domain services (NOT roadmap phases) ---
    const domainServices = extractDomainServices(query, artifacts, dossier);
    // --- Pseudocode (from domain services) ---
    // Function names use clean slug from capability (max 3 words), not raw sentences.
    const toFuncName = (cap) => {
        const words = cap.toLowerCase()
            .replace(/[^a-z0-9\s-]/g, ' ')
            .split(/[\s-]+/)
            .filter(w => w.length > 1);
        return words.slice(0, 3).join('_') || 'execute';
    };
    const modules = domainServices.map((svc, i) => ({
        id: `PC-MOD-${pad3(i + 1)}`,
        name: svc.name,
        algorithms: svc.capabilities.map(c => `${toFuncName(c)}: ${svc.responsibility.slice(0, 60)}`),
        markdown: svc.capabilities.map(c => {
            const funcName = toFuncName(c);
            return `function ${funcName}(input):\n    // ${svc.responsibility.slice(0, 80)}\n    validate_preconditions(input)\n    result = execute_domain_logic(input)\n    emit_domain_event("${svc.name}.${funcName}.completed", result)\n    return result`;
        }).join('\n\n'),
        tddAnchors: svc.capabilities.map(c => `test_${toFuncName(c)}`),
        dataStructures: [`${svc.name.replace(/-/g, '_')}_aggregate`, `${svc.name.replace(/-/g, '_')}_dto`],
        dossierItemRefs: svc.dossierItemRefs.slice(0, 5),
    }));
    const pseudocode = { modules };
    // --- Architecture (services from domain extraction) ---
    // API paths use clean CRUD slugs (max 3 words), never raw sentence fragments.
    const sanitizeCapSlug = (cap) => {
        const words = cap.toLowerCase()
            .replace(/[^a-z0-9\s-]/g, ' ')
            .split(/[\s-]+/)
            .filter(w => w.length > 1);
        return words.slice(0, 3).join('-') || 'execute';
    };
    const services = domainServices.map((svc, i) => ({
        id: `SVC-${pad3(i + 1)}`,
        name: svc.name,
        responsibility: svc.responsibility,
        api: [
            ...svc.capabilities.slice(0, 3).map(c => `POST /${svc.name}/${sanitizeCapSlug(c)}`),
            `GET /${svc.name}`,
            `GET /${svc.name}/:id`,
        ],
        dependencies: svc.relatedSystems.length > 0
            ? svc.relatedSystems.map(s => s.toLowerCase().replace(/[^a-z0-9]+/g, '-'))
            : [],
        dossierItemRefs: svc.dossierItemRefs.slice(0, 5),
    }));
    const architecture = {
        services,
        boundaries: [
            {
                id: 'BND-001',
                name: 'api-boundary',
                description: 'External API boundary — all external traffic enters here',
                dossierItemRefs: [],
            },
            ...domainServices
                .filter(s => s.relatedSystems.length > 0)
                .map((s, i) => ({
                id: `BND-${pad3(i + 2)}`,
                name: `${s.name}-boundary`,
                description: `Integration boundary for ${s.relatedSystems.join(', ')} — adapter pattern with circuit breaker`,
                dossierItemRefs: [],
            })),
        ],
        dataFlow: `User → API Gateway → ${services.filter(s => s.name !== 'api-gateway').map(s => s.name).join(' | ')} → Data Store`,
        apiContracts: services.map(s => `${s.name}: ${s.api.join(', ')}`),
        securityBoundaries: ['API authentication required at gateway', 'Service-to-service mTLS', 'ERP integration uses OAuth2/API keys'],
    };
    // --- Refinement ---
    const securityItems = dossier.items.filter(i => i.content.toLowerCase().match(/security|auth|access|pii|encrypt|credential|token/));
    const perfItems = dossier.items.filter(i => i.content.toLowerCase().match(/performance|latency|throughput|scalab|sla/));
    const refinement = {
        securityConsiderations: securityItems.slice(0, 10).map((item, i) => ({
            id: `SEC-${pad3(i + 1)}`,
            text: item.content.slice(0, 200),
            dossierItemRefs: [item.id],
        })),
        performanceTargets: perfItems.slice(0, 10).map((item, i) => ({
            id: `PERF-${pad3(i + 1)}`,
            text: item.content.slice(0, 200),
            dossierItemRefs: [item.id],
        })),
        testStrategy: 'London School TDD — outside-in, mock-driven, test pyramid (unit 70% / integration 20% / e2e 10%)',
    };
    // --- Completion ---
    const completion = {
        integrationPlan: 'Event-driven integration across bounded contexts with circuit breakers for external systems.',
        documentation: [
            'API documentation (OpenAPI/Swagger)',
            'Architecture Decision Records (ADRs)',
            'Domain model (DDD bounded contexts)',
            'Deployment runbook',
        ],
        monitoringSetup: [
            'Health check endpoints',
            'Structured logging with correlation IDs',
            'SLO alerting',
            'Business metrics dashboard',
        ],
    };
    return { specification, pseudocode, architecture, refinement, completion };
}
// ============================================================================
// Public: buildPhase2SPARC (backward compat export)
// ============================================================================
export function buildPhase2SPARC(dossier, artifacts) {
    return buildCompatJSON(dossier, artifacts);
}
// ============================================================================
// Public: Main Stage Function
// ============================================================================
/**
 * Stage 3 entry point: generate SPARC specification from the research dossier
 * and Phase 1 artifacts stored on the pipeline context.
 *
 * Writes 5 SPARC markdown documents + JSON compatibility files to
 * `context.phase2Dir/sparc/`.
 */
export async function generateSPARCSpecification(context) {
    const rootSpan = createSpan('sparc-specification', 'generate-sparc', {
        phase2Dir: context.phase2Dir,
    });
    try {
        // Retrieve the research dossier from the previous stage
        const dossierStage = context.stageOutputs.get('research-dossier');
        if (!dossierStage || dossierStage.status !== 'completed') {
            throw new Error('ECLI-P2-010: Research dossier not available on context. ' +
                'Ensure the research-dossier stage completed successfully before running sparc-specification.');
        }
        const dossier = dossierStage.data;
        const query = context.phase1Artifacts?.scenario?.query
            ?? context.phase1Artifacts?.manifest?.query
            ?? '';
        // Gather project context from all artifacts
        const projectContext = gatherProjectContext(context.phase1Artifacts);
        // ADR-027: Try single comprehensive LLM SPARC generation first
        // SPARC is ONE cohesive architecture document, not 5 disconnected fragments
        const llmSparc = tryGenerateCompleteSPARC(query, projectContext, dossier, context.phase1Artifacts, context.phase2Dir);
        let specMd;
        let pseudoMd;
        let archMd;
        let refineMd;
        let completionMd;
        if (llmSparc) {
            // LLM generated the full SPARC — split into sections
            const sections = splitSPARCSections(llmSparc);
            specMd = sections.specification;
            pseudoMd = sections.pseudocode;
            archMd = sections.architecture;
            refineMd = sections.refinement;
            completionMd = sections.completion;
        }
        else {
            // Complete SPARC call failed/timed out — go straight to templates.
            // Per-section LLM calls would also likely timeout, wasting minutes.
            // Templates are fast (<1s) and produce valid structured output.
            console.error('  [SPARC] Using fast template generators (LLM unavailable or timed out)');
            specMd = writeTemplateSpecification(query, projectContext, dossier);
            const domainServices = extractDomainServices(query, context.phase1Artifacts, dossier);
            pseudoMd = writeTemplatePseudocode(query, domainServices);
            archMd = writeArchitectureMarkdown(query, projectContext, dossier, context.phase1Artifacts);
            refineMd = writeRefinementMarkdown(query, projectContext, dossier);
            completionMd = writeCompletionMarkdown(query, projectContext, dossier, context.phase1Artifacts);
        }
        // Build JSON compat shim
        const sparc = buildCompatJSON(dossier, context.phase1Artifacts);
        // Write files
        const sparcDir = path.join(context.phase2Dir, SPARC_SUBDIR);
        fs.mkdirSync(sparcDir, { recursive: true, mode: DIR_MODE });
        const writtenPaths = [];
        const writeFile = (name, content) => {
            const filePath = path.join(sparcDir, name);
            try {
                fs.writeFileSync(filePath, content + '\n', { encoding: 'utf-8', mode: FILE_MODE });
                writtenPaths.push(filePath);
            }
            catch (err) {
                const message = err instanceof Error ? err.message : String(err);
                throw new Error(`ECLI-P2-011: Failed to write SPARC file ${name}: ${message}`);
            }
        };
        // Primary output: 5 SPARC markdown documents
        writeFile('Specification.md', specMd);
        writeFile('Pseudocode.md', pseudoMd);
        writeFile('Architecture.md', archMd);
        writeFile('Refinement.md', refineMd);
        writeFile('Completion.md', completionMd);
        // Combined markdown
        const combinedMd = [specMd, pseudoMd, archMd, refineMd, completionMd].join('\n\n---\n\n');
        writeFile('sparc-combined.md', combinedMd);
        // JSON compat shim for downstream consumers
        const writeJSON = (name, data) => {
            const filePath = path.join(sparcDir, name);
            try {
                fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', { encoding: 'utf-8', mode: FILE_MODE });
                writtenPaths.push(filePath);
            }
            catch (err) {
                const message = err instanceof Error ? err.message : String(err);
                throw new Error(`ECLI-P2-011: Failed to write SPARC file ${name}: ${message}`);
            }
        };
        writeJSON('specification.json', sparc.specification);
        writeJSON('pseudocode.json', sparc.pseudocode);
        writeJSON('architecture.json', sparc.architecture);
        writeJSON('refinement.json', sparc.refinement);
        writeJSON('completion.json', sparc.completion);
        writeJSON('sparc-combined.json', sparc);
        endSpan(rootSpan, 'ok', {
            markdownFiles: 6,
            jsonFiles: 6,
            dossierCoverage: sparc.specification.dossierCoverage,
        });
        emitSpan(rootSpan);
        const timing = rootSpan.durationMs ?? 0;
        const output = {
            stage: 'sparc-specification',
            status: 'completed',
            data: sparc,
            artifacts: writtenPaths,
            timing,
            summary: `     SPARC: 5 markdown docs + JSON compat | ` +
                `${sparc.specification.requirements.length} requirements, ` +
                `${sparc.specification.constraints.length} constraints, ` +
                `${sparc.architecture.services.length} services ` +
                `(dossier coverage: ${sparc.specification.dossierCoverage}%)`,
        };
        context.stageOutputs.set('sparc-specification', output);
        return output;
    }
    catch (err) {
        const errorMessage = err instanceof Error ? err.message : String(err);
        endSpan(rootSpan, 'error', { error: errorMessage });
        emitSpan(rootSpan);
        const timing = rootSpan.durationMs ?? 0;
        const output = {
            stage: 'sparc-specification',
            status: 'failed',
            data: { error: errorMessage },
            artifacts: [],
            timing,
            summary: `SPARC specification generation failed: ${errorMessage}`,
        };
        context.stageOutputs.set('sparc-specification', output);
        return output;
    }
}
//# sourceMappingURL=sparc-specification.js.map