// SPEC-597-EXEMPT: canonical mapping (npm-package↔framework, keyword catalogue)
// — must enumerate framework names by design; no detection logic to migrate.
// Planu — Contradiction Detector (SPEC-615)
// Detects when a new spec criterion contradicts a prior decision
// using keyword-category heuristics.
// ---------------------------------------------------------------------------
// Category buckets — words that belong to the same "technology slot"
// When a decision says "use X" and a criterion says "use Y" (different X/Y in
// same bucket), we flag a contradiction.
// ---------------------------------------------------------------------------
const TECH_CATEGORIES = [
    // Auth strategies
    ['jwt', 'session', 'sessions', 'cookie', 'cookies', 'oauth', 'saml', 'oidc', 'token', 'tokens'],
    // Databases
    [
        'postgres',
        'postgresql',
        'mysql',
        'sqlite',
        'mongodb',
        'redis',
        'dynamodb',
        'firestore',
        'supabase',
        'planetscale',
        'cockroachdb',
        'neo4j',
        'cassandra',
    ],
    // Frontend frameworks
    [
        'react',
        'vue',
        'angular',
        'svelte',
        'solid',
        'qwik',
        'remix',
        'nextjs',
        'nuxt',
        'astro',
        'gatsby',
    ],
    // Backend frameworks
    [
        'express',
        'fastify',
        'koa',
        'hapi',
        'nestjs',
        'django',
        'flask',
        'fastapi',
        'rails',
        'laravel',
        'spring',
        'gin',
        'fiber',
    ],
    // Testing
    ['jest', 'vitest', 'mocha', 'jasmine', 'pytest', 'rspec', 'junit'],
    // Styling
    ['tailwind', 'css-modules', 'styled-components', 'emotion', 'sass', 'scss', 'less'],
    // State management
    ['redux', 'zustand', 'mobx', 'pinia', 'jotai', 'recoil', 'xstate'],
    // ORMs
    ['prisma', 'drizzle', 'typeorm', 'sequelize', 'sqlalchemy', 'ecto', 'hibernate'],
    // Message brokers
    ['kafka', 'rabbitmq', 'sqs', 'pubsub', 'nats', 'redis'],
    // Container / infra
    ['docker', 'kubernetes', 'k8s', 'nomad', 'heroku', 'railway', 'fly', 'vercel', 'netlify'],
];
/** Returns the category bucket index for a token, or -1 if none. */
function categoryOf(token) {
    for (let i = 0; i < TECH_CATEGORIES.length; i++) {
        const bucket = TECH_CATEGORIES[i];
        if (bucket?.includes(token)) {
            return i;
        }
    }
    return -1;
}
const USE_RE = /\b(use|using|adopt|adopted|choose|chose|prefer|prefers?)\s+(\w+)/gi;
const DONT_RE = /\b(avoid|avoid|no|not|don'?t|never|remove|removing|replace|replac\w+|drop|dropping)\s+(\w+)/gi;
function extractTechUsage(text) {
    const usages = [];
    let m;
    USE_RE.lastIndex = 0;
    while ((m = USE_RE.exec(text)) !== null) {
        const token = (m[2] ?? '').toLowerCase();
        const cat = categoryOf(token);
        if (cat !== -1) {
            usages.push({ token, category: cat, positive: true });
        }
    }
    DONT_RE.lastIndex = 0;
    while ((m = DONT_RE.exec(text)) !== null) {
        const token = (m[2] ?? '').toLowerCase();
        const cat = categoryOf(token);
        if (cat !== -1) {
            usages.push({ token, category: cat, positive: false });
        }
    }
    return usages;
}
/**
 * Detect contradictions between acceptance criteria and prior decisions.
 *
 * A contradiction is raised when:
 * - A decision says "use TOOL_A" (positive)
 * - A criterion says "use TOOL_B" (positive) where TOOL_B ≠ TOOL_A but they
 *   share the same technology category.
 *
 * Severity is 'hard' when the decision is explicitly positive ("use X")
 * and the criterion explicitly prefers something else in the same slot;
 * 'soft' for indirect signals.
 */
export function detectContradictions(criteria, priorDecisions, decisionTexts) {
    const findings = [];
    for (const criterion of criteria) {
        const criterionUsages = extractTechUsage(criterion);
        for (const link of priorDecisions) {
            const fullText = decisionTexts.get(link.decisionId) ?? link.title;
            const decisionUsages = extractTechUsage(fullText);
            for (const cu of criterionUsages) {
                for (const du of decisionUsages) {
                    // Same category, different technology, both positive → contradiction
                    if (cu.category === du.category && cu.token !== du.token && cu.positive && du.positive) {
                        const severity = 'hard';
                        findings.push({
                            criterion,
                            decisionId: link.decisionId,
                            reason: `Criterion prefers "${cu.token}" but decision "${link.decisionId}" chose "${du.token}" (same technology category). Confirm reversal or update the decision log.`,
                            severity,
                        });
                    }
                    // Decision says "use X", criterion says "avoid X" → soft contradiction
                    if (cu.category === du.category && cu.token === du.token && du.positive && !cu.positive) {
                        findings.push({
                            criterion,
                            decisionId: link.decisionId,
                            reason: `Criterion avoids "${cu.token}" but prior decision "${link.decisionId}" adopted it. Review if the decision should be superseded.`,
                            severity: 'soft',
                        });
                    }
                }
            }
        }
    }
    // Deduplicate by criterion+decisionId+token combination
    const seen = new Set();
    return findings.filter((f) => {
        const key = `${f.criterion}|${f.decisionId}|${f.reason}`;
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    });
}
//# sourceMappingURL=contradiction-detector.js.map