/**
 * Guard Checker - Port of Bollharness checks to Bolloon guardrails
 *
 * Transforms bollharness's 15+ check functions into Bolloon's guardrail system.
 *
 * Key concepts:
 * - Finding: bollharness's issue report structure
 * - Guardrail: Bolloon's pre/post execution check interface
 * - Guard Map: Path → Check[] routing
 */
import * as fs from 'fs';
/**
 * Port of bollharness's Finding interface
 */
export function createFinding(params) {
    return {
        severity: params.severity,
        message: params.message,
        file: params.file,
        line: params.line,
        blocking: params.blocking ?? false,
        category: params.category ?? 'general',
        problem_class: params.problem_class ?? 'unknown',
        required_skills: params.required_skills ?? [],
        required_reads: params.required_reads ?? [],
    };
}
/**
 * Guard Map - Maps file paths to relevant checks
 * Adapted from bollharness's guard_router.ts
 */
export const GUARD_MAP = {
    'src/agents/': ['check_api_types', 'check_skill_parity'],
    'src/documents/': ['check_doc_freshness', 'check_api_types'],
    'src/network/': ['check_api_types', 'check_versions'],
    'src/constraints/': ['check_api_types', 'check_versions'],
    'src/social/': ['check_api_types', 'check_versions'],
    'docs/': ['check_doc_freshness', 'check_doc_links'],
    'CLAUDE.md': ['check_doc_freshness', 'check_artifact_link'],
    'README.md': ['check_doc_freshness'],
    'docs/decisions/': ['check_artifact_link', 'check_versions'],
    '.boll/': ['check_hook_installed'],
    'src/test/': ['check_api_types', 'check_versions'],
};
/**
 * Default guards that run for all files
 */
export const DEFAULT_GUARDS = [];
/**
 * Category to Skills mapping
 * Maps guard categories to required bollharness skills
 */
export const CATEGORY_TO_SKILLS = {
    closure_semantics: ['lead', 'harness-ops'],
    contract_drift: ['harness-eng', 'harness-eng-test'],
    bridge_boundary: ['harness-bridge', 'harness-ops'],
    policy_freeze: ['lead', 'arch', 'plan-lock'],
    doc_integrity: ['harness-ops'],
    version_drift: ['harness-ops'],
    artifact_linkage: ['lead'],
    governance_bootstrap: ['harness-ops'],
};
/**
 * Check implementations - Ported from bollharness checks
 */
// Check: API Types
export function checkApiTypes(filePath) {
    const findings = [];
    // Check if API types are consistent
    if (filePath.endsWith('.ts')) {
        const content = fs.readFileSync(filePath, 'utf-8');
        // Check for TODO/FIXME that might indicate type issues
        const todoMatches = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX):/g);
        if (todoMatches) {
            findings.push(createFinding({
                severity: 'P2',
                message: `Found ${todoMatches.length} development markers in code`,
                file: filePath,
                blocking: false,
                category: 'doc_integrity',
                problem_class: 'technical_debt',
                required_skills: ['harness-eng'],
            }));
        }
    }
    return findings;
}
// Check: Document Freshness
export function checkDocFreshness(filePath) {
    const findings = [];
    if (filePath.endsWith('.md')) {
        const content = fs.readFileSync(filePath, 'utf-8');
        // Check for outdated markers
        const outdatedMatches = content.match(/\[OUTDATED\]|\[DEPRECATED\]/gi);
        if (outdatedMatches) {
            findings.push(createFinding({
                severity: 'P1',
                message: 'Document contains outdated/deprecated markers',
                file: filePath,
                blocking: false,
                category: 'doc_integrity',
                problem_class: 'stale_content',
                required_skills: ['harness-ops'],
            }));
        }
        // Check for broken links (simplified)
        const linkMatches = content.match(/\[([^\]]+)\]\(([^)]+)\)/g);
        if (linkMatches) {
            for (const link of linkMatches) {
                const urlMatch = link.match(/\(([^)]+)\)/);
                if (urlMatch && urlMatch[1].startsWith('http')) {
                    // External links need verification
                    findings.push(createFinding({
                        severity: 'P2',
                        message: `External link needs verification: ${urlMatch[1]}`,
                        file: filePath,
                        blocking: false,
                        category: 'doc_integrity',
                        problem_class: 'link_verification',
                        required_skills: [],
                    }));
                }
            }
        }
    }
    return findings;
}
// Check: Skill Parity
export function checkSkillParity(filePath) {
    const findings = [];
    // Check if skills are properly exported
    if (filePath.includes('skills')) {
        const content = fs.readFileSync(filePath, 'utf-8');
        // Check for missing exports
        if (!content.includes('export')) {
            findings.push(createFinding({
                severity: 'P2',
                message: 'Skill file should export functions',
                file: filePath,
                blocking: false,
                category: 'contract_drift',
                problem_class: 'missing_export',
                required_skills: ['harness-eng'],
            }));
        }
    }
    return findings;
}
// Check: Versions
export function checkVersions(filePath) {
    const findings = [];
    if (filePath === 'package.json') {
        const content = fs.readFileSync(filePath, 'utf-8');
        try {
            const pkg = JSON.parse(content);
            // Check for version consistency
            if (!pkg.version) {
                findings.push(createFinding({
                    severity: 'P1',
                    message: 'package.json missing version field',
                    file: filePath,
                    blocking: true,
                    category: 'version_drift',
                    problem_class: 'missing_version',
                    required_skills: ['harness-ops'],
                }));
            }
            // Check for missing dependencies
            if (!pkg.dependencies || Object.keys(pkg.dependencies).length === 0) {
                findings.push(createFinding({
                    severity: 'P2',
                    message: 'package.json has no dependencies',
                    file: filePath,
                    blocking: false,
                    category: 'version_drift',
                    problem_class: 'missing_deps',
                    required_skills: [],
                }));
            }
        }
        catch {
            findings.push(createFinding({
                severity: 'P0',
                message: 'package.json is invalid JSON',
                file: filePath,
                blocking: true,
                category: 'governance_bootstrap',
                problem_class: 'parse_error',
                required_skills: ['harness-ops'],
            }));
        }
    }
    return findings;
}
// Check: Hook Installed
export function checkHookInstalled(filePath) {
    const findings = [];
    if (filePath.includes('.boll/settings')) {
        const content = fs.readFileSync(filePath, 'utf-8');
        // Check for required hooks
        const requiredHooks = ['PostToolUse', 'PreToolUse', 'Stop', 'SessionEnd'];
        for (const hook of requiredHooks) {
            if (!content.includes(hook)) {
                findings.push(createFinding({
                    severity: 'P1',
                    message: `Required hook ${hook} not found in settings`,
                    file: filePath,
                    blocking: false,
                    category: 'governance_bootstrap',
                    problem_class: 'missing_hook',
                    required_skills: ['harness-ops'],
                }));
            }
        }
    }
    return findings;
}
// Check: Artifact Linkage
export function checkArtifactLink(filePath) {
    const findings = [];
    if (filePath.endsWith('.md') || filePath.includes('decisions')) {
        const content = fs.readFileSync(filePath, 'utf-8');
        // Check for ADR references
        const adrMatches = content.match(/ADR-\d+/gi);
        if (adrMatches && adrMatches.length > 5) {
            findings.push(createFinding({
                severity: 'P2',
                message: `Found ${adrMatches.length} ADR references - verify all are linked`,
                file: filePath,
                blocking: false,
                category: 'artifact_linkage',
                problem_class: 'link_verification',
                required_skills: ['lead'],
            }));
        }
    }
    return findings;
}
/**
 * All available checks
 */
export const ALL_CHECKS = {
    check_api_types: checkApiTypes,
    check_doc_freshness: checkDocFreshness,
    check_skill_parity: checkSkillParity,
    check_versions: checkVersions,
    check_hook_installed: checkHookInstalled,
    check_artifact_link: checkArtifactLink,
};
/**
 * Route file path to relevant checks
 * Adapted from bollharness's route() function
 */
export function route(filePath) {
    const matched = [];
    const sortedPatterns = Object.keys(GUARD_MAP).sort((a, b) => b.length - a.length);
    for (const pattern of sortedPatterns) {
        if (filePath.startsWith(pattern) || filePath === pattern.replace(/\/$/, '')) {
            matched.push(...GUARD_MAP[pattern]);
        }
    }
    if (matched.length === 0) {
        return [...DEFAULT_GUARDS];
    }
    return [...new Set(matched)];
}
/**
 * Run guards for a file
 */
export async function runGuards(filePath) {
    const guardNames = route(filePath);
    const findings = [];
    for (const name of guardNames) {
        const checkFn = ALL_CHECKS[name];
        if (checkFn) {
            try {
                const result = checkFn(filePath);
                for (const finding of result) {
                    findings.push({
                        ...finding,
                        checkName: name,
                        checkPath: filePath,
                    });
                }
            }
            catch (error) {
                findings.push({
                    severity: 'P0',
                    message: `Guard ${name} failed: ${error}`,
                    file: filePath,
                    blocking: true,
                    category: 'governance_bootstrap',
                    problem_class: 'guard_error',
                    required_skills: ['harness-ops'],
                    required_reads: [],
                    checkName: name,
                    checkPath: filePath,
                });
            }
        }
    }
    const blockingCount = findings.filter(f => f.blocking).length;
    const severities = findings.map(f => f.severity);
    const severityOrder = { P0: 0, P1: 1, P2: 2 };
    const highestSeverity = severities.length > 0
        ? severities.sort((a, b) => severityOrder[a] - severityOrder[b])[0]
        : null;
    return {
        passed: blockingCount === 0,
        findings,
        blockingCount,
        highestSeverity,
    };
}
/**
 * GuardChecker class for integration with Bolloon's constraint system
 */
export class GuardChecker {
    checksDir;
    enabledChecks;
    constructor(checksDir) {
        this.checksDir = checksDir || process.cwd();
        this.enabledChecks = new Set(Object.keys(ALL_CHECKS));
    }
    enableCheck(name) {
        this.enabledChecks.add(name);
    }
    disableCheck(name) {
        this.enabledChecks.delete(name);
    }
    async check(filePath) {
        const guardNames = route(filePath).filter(name => this.enabledChecks.has(name));
        const findings = [];
        for (const name of guardNames) {
            const checkFn = ALL_CHECKS[name];
            if (checkFn) {
                const result = checkFn(filePath);
                findings.push(...result.map(f => ({
                    ...f,
                    checkName: name,
                    checkPath: filePath,
                })));
            }
        }
        const blockingCount = findings.filter(f => f.blocking).length;
        return {
            passed: blockingCount === 0,
            findings,
            blockingCount,
            highestSeverity: findings.length > 0 ? 'P1' : null,
        };
    }
}
