"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.featureFlagsPathRelative = exports.rollbackPlanPathRelative = void 0;
exports.getRollbackPlanPath = getRollbackPlanPath;
exports.getFeatureFlagsPath = getFeatureFlagsPath;
exports.buildDefaultRollbackPlanTemplate = buildDefaultRollbackPlanTemplate;
exports.buildDefaultFeatureFlagsTemplate = buildDefaultFeatureFlagsTemplate;
exports.ensureRollbackPlanFile = ensureRollbackPlanFile;
exports.ensureFeatureFlagsFile = ensureFeatureFlagsFile;
exports.ensureSafetyControlArtifacts = ensureSafetyControlArtifacts;
exports.detectMissingSafetyControls = detectMissingSafetyControls;
const path = __importStar(require("node:path"));
const node_fs_1 = require("node:fs");
exports.rollbackPlanPathRelative = '.archpilot/rollout/rollback-plan.md';
exports.featureFlagsPathRelative = '.archpilot/rollout/feature-flags.json';
function toAbsolute(workspaceRoot, relativePath) {
    return path.join(workspaceRoot, ...relativePath.split('/'));
}
function getRollbackPlanPath(workspaceRoot) {
    return toAbsolute(workspaceRoot, exports.rollbackPlanPathRelative);
}
function getFeatureFlagsPath(workspaceRoot) {
    return toAbsolute(workspaceRoot, exports.featureFlagsPathRelative);
}
function buildDefaultRollbackPlanTemplate() {
    return `# Rollback Plan

## Change
Describe the architecture change this rollback plan covers.

## Rollback Trigger
Define deterministic conditions that trigger rollback.

## Rollback Steps
1. Revert to the previous stable architecture boundary state.
2. Restore impacted contracts and entrypoints.
3. Re-run \`archpilot validate\` to confirm governance state.

## Owner
- Team: architecture-team
- Primary owner: <name>

## Validation After Rollback
- \`archpilot validate\`
- Verify critical architecture violations are resolved.
`;
}
function buildDefaultFeatureFlagsTemplate() {
    return `${JSON.stringify({
        enabled: true,
        flags: [
            {
                name: 'architecture-change-rollout',
                description: 'Controls staged rollout for architecture changes.',
                stage: 'canary',
            },
        ],
    }, null, 2)}\n`;
}
async function writeFileIfMissing(filePath, contents) {
    try {
        await node_fs_1.promises.access(filePath);
        return false;
    }
    catch {
        await node_fs_1.promises.mkdir(path.dirname(filePath), { recursive: true });
        await node_fs_1.promises.writeFile(filePath, contents, { encoding: 'utf8' });
        return true;
    }
}
async function ensureRollbackPlanFile(workspaceRoot) {
    const filePath = getRollbackPlanPath(workspaceRoot);
    return {
        path: filePath,
        created: await writeFileIfMissing(filePath, buildDefaultRollbackPlanTemplate()),
    };
}
async function ensureFeatureFlagsFile(workspaceRoot) {
    const filePath = getFeatureFlagsPath(workspaceRoot);
    return {
        path: filePath,
        created: await writeFileIfMissing(filePath, buildDefaultFeatureFlagsTemplate()),
    };
}
async function ensureSafetyControlArtifacts(workspaceRoot) {
    const rollbackPlan = await ensureRollbackPlanFile(workspaceRoot);
    const featureFlags = await ensureFeatureFlagsFile(workspaceRoot);
    return { rollbackPlan, featureFlags };
}
async function fileExists(filePath) {
    try {
        await node_fs_1.promises.access(filePath);
        return true;
    }
    catch {
        return false;
    }
}
async function rollbackPlanPresent(filePath) {
    if (!(await fileExists(filePath))) {
        return { present: false, detail: 'rollback plan file is missing' };
    }
    try {
        const content = await node_fs_1.promises.readFile(filePath, { encoding: 'utf8' });
        return content.trim().length > 0
            ? { present: true }
            : { present: false, detail: 'rollback plan file is empty' };
    }
    catch (error) {
        return {
            present: false,
            detail: error instanceof Error ? error.message : 'rollback plan could not be read',
        };
    }
}
async function featureFlagsPresent(filePath) {
    if (!(await fileExists(filePath))) {
        return { present: false, valid: false, detail: 'feature flag config file is missing' };
    }
    let content;
    try {
        content = await node_fs_1.promises.readFile(filePath, { encoding: 'utf8' });
    }
    catch (error) {
        return {
            present: true,
            valid: false,
            detail: error instanceof Error ? error.message : 'feature flag config could not be read',
        };
    }
    let parsed;
    try {
        parsed = JSON.parse(content);
    }
    catch {
        return {
            present: true,
            valid: false,
            detail: 'feature flag config is invalid JSON',
        };
    }
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
        return {
            present: true,
            valid: false,
            detail: 'feature flag config must be a JSON object',
        };
    }
    const typed = parsed;
    const enabled = typed.enabled === true;
    const flagEntries = Array.isArray(typed.flags) ? typed.flags.length : 0;
    if (!enabled && flagEntries === 0) {
        return {
            present: true,
            valid: false,
            detail: 'feature flag config must define flags[] entries or enabled=true',
        };
    }
    return { present: true, valid: true };
}
function buildTriggerReasons(context) {
    const reasons = [];
    if (context.adrRequirement.required) {
        reasons.push('ADR follow-up is required for this run.');
    }
    if (context.enforcement.action === 'require-adr' || context.enforcement.action === 'fail') {
        reasons.push(`Enforcement outcome is ${context.enforcement.action.toUpperCase()}, requiring governance safety review.`);
    }
    if (context.risk.level === 'high' || context.risk.level === 'critical') {
        reasons.push(`Governance risk is ${context.risk.level.toUpperCase()}, requiring safety controls verification.`);
    }
    if (context.changeRisk.riskLevel !== 'low' &&
        context.changeRisk.signals.diffContextAvailable === true) {
        reasons.push(`Diff-aware change risk is ${context.changeRisk.riskLevel.toUpperCase()}, requiring rollout safety controls.`);
    }
    const structuralFindingCount = context.findings.filter((finding) => (finding.category === 'architecture' || finding.category === 'dependency') &&
        finding.kind === 'violation' &&
        (finding.severity === 'error' || finding.severity === 'warning')).length;
    if (structuralFindingCount > 0) {
        reasons.push(`Detected ${structuralFindingCount} structural/dependency violation(s) in this run.`);
    }
    return reasons;
}
function toAdrStatus(context) {
    if (!context.adrRequirement.required) {
        return 'not-required';
    }
    return context.adrRequirement.satisfied ? 'required-satisfied' : 'required-missing';
}
async function detectMissingSafetyControls(input) {
    const { workspaceRoot, context } = input;
    const triggerReasons = buildTriggerReasons(context);
    const triggered = triggerReasons.length > 0;
    const relatedRiskLevel = context.risk.level.toUpperCase();
    const relatedEnforcementDecision = context.enforcement.enforcementDecision ?? context.enforcement.action.toUpperCase();
    const relatedAdrStatus = toAdrStatus(context);
    if (!triggered) {
        return {
            modelVersion: 1,
            applicable: false,
            triggered: false,
            triggerReasons: [],
            controlsChecked: [],
            missingControls: [],
            findings: [],
            summary: 'Safety controls review not triggered for this run.',
        };
    }
    const rollbackPath = getRollbackPlanPath(workspaceRoot);
    const featureFlagsPath = getFeatureFlagsPath(workspaceRoot);
    const rollbackEvidence = await rollbackPlanPresent(rollbackPath);
    const featureFlagsEvidence = await featureFlagsPresent(featureFlagsPath);
    const findings = [];
    const controlsChecked = [];
    const adrRequired = context.adrRequirement.required;
    const adrMissing = adrRequired && !context.adrRequirement.satisfied;
    controlsChecked.push({
        controlType: 'adr',
        label: 'ADR coverage',
        required: adrRequired,
        status: !adrRequired ? 'not-applicable' : adrMissing ? 'missing' : 'present',
        description: adrRequired
            ? 'ADR requirement signal from current policy/enforcement context.'
            : 'ADR follow-up not required for this run.',
    });
    if (adrMissing) {
        findings.push({
            ruleId: 'AP-SAFE-001',
            title: 'Missing ADR coverage for risky architecture change',
            severity: 'warning',
            category: 'architecture',
            controlType: 'adr',
            message: 'ADR is required but no satisfying ADR coverage was detected.',
            whyItMatters: 'Risky architecture changes should be documented to keep decisions, tradeoffs, and approvals reviewable.',
            howToFix: 'Create or update the ADR referenced by ArchPilot ADR requirement guidance, then re-run validation.',
            relatedRiskLevel,
            relatedEnforcementDecision,
            relatedAdrStatus,
        });
    }
    controlsChecked.push({
        controlType: 'rollback',
        label: 'Rollback plan',
        required: true,
        status: rollbackEvidence.present ? 'present' : 'missing',
        description: 'Rollback evidence artifact is required when safety controls review is triggered.',
        evidencePath: exports.rollbackPlanPathRelative,
        ...(rollbackEvidence.detail ? { evidenceDetail: rollbackEvidence.detail } : {}),
    });
    if (!rollbackEvidence.present) {
        findings.push({
            ruleId: 'AP-SAFE-002',
            title: 'Missing rollback plan for risky architecture change',
            severity: 'warning',
            category: 'architecture',
            controlType: 'rollback',
            message: 'Rollback evidence is missing for a risky architecture change context.',
            whyItMatters: 'Rollback preparation reduces governance and operational risk when architecture changes need fast reversal.',
            howToFix: 'Create .archpilot/rollout/rollback-plan.md with trigger conditions, rollback steps, owner, and validation steps.',
            evidencePath: exports.rollbackPlanPathRelative,
            relatedRiskLevel,
            relatedEnforcementDecision,
            relatedAdrStatus,
        });
    }
    controlsChecked.push({
        controlType: 'feature-flag',
        label: 'Feature flag / staged rollout evidence',
        required: true,
        status: !featureFlagsEvidence.present
            ? 'missing'
            : featureFlagsEvidence.valid
                ? 'present'
                : 'invalid',
        description: 'Feature flag or staged rollout evidence artifact is required when safety controls review is triggered.',
        evidencePath: exports.featureFlagsPathRelative,
        ...(featureFlagsEvidence.detail ? { evidenceDetail: featureFlagsEvidence.detail } : {}),
    });
    if (!featureFlagsEvidence.present || !featureFlagsEvidence.valid) {
        findings.push({
            ruleId: 'AP-SAFE-003',
            title: 'Missing feature-flag or staged rollout evidence for risky architecture change',
            severity: 'warning',
            category: 'architecture',
            controlType: 'feature-flag',
            message: !featureFlagsEvidence.present
                ? 'Feature flag/staged rollout evidence artifact is missing.'
                : `Feature flag/staged rollout evidence is invalid: ${featureFlagsEvidence.detail ?? 'invalid configuration'}.`,
            whyItMatters: 'Staged rollout controls reduce blast radius when introducing architecture changes with uncertain impact.',
            howToFix: 'Create or fix .archpilot/rollout/feature-flags.json with enabled=true or non-empty flags[] entries.',
            evidencePath: exports.featureFlagsPathRelative,
            relatedRiskLevel,
            relatedEnforcementDecision,
            relatedAdrStatus,
        });
    }
    return {
        modelVersion: 1,
        applicable: true,
        triggered: true,
        triggerReasons,
        controlsChecked,
        missingControls: [...new Set(findings.map((finding) => finding.controlType))],
        findings,
        summary: findings.length === 0
            ? 'Safety controls review triggered; required controls are present.'
            : `Safety controls review triggered; ${findings.length} missing control finding(s) detected.`,
    };
}
//# sourceMappingURL=safetyControlsDetection.js.map