"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runPlanningArtifactMetadataCheck = runPlanningArtifactMetadataCheck;
exports.runLightweightArtifactStrictnessCheck = runLightweightArtifactStrictnessCheck;
exports.runPlanningCliProvenanceCheck = runPlanningCliProvenanceCheck;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_child_process_1 = require("node:child_process");
const output_1 = require("../output");
const local_paths_1 = require("./local-paths");
const workflow_provenance_1 = require("../planning/workflow-provenance");
const front_matter_utils_1 = require("../planning/front-matter-utils");
const WORKFLOW_PROVENANCE_ENFORCEMENT_CUTOFF_ISO = "2026-06-18T00:00:00.000Z";
// Older planning artifacts used `manual` before the stricter manual-exception token existed.
const LEGACY_WORKFLOW_PROVENANCE_ALIASES = new Set(["manual"]);
function runPlanningArtifactMetadataCheck(resolvedConfig, findings) {
    const planningRoot = resolvedConfig.derived.planningRootPath;
    let metadataIssueCount = 0;
    if (!(0, local_paths_1.isExistingDirectory)(planningRoot)) {
        return {
            id: "planning-artifact-metadata",
            status: "ok",
            message: "Planning root does not exist; metadata check is skipped."
        };
    }
    try {
        scanPlanningArtifactsForMetadataIssues(planningRoot, "", findings, (count) => {
            metadataIssueCount = count;
        });
    }
    catch {
        return {
            id: "planning-artifact-metadata",
            status: "ok",
            message: "Metadata check encountered an error; skipping detailed validation."
        };
    }
    if (metadataIssueCount > 0) {
        return {
            id: "planning-artifact-metadata",
            status: "error",
            message: `Found ${metadataIssueCount} planning artifact metadata issue${metadataIssueCount === 1 ? "" : "s"}; command-critical artifacts must include valid front matter.`
        };
    }
    return {
        id: "planning-artifact-metadata",
        status: "ok",
        message: "Planning artifact metadata appears valid."
    };
}
function runLightweightArtifactStrictnessCheck(resolvedConfig, findings) {
    const planningRoot = resolvedConfig.derived.planningRootPath;
    let strictnessIssueCount = 0;
    if (!(0, local_paths_1.isExistingDirectory)(planningRoot)) {
        return {
            id: "lightweight-artifact-strictness",
            status: "ok",
            message: "Planning root does not exist; lightweight strictness check is skipped."
        };
    }
    try {
        strictnessIssueCount = scanLightweightArtifactsForStrictness(planningRoot, "", findings);
    }
    catch {
        return {
            id: "lightweight-artifact-strictness",
            status: "ok",
            message: "Lightweight strictness check encountered an error; skipping detailed validation."
        };
    }
    if (strictnessIssueCount > 0) {
        return {
            id: "lightweight-artifact-strictness",
            status: "warning",
            message: `Found ${strictnessIssueCount} lightweight artifact${strictnessIssueCount === 1 ? "" : "s"} missing strict-mode justification; add lightweight_justification in standard mode.`
        };
    }
    return {
        id: "lightweight-artifact-strictness",
        status: "ok",
        message: "Lightweight artifacts satisfy strictness requirements."
    };
}
function runPlanningCliProvenanceCheck(resolvedConfig, findings) {
    const planningRoot = resolvedConfig.derived.planningRootPath;
    const repositoryRoot = resolvedConfig.derived.repositoryRoot;
    const grandfatheringContext = buildProvenanceGrandfatheringContext(repositoryRoot, planningRoot);
    let warningCount = 0;
    let errorCount = 0;
    if (!(0, local_paths_1.isExistingDirectory)(planningRoot)) {
        return {
            id: "planning-cli-provenance",
            status: "ok",
            message: "Planning root does not exist; CLI provenance check is skipped."
        };
    }
    try {
        const result = scanPlanningArtifactsForCliProvenance(planningRoot, "", findings, grandfatheringContext);
        warningCount = result.warningCount;
        errorCount = result.errorCount;
    }
    catch {
        return {
            id: "planning-cli-provenance",
            status: "ok",
            message: "CLI provenance check encountered an error; skipping detailed validation."
        };
    }
    if (errorCount > 0) {
        return {
            id: "planning-cli-provenance",
            status: "error",
            message: `Found ${errorCount} blocking CLI provenance issue${errorCount === 1 ? "" : "s"} in planning artifacts.`
        };
    }
    if (warningCount > 0) {
        return {
            id: "planning-cli-provenance",
            status: "warning",
            message: `Found ${warningCount} grandfathered legacy planning artifact${warningCount === 1 ? "" : "s"} missing CLI provenance evidence; backfill workflow provenance when feasible.`
        };
    }
    return {
        id: "planning-cli-provenance",
        status: "ok",
        message: "Planning artifacts include valid CLI provenance or explicit manual exceptions."
    };
}
function scanPlanningArtifactsForMetadataIssues(currentPath, relativePath, findings, updateCount) {
    const entries = node_fs_1.default.readdirSync(currentPath, { withFileTypes: true });
    let issueCount = 0;
    for (const entry of entries) {
        const entryPath = node_path_1.default.join(currentPath, entry.name);
        const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
        if (entry.isFile() && entry.name.endsWith(".md")) {
            const isArtifactFile = [
                "overview.md",
                "reconciliation.md",
                "task-definition.md",
                "implementation-summary.md"
            ].includes(entry.name);
            if (isArtifactFile) {
                try {
                    const content = node_fs_1.default.readFileSync(entryPath, "utf8");
                    if (!content.startsWith("---")) {
                        issueCount++;
                        findings.push((0, output_1.createBlockingValidationFinding)({
                            code: "ARTIFACT_MISSING_FRONTMATTER",
                            message: "Planning artifact is missing required YAML front matter.",
                            path: entryRelativePath,
                            blockingReason: "invalid-command-input",
                            recovery: "Add valid planning artifact front matter through an explicit metadata or planning update before rerunning validation rollup.",
                            evidence: {
                                category: "validation-rollup",
                                details: {
                                    source: "planning-artifact-metadata"
                                }
                            }
                        }));
                    }
                    else {
                        const endMarker = content.indexOf("\n---\n");
                        if (endMarker === -1) {
                            issueCount++;
                            findings.push((0, output_1.createBlockingValidationFinding)({
                                code: "ARTIFACT_MALFORMED_FRONTMATTER",
                                message: "Planning artifact front matter is not properly closed.",
                                path: entryRelativePath,
                                blockingReason: "invalid-command-input",
                                recovery: "Close the planning artifact front matter through an explicit metadata or planning update before rerunning validation rollup.",
                                evidence: {
                                    category: "validation-rollup",
                                    details: {
                                        source: "planning-artifact-metadata"
                                    }
                                }
                            }));
                        }
                    }
                }
                catch {
                }
            }
        }
        else if (entry.isDirectory() && !entry.name.startsWith(".")) {
            scanPlanningArtifactsForMetadataIssues(entryPath, entryRelativePath, findings, (count) => {
                issueCount += count;
            });
        }
    }
    updateCount(issueCount);
}
function scanLightweightArtifactsForStrictness(currentPath, relativePath, findings) {
    const entries = node_fs_1.default.readdirSync(currentPath, { withFileTypes: true });
    let issueCount = 0;
    for (const entry of entries) {
        const entryPath = node_path_1.default.join(currentPath, entry.name);
        const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
        if (entry.isFile() && entry.name === "task-definition.md") {
            try {
                const content = node_fs_1.default.readFileSync(entryPath, "utf8");
                const frontMatterLines = readFrontMatterLines(content);
                if (readTopLevelField(frontMatterLines, "planning_depth") === "lightweight") {
                    if (readTopLevelField(frontMatterLines, "lightweight_justification") === undefined) {
                        issueCount++;
                        findings.push((0, output_1.createAdvisoryValidationFinding)({
                            code: "LIGHTWEIGHT_MISSING_JUSTIFICATION",
                            message: "Lightweight task definition is missing lightweight_justification field.",
                            path: entryRelativePath,
                            advisoryReason: "weak-evidence",
                            recovery: "Review the lightweight task evidence and add lightweight_justification through an explicit metadata or planning update if the exception remains valid."
                        }));
                    }
                }
            }
            catch {
            }
        }
        else if (entry.isDirectory() && !entry.name.startsWith(".")) {
            issueCount += scanLightweightArtifactsForStrictness(entryPath, entryRelativePath, findings);
        }
    }
    return issueCount;
}
function readFrontMatterLines(content) {
    if (!content.startsWith("---")) {
        return [];
    }
    const endMarker = content.indexOf("\n---\n");
    if (endMarker === -1) {
        return [];
    }
    return content
        .slice(4, endMarker)
        .split(/\r?\n/);
}
function scanPlanningArtifactsForCliProvenance(currentPath, relativePath, findings, grandfatheringContext) {
    const entries = node_fs_1.default.readdirSync(currentPath, { withFileTypes: true });
    let warningCount = 0;
    let errorCount = 0;
    for (const entry of entries) {
        const entryPath = node_path_1.default.join(currentPath, entry.name);
        const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
        if (entry.isFile() && entry.name.endsWith(".md")) {
            const validation = validateWorkflowProvenance(entryPath, entryRelativePath, findings, grandfatheringContext);
            warningCount += validation.warningCount;
            errorCount += validation.errorCount;
            continue;
        }
        if (entry.isDirectory() && !entry.name.startsWith(".")) {
            const nested = scanPlanningArtifactsForCliProvenance(entryPath, entryRelativePath, findings, grandfatheringContext);
            warningCount += nested.warningCount;
            errorCount += nested.errorCount;
        }
    }
    return { warningCount, errorCount };
}
function validateWorkflowProvenance(artifactPath, artifactRelativePath, findings, grandfatheringContext) {
    const artifactName = node_path_1.default.basename(artifactPath);
    const isArtifactFile = [
        "overview.md",
        "slice-overview.md",
        "phase-overview.md",
        "milestone-overview.md",
        "reconciliation.md",
        "task-definition.md",
        "implementation-summary.md"
    ].includes(artifactName);
    if (!isArtifactFile) {
        return { warningCount: 0, errorCount: 0 };
    }
    let content;
    try {
        content = node_fs_1.default.readFileSync(artifactPath, "utf8");
    }
    catch {
        return { warningCount: 0, errorCount: 0 };
    }
    if (!content.startsWith("---")) {
        return { warningCount: 0, errorCount: 0 };
    }
    const lines = content.split(/\r?\n/);
    const closingIndex = lines.findIndex((line, index) => index > 0 && line === "---");
    if (closingIndex === -1) {
        return { warningCount: 0, errorCount: 0 };
    }
    const frontMatterLines = lines.slice(1, closingIndex);
    const artifactType = readTopLevelField(frontMatterLines, "artifact_type");
    if (!isCommandSupportedPlanningArtifactType(artifactType)) {
        return { warningCount: 0, errorCount: 0 };
    }
    const workflowProvenance = readTopLevelField(frontMatterLines, "workflow_provenance")?.trim();
    const workflowCliCommand = readTopLevelField(frontMatterLines, "workflow_cli_command")?.trim() ?? "";
    const workflowManualException = readTopLevelField(frontMatterLines, "workflow_manual_exception")?.trim() ?? "";
    if ((workflowProvenance === undefined || workflowProvenance === "")
        && isGrandfatheredLegacyPlanningArtifact(artifactRelativePath, grandfatheringContext)) {
        return { warningCount: 0, errorCount: 0 };
    }
    const sharedProvenanceIssues = (0, workflow_provenance_1.inspectPlanningWorkflowProvenance)({
        artifactType,
        workflowProvenance: workflowProvenance ?? "",
        workflowCliCommand,
        workflowManualException,
        allowLegacyManualAlias: true
    });
    if (sharedProvenanceIssues.length > 0) {
        for (const issue of sharedProvenanceIssues) {
            findings.push((0, output_1.createBlockingValidationFinding)({
                code: issue.code,
                message: issue.message,
                path: artifactRelativePath,
                blockingReason: "invalid-command-input",
                recovery: "Correct planning workflow provenance through an explicit planning update before rerunning validation rollup.",
                evidence: {
                    category: "validation-rollup",
                    details: {
                        source: "planning-authoring-policy"
                    }
                }
            }));
        }
        return { warningCount: 0, errorCount: sharedProvenanceIssues.length };
    }
    if (workflowProvenance === undefined || workflowProvenance === "") {
        if (isGrandfatheredLegacyPlanningArtifact(artifactRelativePath, grandfatheringContext)) {
            return { warningCount: 0, errorCount: 0 };
        }
        findings.push((0, output_1.createBlockingValidationFinding)({
            code: "PLANNING_WORKFLOW_PROVENANCE_MISSING",
            message: "Planning artifact is missing workflow provenance. Record CLI-first evidence or an allowed manual exception.",
            path: artifactRelativePath,
            blockingReason: "invalid-command-input",
            recovery: "Record CLI-first workflow provenance or an allowed manual exception through an explicit planning update before rerunning validation rollup.",
            evidence: {
                category: "validation-rollup",
                details: {
                    source: "planning-cli-provenance"
                }
            }
        }));
        return { warningCount: 0, errorCount: 1 };
    }
    if (workflowProvenance === "cli") {
        if (workflowCliCommand === "") {
            findings.push((0, output_1.createBlockingValidationFinding)({
                code: "PLANNING_WORKFLOW_CLI_COMMAND_MISSING",
                message: "CLI-authored planning artifact is missing workflow_cli_command.",
                path: artifactRelativePath,
                blockingReason: "invalid-command-input",
                recovery: "Record the CLI command that produced the planning artifact through an explicit planning update before rerunning validation rollup.",
                evidence: {
                    category: "validation-rollup",
                    details: {
                        source: "planning-cli-provenance"
                    }
                }
            }));
            return { warningCount: 0, errorCount: 1 };
        }
        const expectedPrefixes = (0, workflow_provenance_1.expectedWorkflowCommandPrefixes)(artifactType);
        if (!expectedPrefixes.some((prefix) => workflowCliCommand.startsWith(prefix))) {
            findings.push((0, output_1.createBlockingValidationFinding)({
                code: "PLANNING_WORKFLOW_CLI_COMMAND_INVALID",
                message: `workflow_cli_command '${workflowCliCommand}' does not match the expected command family for artifact_type '${artifactType}'.`,
                path: artifactRelativePath,
                blockingReason: "invalid-command-input",
                recovery: "Correct workflow_cli_command through an explicit planning update before rerunning validation rollup.",
                evidence: {
                    category: "validation-rollup",
                    details: {
                        source: "planning-cli-provenance"
                    }
                }
            }));
            return { warningCount: 0, errorCount: 1 };
        }
        if (workflowManualException !== "") {
            findings.push((0, output_1.createBlockingValidationFinding)({
                code: "PLANNING_WORKFLOW_EXCEPTION_UNEXPECTED",
                message: "CLI-authored planning artifact must not record workflow_manual_exception.",
                path: artifactRelativePath,
                blockingReason: "invalid-command-input",
                recovery: "Remove workflow_manual_exception from CLI-authored planning metadata through an explicit planning update before rerunning validation rollup.",
                evidence: {
                    category: "validation-rollup",
                    details: {
                        source: "planning-cli-provenance"
                    }
                }
            }));
            return { warningCount: 0, errorCount: 1 };
        }
        return { warningCount: 0, errorCount: 0 };
    }
    if (workflowProvenance === "manual-exception") {
        if (workflowCliCommand === "") {
            findings.push((0, output_1.createBlockingValidationFinding)({
                code: "PLANNING_WORKFLOW_CLI_COMMAND_MISSING",
                message: "Manual-exception planning artifact must record the skipped CLI command in workflow_cli_command.",
                path: artifactRelativePath,
                blockingReason: "invalid-command-input",
                recovery: "Record the skipped CLI command through an explicit planning update before rerunning validation rollup.",
                evidence: {
                    category: "validation-rollup",
                    details: {
                        source: "planning-cli-provenance"
                    }
                }
            }));
            return { warningCount: 0, errorCount: 1 };
        }
        if (!(0, workflow_provenance_1.isAllowedManualWorkflowException)(workflowManualException)) {
            findings.push((0, output_1.createBlockingValidationFinding)({
                code: "PLANNING_WORKFLOW_EXCEPTION_INVALID",
                message: "workflow_manual_exception must be one of: unsupported by CLI, CLI failed, manual recovery after scaffold verification.",
                path: artifactRelativePath,
                blockingReason: "invalid-command-input",
                recovery: "Record an allowed workflow_manual_exception value through an explicit planning update before rerunning validation rollup.",
                evidence: {
                    category: "validation-rollup",
                    details: {
                        source: "planning-cli-provenance"
                    }
                }
            }));
            return { warningCount: 0, errorCount: 1 };
        }
        const companionValidation = validateManualExceptionCompanionArtifacts({
            artifactPath,
            artifactRelativePath,
            artifactType,
            findings
        });
        if (companionValidation.errorCount > 0) {
            return companionValidation;
        }
        return { warningCount: 0, errorCount: 0 };
    }
    if (LEGACY_WORKFLOW_PROVENANCE_ALIASES.has(workflowProvenance)) {
        return { warningCount: 0, errorCount: 0 };
    }
    findings.push((0, output_1.createBlockingValidationFinding)({
        code: "PLANNING_WORKFLOW_PROVENANCE_INVALID",
        message: "workflow_provenance must be 'cli' or 'manual-exception'.",
        path: artifactRelativePath,
        blockingReason: "invalid-command-input",
        recovery: "Correct workflow_provenance through an explicit planning update before rerunning validation rollup.",
        evidence: {
            category: "validation-rollup",
            details: {
                source: "planning-cli-provenance"
            }
        }
    }));
    return { warningCount: 0, errorCount: 1 };
}
function validateManualExceptionCompanionArtifacts(options) {
    const missing = expectedManualExceptionCompanionArtifacts(options.artifactPath, options.artifactType)
        .filter((companion) => !node_fs_1.default.existsSync(companion.absolutePath));
    if (missing.length === 0) {
        return { warningCount: 0, errorCount: 0 };
    }
    for (const companion of missing) {
        options.findings.push((0, output_1.createBlockingValidationFinding)({
            code: "PLANNING_WORKFLOW_MANUAL_COMPANION_MISSING",
            message: `Manual-exception planning artifact is missing required companion artifact: ${companion.relativePath}.`,
            path: options.artifactRelativePath,
            blockingReason: "broken-traceability",
            recovery: "Create the missing companion artifact through the command-supported scaffold or record a valid manual recovery after scaffold verification before rerunning validation rollup.",
            evidence: {
                category: "validation-rollup",
                details: {
                    source: "planning-cli-provenance",
                    requiredCompanion: companion.relativePath
                }
            }
        }));
    }
    return { warningCount: 0, errorCount: missing.length };
}
function expectedManualExceptionCompanionArtifacts(artifactPath, artifactType) {
    const artifactDirectory = node_path_1.default.dirname(artifactPath);
    switch (artifactType) {
        case "slice_overview":
            return [
                {
                    absolutePath: node_path_1.default.join(artifactDirectory, "decisions", ".gitkeep"),
                    relativePath: "decisions/.gitkeep"
                },
                {
                    absolutePath: node_path_1.default.join(artifactDirectory, "notes", ".gitkeep"),
                    relativePath: "notes/.gitkeep"
                }
            ];
        case "milestone_overview":
            return [
                {
                    absolutePath: node_path_1.default.join(artifactDirectory, "reconciliation.md"),
                    relativePath: "reconciliation.md"
                }
            ];
        case "task_definition":
            return [
                {
                    absolutePath: node_path_1.default.join(artifactDirectory, "implementation-summary.md"),
                    relativePath: "implementation-summary.md"
                }
            ];
        case "implementation_summary":
            return [
                {
                    absolutePath: node_path_1.default.join(artifactDirectory, "task-definition.md"),
                    relativePath: "task-definition.md"
                }
            ];
        case "reconciliation":
            return [
                {
                    absolutePath: node_path_1.default.join(artifactDirectory, "overview.md"),
                    relativePath: "overview.md"
                }
            ];
        case "phase_overview":
            return [];
        default:
            return [];
    }
}
function readTopLevelField(lines, key) {
    for (const line of lines) {
        if (/^\s/.test(line)) {
            continue;
        }
        const match = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
        if (match === null) {
            continue;
        }
        if (match[1] === key) {
            return (0, front_matter_utils_1.parseScalar)(match[2]);
        }
    }
    return undefined;
}
function buildProvenanceGrandfatheringContext(repositoryRoot, planningRoot) {
    const planningRootRelativePath = normalizeRepositoryRelativePath(node_path_1.default.relative(repositoryRoot, planningRoot));
    const legacyPlanningArtifactPaths = resolveLegacyPlanningArtifactPaths({
        repositoryRoot,
        planningRootRelativePath
    });
    return {
        planningRootRelativePath,
        legacyPlanningArtifactPaths
    };
}
function resolveLegacyPlanningArtifactPaths(input) {
    const { repositoryRoot, planningRootRelativePath } = input;
    if (planningRootRelativePath === "") {
        return new Set();
    }
    const result = (0, node_child_process_1.spawnSync)("git", [
        "log",
        "--name-only",
        "--diff-filter=A",
        `--before=${WORKFLOW_PROVENANCE_ENFORCEMENT_CUTOFF_ISO}`,
        "--pretty=format:",
        "--",
        planningRootRelativePath
    ], {
        cwd: repositoryRoot,
        encoding: "utf8"
    });
    if (result.status !== 0 || (result.error && result.stdout.trim() === "")) {
        return new Set();
    }
    const paths = result.stdout
        .split(/\r?\n/)
        .map((line) => normalizeRepositoryRelativePath(line.trim()))
        .filter((line) => line !== "");
    const legacyPaths = new Set();
    for (const legacyPath of paths) {
        legacyPaths.add(legacyPath);
        const planningRootPrefix = `${planningRootRelativePath}/`;
        if (legacyPath.startsWith(planningRootPrefix)) {
            legacyPaths.add(legacyPath.slice(planningRootPrefix.length));
        }
    }
    return legacyPaths;
}
function isGrandfatheredLegacyPlanningArtifact(artifactRelativePath, context) {
    const normalizedArtifactRelativePath = normalizeRepositoryRelativePath(artifactRelativePath);
    const repositoryRelativePath = context.planningRootRelativePath === ""
        ? normalizedArtifactRelativePath
        : `${context.planningRootRelativePath}/${normalizedArtifactRelativePath}`;
    return context.legacyPlanningArtifactPaths.has(repositoryRelativePath)
        || context.legacyPlanningArtifactPaths.has(normalizedArtifactRelativePath);
}
function normalizeRepositoryRelativePath(value) {
    return value.replace(/\\+/g, "/").replace(/^\.\//, "");
}
function isCommandSupportedPlanningArtifactType(value) {
    return value === "slice_overview"
        || value === "phase_overview"
        || value === "milestone_overview"
        || value === "task_definition"
        || value === "implementation_summary"
        || value === "reconciliation";
}
