// tools/red-team.ts — Adversarial spec review before approval
// Argues against the spec from first principles: wrong problem, wrong solution,
// wrong timing, wrong scope, hidden assumptions, second-order effects.
import { readFile } from 'node:fs/promises';
import { specStore } from '../storage/index.js';
import { readSpecTechnicalSection } from '../engine/spec-format/read-technical-section.js';
import { ti } from '../i18n/index.js';
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
async function readSpecText(spec) {
    let content = `${spec.title} ${spec.type} ${spec.scope} ${spec.risk} ${spec.tags.join(' ')}`;
    try {
        if (spec.specPath) {
            content += ' ' + (await readFile(spec.specPath, 'utf-8'));
        }
    }
    catch {
        /* file not available */
    }
    content += ' ' + (await readSpecTechnicalSection(spec));
    return content;
}
function objection(lens, claim, evidence, severity) {
    return { lens, claim, evidence, severity };
}
// ---------------------------------------------------------------------------
// Lens generators — each returns 0-3 objections based on spec content
// ---------------------------------------------------------------------------
function wrongProblemObjections(spec, content) {
    const lower = content.toLowerCase();
    const result = [];
    if (!spec.title || spec.title.trim().length < 5) {
        result.push(objection('wrong-problem', 'The spec has no clear problem statement. You cannot solve a problem you have not defined.', 'Title is too short or missing. A spec without a well-defined problem is a solution looking for a justification.', 'fatal'));
    }
    if (lower.includes('improve') || lower.includes('enhance') || lower.includes('better')) {
        if (!lower.includes('because') && !lower.includes('users') && !lower.includes('pain')) {
            result.push(objection('wrong-problem', 'Vague improvement language with no root-cause evidence. "Better" is not a problem definition.', 'Spec uses improvement language but provides no evidence of a specific pain point, metric, or user complaint.', 'major'));
        }
    }
    if (lower.includes('technical debt') && !lower.includes('impact') && !lower.includes('slow')) {
        result.push(objection('wrong-problem', 'Technical debt cited as justification with no business impact stated. Debt alone is not a reason to build.', 'Spec references technical debt but does not explain how it affects delivery speed, error rates, or user experience.', 'minor'));
    }
    return result.slice(0, 3);
}
function wrongSolutionObjections(spec, content) {
    const lower = content.toLowerCase();
    const result = [];
    const complexKeywords = [
        'microservice',
        'distributed',
        'event-driven',
        'message queue',
        'kafka',
        'saga',
    ];
    const hasComplexSolution = complexKeywords.some((kw) => lower.includes(kw));
    if (hasComplexSolution && spec.scope === 'trivial') {
        result.push(objection('wrong-solution', 'Distributed architecture proposed for a trivial-scope change. YAGNI — you are building a rocket to cross the street.', 'Spec scope is trivial but proposes complex distributed patterns. Simpler solutions almost certainly exist.', 'major'));
    }
    if (!lower.includes('alternative') &&
        !lower.includes('option') &&
        !lower.includes('considered')) {
        result.push(objection('wrong-solution', 'No alternatives were considered. The first solution that comes to mind is rarely the best one.', 'Spec does not document any alternative approaches or explain why this solution was chosen over others.', 'minor'));
    }
    const newSystemKeywords = [
        'new system',
        'new service',
        'new module',
        'build from scratch',
        'rewrite',
    ];
    if (newSystemKeywords.some((kw) => lower.includes(kw))) {
        result.push(objection('wrong-solution', 'Proposes building something new when extending existing code is almost always faster and safer.', 'Spec introduces a new system without documenting why existing components cannot be extended.', 'major'));
    }
    return result.slice(0, 3);
}
function wrongTimingObjections(spec, content) {
    const lower = content.toLowerCase();
    const result = [];
    if (spec.dependencies.length > 3) {
        result.push(objection('wrong-timing', `This spec has ${spec.dependencies.length} declared dependencies. If any are incomplete, this is dead on arrival.`, 'High dependency count increases the probability that at least one blocker will delay or invalidate the work.', 'major'));
    }
    if (spec.blockedBy.length > 0) {
        result.push(objection('wrong-timing', `Spec is blocked by ${spec.blockedBy.join(', ')}. Starting work now wastes the team's focus.`, 'Active blockers signal the work should not be scheduled until dependencies are resolved.', 'fatal'));
    }
    if (lower.includes('proof of concept') || lower.includes('poc') || lower.includes('spike')) {
        result.push(objection('wrong-timing', 'Spec is a PoC/spike but is being tracked like production work. PoCs time-box themselves into irrelevance.', 'Research spikes belong in a time-boxed experiment, not in a spec that implies a commitment to deliver.', 'minor'));
    }
    return result.slice(0, 3);
}
function wrongScopeObjections(spec, content) {
    const lower = content.toLowerCase();
    const result = [];
    const acMatches = lower.match(/acceptance criteria|given |when |then |must |should /g);
    const acCount = acMatches ? acMatches.length : 0;
    if (acCount > 20) {
        result.push(objection('wrong-scope', `${acCount} acceptance criteria signals this is at least 3 specs compressed into one. It will never ship as a unit.`, 'Specs with more than ~15 acceptance criteria consistently miss deadlines and cause integration problems.', 'major'));
    }
    if (acCount === 0 || !spec.title) {
        result.push(objection('wrong-scope', 'No acceptance criteria found. Without criteria, scope is infinite — this spec will expand until someone stops it.', 'A spec without measurable acceptance criteria has no definition of done. The team cannot know when to stop.', 'fatal'));
    }
    if (spec.scope === 'architectural' && spec.estimation.devHours < 8) {
        result.push(objection('wrong-scope', 'Architectural scope with less than 8 dev-hours estimated is almost certainly under-scoped.', 'Cross-cutting concerns always carry hidden coordination costs not reflected in the initial estimate.', 'major'));
    }
    return result.slice(0, 3);
}
function hiddenAssumptionsObjections(spec, content) {
    const lower = content.toLowerCase();
    const result = [];
    if (!lower.includes('test') && !lower.includes('spec') && spec.type !== 'docs') {
        result.push(objection('hidden-assumptions', 'Assumes the feature can be delivered without a testing strategy. This assumption is almost always wrong.', 'No testing approach is mentioned. The hidden assumption is that testing will be figured out during implementation.', 'major'));
    }
    if ((lower.includes('user') || lower.includes('customer')) &&
        !lower.includes('validated') &&
        !lower.includes('feedback') &&
        !lower.includes('interview')) {
        result.push(objection('hidden-assumptions', 'Assumes the team knows what users want without evidence of user validation.', 'Spec involves users but provides no evidence of user research, interviews, or feedback to validate the need.', 'major'));
    }
    if (lower.includes('api') && !lower.includes('version') && !lower.includes('contract')) {
        result.push(objection('hidden-assumptions', 'Assumes external API is stable and versioned. APIs break without warning.', 'Spec uses an external API but has no contract definition or fallback strategy for API changes or downtime.', 'minor'));
    }
    return result.slice(0, 3);
}
function secondOrderObjections(spec, content) {
    const lower = content.toLowerCase();
    const result = [];
    if (lower.includes('performance') ||
        lower.includes('load') ||
        lower.includes('cache') ||
        lower.includes('index')) {
        result.push(objection('second-order', 'Performance changes ripple. What is faster in isolation often creates bottlenecks elsewhere.', 'Spec includes performance-sensitive changes but has no load testing plan or rollback strategy.', 'minor'));
    }
    if (lower.includes('auth') || lower.includes('permission') || lower.includes('role')) {
        result.push(objection('second-order', 'Auth/permission changes affect every part of the system that enforces access control. One missed check = security hole.', 'Authorization changes have non-obvious reach. Auditing every affected endpoint is rarely included in scope.', 'major'));
    }
    if (spec.scope === 'architectural' ||
        lower.includes('schema') ||
        lower.includes('migration') ||
        lower.includes('rename')) {
        result.push(objection('second-order', 'Structural changes require every consumer to adapt. The spec does not account for migration burden on dependents.', 'Schema changes, renames, and architectural moves have a tail of integration fixes not counted in this spec.', 'major'));
    }
    return result.slice(0, 3);
}
// ---------------------------------------------------------------------------
// Verdict + hidden assumptions + alternative
// ---------------------------------------------------------------------------
function calculateVerdict(objections, spec) {
    const fatalCount = objections.filter((o) => o.severity === 'fatal').length;
    if (fatalCount > 0) {
        return 'stop';
    }
    let total = objections.length;
    // Escalate if architectural + high risk
    if (spec.scope === 'architectural' && spec.risk === 'high') {
        total += 1;
    }
    if (total >= 6) {
        return 'stop';
    }
    if (total >= 3) {
        return 'reconsider';
    }
    return 'proceed';
}
function calculateConfidence(objections, verdict) {
    const base = verdict === 'proceed' ? 80 : verdict === 'reconsider' ? 60 : 40;
    const deduction = objections.filter((o) => o.severity === 'fatal').length * 15 +
        objections.filter((o) => o.severity === 'major').length * 5;
    return Math.max(10, Math.min(95, base - deduction));
}
function extractHiddenAssumptions(spec, content) {
    const lower = content.toLowerCase();
    const assumptions = [];
    assumptions.push(`The problem described in "${spec.title}" is actually the root cause, not a symptom of a deeper issue.`);
    if (lower.includes('user')) {
        assumptions.push('Users will adopt the feature as designed without requiring onboarding or change management.');
    }
    if (lower.includes('api') || lower.includes('integration')) {
        assumptions.push('Third-party integrations will behave as documented throughout the entire delivery cycle.');
    }
    if (spec.estimation.devHours > 0) {
        assumptions.push(`The ${spec.estimation.devHours}h estimate is accurate. Historical data shows estimates are typically 30-50% low for this scope.`);
    }
    if (spec.risk === 'high' || spec.risk === 'critical') {
        assumptions.push('The team has sufficient expertise to execute a high-risk spec without external consultation or spikes.');
    }
    return assumptions.slice(0, 5);
}
function generateAlternative(spec, content) {
    const lower = content.toLowerCase();
    if (lower.includes('new') || lower.includes('build') || lower.includes('create')) {
        return {
            title: 'Extend existing functionality instead of building new',
            summary: `Before building "${spec.title}" from scratch, audit what already exists. Extend current components, add a configuration flag, or compose existing tools differently. This delivers 80% of the value in 20% of the time.`,
            tradeoffs: 'Slower to reach the ideal end-state. May require refactoring existing code. Produces less clean architecture but ships faster and with less risk.',
        };
    }
    if (lower.includes('migrate') || lower.includes('refactor') || lower.includes('rewrite')) {
        return {
            title: 'Incremental strangler-fig migration instead of a big-bang rewrite',
            summary: 'Route new traffic through the new path while leaving old code intact. Migrate one use case at a time, verify in production, then delete the old path. Never have two systems live simultaneously for more than one sprint.',
            tradeoffs: 'Takes longer to fully complete. Requires maintaining two code paths temporarily. Much lower risk of catastrophic failure than a full cutover.',
        };
    }
    return {
        title: 'Do nothing and measure first',
        summary: `Before committing to building "${spec.title}", instrument the current system and measure the actual impact of the problem. Data from production often reveals the problem is smaller than assumed — or points to a different root cause entirely.`,
        tradeoffs: 'Delays delivery. Requires instrumentation work upfront. Risk: the measurement itself may become the project. Reward: avoids building the wrong thing at full cost.',
    };
}
function buildRecommendation(verdict, objections, spec) {
    const fatalClaims = objections
        .filter((o) => o.severity === 'fatal')
        .map((o) => `"${o.claim}"`)
        .join('; ');
    if (verdict === 'stop') {
        return fatalClaims
            ? `Do not proceed. Fatal objections must be resolved first: ${fatalClaims}. Revise the spec or abandon it.`
            : `Do not proceed. The spec accumulates ${objections.length} objections that collectively make implementation high-risk. Decompose, clarify, or abandon.`;
    }
    if (verdict === 'reconsider') {
        const majorCount = objections.filter((o) => o.severity === 'major').length;
        return `Proceed only after resolving ${majorCount} major objection(s). The spec is not ready for implementation in its current form. Address the identified weaknesses, then re-run red_team before approving.`;
    }
    const minorCount = objections.filter((o) => o.severity === 'minor').length;
    if (minorCount > 0) {
        return `Safe to proceed with ${minorCount} minor caveat(s) noted. Document mitigations for the minor objections before closing the spec. The core approach is sound.`;
    }
    return `Proceed. The spec passes adversarial review. No critical objections found against "${spec.title}".`;
}
// ---------------------------------------------------------------------------
// Public handler
// ---------------------------------------------------------------------------
export async function handleRedTeam(args) {
    const { specId, projectId, intensity = 'light' } = args;
    const spec = await specStore.getSpec(projectId, specId);
    if (!spec) {
        return {
            content: [{ type: 'text', text: ti('spec.notFound', { id: specId }) }],
            isError: true,
        };
    }
    const content = await readSpecText(spec);
    // Run all 6 lenses
    const allObjections = [
        ...wrongProblemObjections(spec, content),
        ...wrongSolutionObjections(spec, content),
        ...wrongTimingObjections(spec, content),
        ...wrongScopeObjections(spec, content),
        ...hiddenAssumptionsObjections(spec, content),
        ...secondOrderObjections(spec, content),
    ];
    // Apply intensity filter
    const objections = intensity === 'light'
        ? allObjections
            .sort((a, b) => {
            const order = { fatal: 0, major: 1, minor: 2 };
            return (order[a.severity] ?? 2) - (order[b.severity] ?? 2);
        })
            .slice(0, 3)
        : allObjections;
    const verdict = calculateVerdict(objections, spec);
    const confidence = calculateConfidence(objections, verdict);
    const hiddenAssumptions = extractHiddenAssumptions(spec, content);
    const alternative = generateAlternative(spec, content);
    const recommendation = buildRecommendation(verdict, objections, spec);
    const result = {
        specId,
        verdict,
        confidence,
        objections,
        hiddenAssumptions,
        alternative,
        recommendation,
    };
    const verdictEmoji = verdict === 'proceed' ? 'PROCEED' : verdict === 'reconsider' ? 'RECONSIDER' : 'STOP';
    const summaryLines = [
        `Red Team Review — ${spec.title}`,
        `Verdict: ${verdictEmoji} | Confidence: ${confidence}% | Objections: ${objections.length}`,
        '',
        recommendation,
        '',
        objections.length > 0
            ? `Top objections:\n${objections.map((o) => `  [${o.severity.toUpperCase()}] ${o.lens}: ${o.claim}`).join('\n')}`
            : 'No significant objections found.',
        '',
        `Alternative: ${alternative.title}`,
        `  ${alternative.summary}`,
    ].join('\n');
    return {
        content: [{ type: 'text', text: summaryLines }],
        structuredContent: result,
    };
}
//# sourceMappingURL=red-team.js.map