"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runCrossArtifactAlignmentSmoke = runCrossArtifactAlignmentSmoke;
exports.runSelectedArtifactShapeChecks = runSelectedArtifactShapeChecks;
exports.createCrossArtifactLane = createCrossArtifactLane;
exports.runHeadingSectionOrderChecks = runHeadingSectionOrderChecks;
exports.runDeclaredChildSlugChecks = runDeclaredChildSlugChecks;
exports.declaredChildContractForArtifact = declaredChildContractForArtifact;
exports.runPromptRenderingLaneChecks = runPromptRenderingLaneChecks;
exports.runSourceDestinationRoutingChecks = runSourceDestinationRoutingChecks;
exports.runReviewHandoffChecks = runReviewHandoffChecks;
exports.runPlaceholderScaffoldingChecks = runPlaceholderScaffoldingChecks;
exports.runNoWriteSurfaceChecks = runNoWriteSurfaceChecks;
exports.requirePromptTemplateRefs = requirePromptTemplateRefs;
exports.requireTextSnippets = requireTextSnippets;
exports.isDayShiftSourceRepository = isDayShiftSourceRepository;
exports.readRepositoryFile = readRepositoryFile;
exports.collectPlanningMarkdownArtifacts = collectPlanningMarkdownArtifacts;
exports.readJsonObject = readJsonObject;
exports.readObjectArray = readObjectArray;
exports.readTemplateReferences = readTemplateReferences;
exports.readString = readString;
exports.readNestedString = readNestedString;
exports.isRecord = isRecord;
exports.arraysEqual = arraysEqual;
exports.statusFromFindings = statusFromFindings;
exports.statusFromPlanningGaps = statusFromPlanningGaps;
exports.validationFindingFromPlanningGap = validationFindingFromPlanningGap;
exports.createCrossArtifactFinding = createCrossArtifactFinding;
exports.loadPlanningValidationBaseline = loadPlanningValidationBaseline;
exports.isPlanningValidationBaselineRecord = isPlanningValidationBaselineRecord;
exports.filterBaselineFindings = filterBaselineFindings;
exports.filterBaselineInventoryFindings = filterBaselineInventoryFindings;
exports.filterBaselineGaps = filterBaselineGaps;
exports.buildPlanningValidationBaselineEvidence = buildPlanningValidationBaselineEvidence;
exports.applyPlanningValidationBaselineToChecks = applyPlanningValidationBaselineToChecks;
exports.applyFindingBaselineToCheck = applyFindingBaselineToCheck;
exports.fingerprintFinding = fingerprintFinding;
exports.fingerprintInventoryFinding = fingerprintInventoryFinding;
exports.fingerprintGap = fingerprintGap;
exports.stableFingerprint = stableFingerprint;
exports.derivePlanningValidationSmokeStatus = derivePlanningValidationSmokeStatus;
exports.derivePlanningValidationSmokeNextActions = derivePlanningValidationSmokeNextActions;
// Doctor report contracts, scoped execution, rendering, and validation rollup orchestration.
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const cli_constants_1 = require("../config/cli-constants");
const output_1 = require("../output");
const planning_document_utils_1 = require("../planning/planning-document-utils");
const validation_1 = require("../validation");
const contracts_1 = require("./contracts");
function runCrossArtifactAlignmentSmoke(repositoryRoot, selectedArtifactPath) {
    const lanes = [
        createCrossArtifactLane('heading-section-order', runHeadingSectionOrderChecks(repositoryRoot, selectedArtifactPath)),
        createCrossArtifactLane('declared-child-slugs', runDeclaredChildSlugChecks(repositoryRoot)),
        createCrossArtifactLane('prompt-rendering-lanes', runPromptRenderingLaneChecks(repositoryRoot)),
        createCrossArtifactLane('source-destination-routing', runSourceDestinationRoutingChecks(repositoryRoot)),
        createCrossArtifactLane('review-handoff', runReviewHandoffChecks(repositoryRoot)),
        createCrossArtifactLane('placeholder-scaffolding', runPlaceholderScaffoldingChecks(repositoryRoot)),
        createCrossArtifactLane('artifact-shape', runSelectedArtifactShapeChecks(repositoryRoot, selectedArtifactPath)),
        createCrossArtifactLane('no-write-surface', runNoWriteSurfaceChecks(repositoryRoot)),
    ];
    return {
        selectedArtifactFamily: 'planning',
        surfaceMode: 'validation-smoke',
        noWrite: true,
        findingsTaxonomy: 'shared-findings',
        lanes,
    };
}
function runSelectedArtifactShapeChecks(repositoryRoot, selectedArtifactPath) {
    if (selectedArtifactPath === undefined) {
        return [];
    }
    const artifactPath = (0, planning_document_utils_1.toRepositoryLikePath)(selectedArtifactPath).replace(/^\.\//, '');
    const content = readRepositoryFile(repositoryRoot, artifactPath);
    if (content === null) {
        return [
            createCrossArtifactFinding({
                code: 'ARTIFACT_SHAPE_INPUT_MISSING',
                message: `Cannot verify artifact shape because '${artifactPath}' is missing.`,
                severity: 'error',
                path: artifactPath,
                evidence: (0, output_1.createValidationCommandEvidence)({
                    category: 'validation-rollup',
                    details: {
                        source: 'artifact-shape',
                    },
                }),
            }),
        ];
    }
    const result = (0, validation_1.validateArtifactShape)({
        path: artifactPath,
        content,
    });
    return result.findings.map((finding) => createCrossArtifactFinding({
        code: finding.code,
        message: finding.message,
        severity: finding.severity,
        path: finding.path,
        evidence: (0, output_1.createValidationCommandEvidence)({
            category: 'validation-rollup',
            details: {
                source: 'artifact-shape',
                family: result.family.family,
            },
        }),
    }));
}
function createCrossArtifactLane(id, findings) {
    return {
        id,
        selectedArtifactFamily: 'planning',
        surfaceMode: 'validation-smoke',
        status: statusFromFindings(findings),
        findings,
    };
}
function runHeadingSectionOrderChecks(repositoryRoot, selectedArtifactPath) {
    const findings = [];
    const expectedTemplates = selectExpectedTemplateHeadings(repositoryRoot, selectedArtifactPath);
    for (const [templatePath, expectedHeadings] of expectedTemplates) {
        const content = readRepositoryFile(repositoryRoot, templatePath);
        if (content === null) {
            findings.push(createCrossArtifactFinding({
                code: 'CROSS_ARTIFACT_ALIGNMENT_INPUT_MISSING',
                message: `Cannot verify heading order because '${templatePath}' is missing.`,
                severity: 'warning',
                path: templatePath,
            }));
            continue;
        }
        const observedHeadings = content.split(/\r?\n/u).filter((line) => line.startsWith('## '));
        if (!arraysEqual(observedHeadings, expectedHeadings)) {
            findings.push(createCrossArtifactFinding({
                code: 'CROSS_ARTIFACT_HEADING_ORDER_DRIFT',
                message: `Template '${templatePath}' does not preserve the canonical H2 heading order for its artifact family.`,
                severity: 'error',
                path: templatePath,
            }));
        }
    }
    return findings;
}
function selectExpectedTemplateHeadings(repositoryRoot, selectedArtifactPath) {
    if (selectedArtifactPath === undefined) {
        return contracts_1.EXPECTED_TEMPLATE_H2_HEADINGS;
    }
    const artifactPath = (0, planning_document_utils_1.toRepositoryLikePath)(selectedArtifactPath).replace(/^\.\//, '');
    const content = readRepositoryFile(repositoryRoot, artifactPath);
    if (content === null) {
        return new Map();
    }
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(content);
    const artifactType = document.hasFrontMatter
        ? (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, 'artifact_type')?.trim() ?? null
        : null;
    const templatePath = expectedTemplatePathForArtifact(artifactPath, artifactType);
    const headings = templatePath === null
        ? undefined
        : contracts_1.EXPECTED_TEMPLATE_H2_HEADINGS.get(templatePath);
    return templatePath !== null && headings !== undefined
        ? new Map([[templatePath, headings]])
        : new Map();
}
function expectedTemplatePathForArtifact(artifactPath, artifactType) {
    const byArtifactType = {
        specification: '.day-shift/templates/specification.md',
        slice_overview: '.day-shift/templates/slice-overview.md',
        phase_overview: '.day-shift/templates/phase-overview.md',
        milestone_overview: '.day-shift/templates/milestone-overview.md',
        task_definition: '.day-shift/templates/task-definition.md',
        implementation_summary: '.day-shift/templates/implementation-summary.md',
    };
    if (artifactType !== null && byArtifactType[artifactType] !== undefined) {
        return byArtifactType[artifactType];
    }
    if (artifactType === 'reconciliation') {
        return artifactPath.endsWith('/milestone-reconciliation.md')
            ? '.day-shift/templates/milestone-reconciliation.md'
            : '.day-shift/templates/reconciliation.md';
    }
    return null;
}
function runDeclaredChildSlugChecks(repositoryRoot) {
    const findings = [];
    const planningRoot = node_path_1.default.join(repositoryRoot, '.day-shift', 'planning');
    if (!node_fs_1.default.existsSync(planningRoot)) {
        return findings;
    }
    for (const artifactPath of collectPlanningMarkdownArtifacts(planningRoot, '.day-shift/planning')) {
        if (!artifactPath.endsWith('/overview.md')) {
            continue;
        }
        const absolutePath = node_path_1.default.join(repositoryRoot, artifactPath);
        const content = readRepositoryFile(repositoryRoot, artifactPath);
        if (content === null) {
            continue;
        }
        const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(content);
        if (!document.hasFrontMatter) {
            continue;
        }
        const artifactType = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, 'artifact_type')?.trim() ?? null;
        const declaredChildContract = declaredChildContractForArtifact(document.frontMatterLines, artifactType);
        if (declaredChildContract === null) {
            continue;
        }
        const validation = (0, planning_document_utils_1.validateDeclaredChildSlugSection)(content, declaredChildContract.sectionName);
        for (const issue of validation.issues) {
            findings.push(createCrossArtifactFinding({
                code: 'CROSS_ARTIFACT_DECLARED_CHILD_SLUG_DRIFT',
                message: `${declaredChildContract.artifactLabel} '${artifactPath}' has invalid ${declaredChildContract.childLabel} declarations: ${issue.message}`,
                severity: 'error',
                path: (0, planning_document_utils_1.toRepositoryLikePath)(node_path_1.default.relative(repositoryRoot, absolutePath)),
            }));
        }
    }
    return findings;
}
function declaredChildContractForArtifact(frontMatterLines, artifactType) {
    if (artifactType === 'slice_overview') {
        const planningDepth = (0, planning_document_utils_1.readTopLevelField)(frontMatterLines, 'planning_depth')?.trim() ?? 'standard';
        if (planningDepth === 'lightweight') {
            return null;
        }
        return {
            artifactLabel: 'Slice overview',
            childLabel: 'phase',
            sectionName: 'Phases',
        };
    }
    if (artifactType === 'phase_overview') {
        return {
            artifactLabel: 'Phase overview',
            childLabel: 'milestone',
            sectionName: 'Milestones',
        };
    }
    if (artifactType === 'milestone_overview') {
        return {
            artifactLabel: 'Milestone overview',
            childLabel: 'task',
            sectionName: 'Tasks',
        };
    }
    return null;
}
function runPromptRenderingLaneChecks(repositoryRoot) {
    const findings = [];
    const enforceSourceOnlyContracts = isDayShiftSourceRepository(repositoryRoot);
    requireTextSnippets(repositoryRoot, findings, '.day-shift/specs/06-templates-and-prompts/prompts.md', [
        'A rendered prompt must keep four visible lanes distinct: generated context, agent entrypoint guidance, warnings, and prompt instructions.',
        'fail by default when required agent entrypoint guidance is missing',
        'allow `--no-agent` only as an explicit degraded mode with a visible warning',
    ], 'CROSS_ARTIFACT_PROMPT_LANE_DRIFT', {
        allowMissingInput: !enforceSourceOnlyContracts,
    });
    requireTextSnippets(repositoryRoot, findings, '.day-shift/prompts/user/manifest.json', [
        'Rendered prompt surfaces keep four visible lanes in order: generated context, agent entrypoint guidance, warnings, and prompt instructions',
        'Read-only prompt show/reporting surfaces should keep generated context, agent entrypoint guidance, warnings, and prompt instructions in separate visible lanes',
    ], 'CROSS_ARTIFACT_PROMPT_LANE_DRIFT');
    requireTextSnippets(repositoryRoot, findings, cli_constants_1.CLI_SOURCE_SPEC_PLANNING_WORKFLOW, [
        'keep four visible lanes distinct in this order: generated context, agent entrypoint guidance, warnings, then prompt instructions',
        'fail by default when that agent entrypoint is missing',
    ], 'CROSS_ARTIFACT_PROMPT_LANE_DRIFT');
    return findings;
}
function runSourceDestinationRoutingChecks(repositoryRoot) {
    const findings = [];
    const templateManifestPath = '.day-shift/templates/manifest.json';
    const promptManifestPath = '.day-shift/prompts/user/manifest.json';
    const templateManifest = readJsonObject(repositoryRoot, templateManifestPath, findings);
    const promptManifest = readJsonObject(repositoryRoot, promptManifestPath, findings);
    const templatePaths = new Set();
    if (templateManifest !== null) {
        for (const entry of readObjectArray(templateManifest, 'templates')) {
            const templatePath = readString(entry, 'path');
            const kind = readString(entry, 'kind');
            const destination = readString(entry, 'destination');
            if (templatePath !== null) {
                templatePaths.add(templatePath);
            }
            if (destination !== null && kind !== 'scaffold') {
                findings.push(createCrossArtifactFinding({
                    code: 'CROSS_ARTIFACT_DESTINATION_ROUTING_DRIFT',
                    message: `Template manifest entry '${templatePath ?? '(unknown)'}' declares destination outside the scaffold/support destination lane.`,
                    severity: 'error',
                    path: templateManifestPath,
                }));
            }
        }
    }
    if (promptManifest !== null) {
        for (const entry of readObjectArray(promptManifest, 'prompts')) {
            const promptPath = readString(entry, 'path') ?? '(unknown)';
            if (readString(entry, 'destination') !== null) {
                findings.push(createCrossArtifactFinding({
                    code: 'CROSS_ARTIFACT_PROMPT_DESTINATION_DRIFT',
                    message: `Prompt manifest entry '${promptPath}' declares destination routing; prompt template_ref/template_refs must stay source-template metadata only.`,
                    severity: 'error',
                    path: promptManifestPath,
                }));
            }
            for (const templateRef of readTemplateReferences(entry)) {
                if (!templateRef.startsWith('.day-shift/templates/')) {
                    findings.push(createCrossArtifactFinding({
                        code: 'CROSS_ARTIFACT_TEMPLATE_REF_DRIFT',
                        message: `Prompt manifest entry '${promptPath}' references '${templateRef}' outside the template source path lane.`,
                        severity: 'error',
                        path: promptManifestPath,
                    }));
                }
                else if (templateManifest !== null && !templatePaths.has(templateRef)) {
                    findings.push(createCrossArtifactFinding({
                        code: 'CROSS_ARTIFACT_TEMPLATE_REF_DRIFT',
                        message: `Prompt manifest entry '${promptPath}' references '${templateRef}', which is absent from the template manifest source inventory.`,
                        severity: 'error',
                        path: promptManifestPath,
                    }));
                }
            }
        }
    }
    const specIndex = readJsonObject(repositoryRoot, cli_constants_1.CLI_SPEC_INDEX_PATH, findings);
    if (specIndex !== null) {
        const requiredSpecEntries = new Map([
            ['templates-and-prompts-templates', 'templates-and-prompts/templates'],
            ['templates-and-prompts-prompts', 'templates-and-prompts/prompts'],
        ]);
        const specEntries = readObjectArray(specIndex, 'specs');
        const shouldEnforceTemplatesAndPromptsTraceability = specEntries.some((entry) => {
            return requiredSpecEntries.has(readString(entry, 'id') ?? '');
        }) || node_fs_1.default.existsSync(node_path_1.default.join(repositoryRoot, '.day-shift/specs/06-templates-and-prompts'));
        if (shouldEnforceTemplatesAndPromptsTraceability) {
            for (const [specId, expectedDestination] of requiredSpecEntries) {
                const entry = specEntries.find((candidate) => readString(candidate, 'id') === specId);
                if (entry === undefined) {
                    findings.push(createCrossArtifactFinding({
                        code: 'CROSS_ARTIFACT_TRACEABILITY_DRIFT',
                        message: `Spec index is missing required entry '${specId}'.`,
                        severity: 'error',
                        path: cli_constants_1.CLI_SPEC_INDEX_PATH,
                    }));
                    continue;
                }
                if (readString(entry, 'destination') !== expectedDestination ||
                    readNestedString(entry, ['source', 'ref']) !== 'root-spec.md') {
                    findings.push(createCrossArtifactFinding({
                        code: 'CROSS_ARTIFACT_TRACEABILITY_DRIFT',
                        message: `Spec index entry '${specId}' must preserve destination '${expectedDestination}' and source.ref 'root-spec.md'.`,
                        severity: 'error',
                        path: cli_constants_1.CLI_SPEC_INDEX_PATH,
                    }));
                }
            }
        }
    }
    return findings;
}
function runReviewHandoffChecks(repositoryRoot) {
    const findings = [];
    const enforceSourceOnlyContracts = isDayShiftSourceRepository(repositoryRoot);
    const promptManifestPath = '.day-shift/prompts/user/manifest.json';
    const promptManifest = readJsonObject(repositoryRoot, promptManifestPath, findings);
    if (promptManifest !== null) {
        requirePromptTemplateRefs(promptManifest, promptManifestPath, findings, '.day-shift/prompts/user/phase.next.md', [
            '.day-shift/templates/milestone-overview.md',
            '.day-shift/templates/milestone-reconciliation.md',
        ]);
        requirePromptTemplateRefs(promptManifest, promptManifestPath, findings, '.day-shift/prompts/user/milestone.next.md', ['.day-shift/templates/task-definition.md', '.day-shift/templates/implementation-summary.md']);
        requirePromptTemplateRefs(promptManifest, promptManifestPath, findings, '.day-shift/prompts/user/implementation.next.md', ['.day-shift/templates/milestone-reconciliation.md']);
        requirePromptTemplateRefs(promptManifest, promptManifestPath, findings, '.day-shift/prompts/user/reconciliation.build.md', ['.day-shift/templates/reconciliation.md']);
    }
    requireTextSnippets(repositoryRoot, findings, '.day-shift/specs/06-templates-and-prompts/prompts.md', [
        'implementation-summary.md` artifacts as the execution-evidence source of truth',
        'milestone `reconciliation.md` as the default downstream review rollup',
        'task-level reconciliation as an explicit exception path only',
    ], 'CROSS_ARTIFACT_REVIEW_HANDOFF_DRIFT', {
        allowMissingInput: !enforceSourceOnlyContracts,
    });
    requireTextSnippets(repositoryRoot, findings, '.day-shift/specs/05-planning-artifacts/artifact-model.md', [
        'Default review path: milestone-level reconciliation is the default review rollup path.',
        'Task-level reconciliation is an explicit exception',
    ], 'CROSS_ARTIFACT_REVIEW_HANDOFF_DRIFT', {
        allowMissingInput: !enforceSourceOnlyContracts,
    });
    requireTextSnippets(repositoryRoot, findings, cli_constants_1.CLI_SOURCE_SPEC_PLANNING_WORKFLOW, [
        'Preserve milestone-level reconciliation as the default review rollup path',
        'task-level reconciliation as an explicit exception path only',
    ], 'CROSS_ARTIFACT_REVIEW_HANDOFF_DRIFT');
    return findings;
}
function runPlaceholderScaffoldingChecks(repositoryRoot) {
    const findings = [];
    const enforceSourceOnlyContracts = isDayShiftSourceRepository(repositoryRoot);
    requireTextSnippets(repositoryRoot, findings, '.day-shift/specs/06-templates-and-prompts/templates.md', [
        'Validation should use the canonical unresolved-token pattern `{{[a-z_][a-z0-9_]*}}`',
        'Saved artifacts should fail validation when unresolved placeholders or leaked scaffolding prose remain.',
    ], 'CROSS_ARTIFACT_PLACEHOLDER_CONTRACT_DRIFT', {
        allowMissingInput: !enforceSourceOnlyContracts,
    });
    requireTextSnippets(repositoryRoot, findings, cli_constants_1.CLI_SOURCE_SPEC_PLANNING_WORKFLOW, [
        'No unresolved placeholder tokens remain',
        'Parser-sensitive formatting rules are enforced from prompt/workflow contract text rather than stored as instructional prose in artifact sections.',
    ], 'CROSS_ARTIFACT_PLACEHOLDER_CONTRACT_DRIFT');
    requireTextSnippets(repositoryRoot, findings, 'tests_v2/init/template-contract.test.js', ['EXPECTED_SCAFFOLDING_STRINGS', 'leaked scaffolding prose'], 'CROSS_ARTIFACT_PLACEHOLDER_CONTRACT_DRIFT', {
        allowMissingInput: !enforceSourceOnlyContracts,
    });
    return findings;
}
function runNoWriteSurfaceChecks(repositoryRoot) {
    const findings = [];
    const enforceSourceOnlyContracts = isDayShiftSourceRepository(repositoryRoot);
    requireTextSnippets(repositoryRoot, findings, '.day-shift/workflows/01-start-here.md', ['validation smoke` are read-only findings or evidence surfaces', 'advisory only and do not authorize writes'], 'CROSS_ARTIFACT_NO_WRITE_DRIFT');
    requireTextSnippets(repositoryRoot, findings, '.day-shift/workflows/workflow-guide.md', ['no-write state', 'shared findings taxonomy'], 'CROSS_ARTIFACT_NO_WRITE_DRIFT');
    requireTextSnippets(repositoryRoot, findings, '.day-shift/specs/05-planning-artifacts/artifact-model.md', [
        'Metadata update-attempt and validate-update output should expose the selected family, surface mode, shared findings taxonomy, no-write state',
    ], 'CROSS_ARTIFACT_NO_WRITE_DRIFT', {
        allowMissingInput: !enforceSourceOnlyContracts,
    });
    return findings;
}
function requirePromptTemplateRefs(promptManifest, promptManifestPath, findings, promptPath, expectedRefs) {
    const entry = readObjectArray(promptManifest, 'prompts').find((candidate) => readString(candidate, 'path') === promptPath);
    if (entry === undefined) {
        findings.push(createCrossArtifactFinding({
            code: 'CROSS_ARTIFACT_REVIEW_HANDOFF_DRIFT',
            message: `Prompt manifest is missing review handoff entry '${promptPath}'.`,
            severity: 'error',
            path: promptManifestPath,
        }));
        return;
    }
    const observedRefs = readTemplateReferences(entry);
    for (const expectedRef of expectedRefs) {
        if (!observedRefs.includes(expectedRef)) {
            findings.push(createCrossArtifactFinding({
                code: 'CROSS_ARTIFACT_REVIEW_HANDOFF_DRIFT',
                message: `Prompt manifest entry '${promptPath}' must reference '${expectedRef}' to preserve the implementation-summary/reconciliation handoff contract.`,
                severity: 'error',
                path: promptManifestPath,
            }));
        }
    }
}
function requireTextSnippets(repositoryRoot, findings, relativePath, snippets, code, options = {}) {
    const content = readRepositoryFile(repositoryRoot, relativePath);
    if (content === null) {
        if (options.allowMissingInput) {
            return;
        }
        findings.push(createCrossArtifactFinding({
            code: 'CROSS_ARTIFACT_ALIGNMENT_INPUT_MISSING',
            message: `Cannot verify cross-artifact alignment because '${relativePath}' is missing.`,
            severity: 'warning',
            path: relativePath,
        }));
        return;
    }
    for (const snippet of snippets) {
        if (!content.includes(snippet)) {
            findings.push(createCrossArtifactFinding({
                code,
                message: `Expected cross-artifact contract text is missing from '${relativePath}': ${snippet}`,
                severity: 'error',
                path: relativePath,
            }));
        }
    }
}
function isDayShiftSourceRepository(repositoryRoot) {
    return (node_fs_1.default.existsSync(node_path_1.default.join(repositoryRoot, 'src', 'cli', 'index.ts')) &&
        node_fs_1.default.existsSync(node_path_1.default.join(repositoryRoot, 'src', 'lib', 'doctor', 'index.ts')));
}
function readRepositoryFile(repositoryRoot, relativePath) {
    const absolutePath = node_path_1.default.join(repositoryRoot, relativePath);
    if (!node_fs_1.default.existsSync(absolutePath)) {
        return null;
    }
    return node_fs_1.default.readFileSync(absolutePath, 'utf8');
}
function collectPlanningMarkdownArtifacts(currentPath, relativePath) {
    const artifacts = [];
    const entries = node_fs_1.default.readdirSync(currentPath, { withFileTypes: true });
    for (const entry of entries) {
        const entryPath = node_path_1.default.join(currentPath, entry.name);
        const entryRelativePath = (0, planning_document_utils_1.toRepositoryLikePath)(node_path_1.default.join(relativePath, entry.name));
        if (entry.isDirectory() && !entry.name.startsWith('.')) {
            artifacts.push(...collectPlanningMarkdownArtifacts(entryPath, entryRelativePath));
            continue;
        }
        if (entry.isFile() && entry.name.endsWith('.md')) {
            artifacts.push(entryRelativePath);
        }
    }
    return artifacts.sort((left, right) => left.localeCompare(right));
}
function readJsonObject(repositoryRoot, relativePath, findings) {
    const content = readRepositoryFile(repositoryRoot, relativePath);
    if (content === null) {
        findings.push(createCrossArtifactFinding({
            code: 'CROSS_ARTIFACT_ALIGNMENT_INPUT_MISSING',
            message: `Cannot verify cross-artifact alignment because '${relativePath}' is missing.`,
            severity: 'warning',
            path: relativePath,
        }));
        return null;
    }
    try {
        const parsed = JSON.parse(content);
        if (isRecord(parsed)) {
            return parsed;
        }
    }
    catch {
        // Report malformed JSON through the shared alignment finding below.
    }
    findings.push(createCrossArtifactFinding({
        code: 'CROSS_ARTIFACT_ALIGNMENT_INPUT_INVALID',
        message: `Cannot verify cross-artifact alignment because '${relativePath}' is not a JSON object.`,
        severity: 'error',
        path: relativePath,
    }));
    return null;
}
function readObjectArray(record, key) {
    const value = record[key];
    if (!Array.isArray(value)) {
        return [];
    }
    return value.filter(isRecord);
}
function readTemplateReferences(record) {
    const refs = [];
    const templateRef = readString(record, 'template_ref');
    if (templateRef !== null) {
        refs.push(templateRef);
    }
    const templateRefs = record.template_refs;
    if (Array.isArray(templateRefs)) {
        refs.push(...templateRefs.filter((value) => typeof value === 'string'));
    }
    return refs;
}
function readString(record, key) {
    const value = record[key];
    return typeof value === 'string' ? value : null;
}
function readNestedString(record, pathParts) {
    let current = record;
    for (const pathPart of pathParts) {
        if (!isRecord(current)) {
            return null;
        }
        current = current[pathPart];
    }
    return typeof current === 'string' ? current : null;
}
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function arraysEqual(left, right) {
    return left.length === right.length && left.every((value, index) => value === right[index]);
}
function statusFromFindings(findings) {
    if (findings.some((finding) => finding.severity === 'error')) {
        return 'error';
    }
    if (findings.some((finding) => finding.severity === 'warning')) {
        return 'warning';
    }
    if (findings.some((finding) => finding.severity === 'informational')) {
        return 'informational';
    }
    return 'ok';
}
function statusFromPlanningGaps(gaps) {
    if (gaps.some((gap) => gap.severity === 'error')) {
        return 'error';
    }
    return gaps.length === 0 ? 'ok' : 'warning';
}
function validationFindingFromPlanningGap(gap) {
    if (gap.evidence !== undefined) {
        return {
            code: gap.code,
            message: gap.message,
            severity: gap.severity ?? 'warning',
            path: gap.path,
            evidence: {
                ...gap.evidence,
                details: {
                    ...gap.evidence.details,
                    source: 'planning-gap-report',
                },
            },
        };
    }
    return {
        code: gap.code,
        message: gap.message,
        severity: gap.severity ?? 'warning',
        path: gap.path,
        evidence: (0, output_1.createValidationCommandEvidence)({
            category: 'validation-rollup',
            details: { source: 'planning-gap-report' },
        }),
    };
}
function createCrossArtifactFinding(finding) {
    return finding;
}
function loadPlanningValidationBaseline(repositoryRoot) {
    const baselinePath = contracts_1.PLANNING_VALIDATION_BASELINE_PATH;
    const absolutePath = node_path_1.default.join(repositoryRoot, baselinePath);
    if (!node_fs_1.default.existsSync(absolutePath)) {
        return {
            path: baselinePath,
            record: null,
            findingFingerprints: new Set(),
            inventoryFindingFingerprints: new Set(),
            gapFingerprints: new Set(),
        };
    }
    try {
        const parsed = JSON.parse(node_fs_1.default.readFileSync(absolutePath, 'utf8'));
        if (!isPlanningValidationBaselineRecord(parsed)) {
            return {
                path: baselinePath,
                record: null,
                findingFingerprints: new Set(),
                inventoryFindingFingerprints: new Set(),
                gapFingerprints: new Set(),
            };
        }
        return {
            path: baselinePath,
            record: parsed,
            findingFingerprints: new Set(parsed.findings.map((finding) => finding.fingerprint)),
            inventoryFindingFingerprints: new Set(parsed.inventory_findings.map((finding) => finding.fingerprint)),
            gapFingerprints: new Set(parsed.gaps.map((gap) => gap.fingerprint)),
        };
    }
    catch {
        return {
            path: baselinePath,
            record: null,
            findingFingerprints: new Set(),
            inventoryFindingFingerprints: new Set(),
            gapFingerprints: new Set(),
        };
    }
}
function isPlanningValidationBaselineRecord(value) {
    return (isRecord(value) &&
        value.schema_version === 1 &&
        value.scope === 'planning' &&
        value.mode === 'forward-only' &&
        typeof value.created_at === 'string' &&
        Array.isArray(value.findings) &&
        Array.isArray(value.inventory_findings) &&
        Array.isArray(value.gaps));
}
function filterBaselineFindings(findings, baseline) {
    if (baseline.record === null) {
        return findings;
    }
    return findings.filter((finding) => !baseline.findingFingerprints.has(fingerprintFinding(finding)));
}
function filterBaselineInventoryFindings(findings, baseline) {
    if (baseline.record === null) {
        return findings;
    }
    return findings.filter((finding) => !baseline.inventoryFindingFingerprints.has(fingerprintInventoryFinding(finding)));
}
function filterBaselineGaps(gaps, baseline) {
    if (baseline.record === null) {
        return gaps;
    }
    return gaps.filter((gap) => !baseline.gapFingerprints.has(fingerprintGap(gap)));
}
function buildPlanningValidationBaselineEvidence(input) {
    return {
        path: input.loadedBaseline.path,
        present: input.loadedBaseline.record !== null,
        schemaVersion: input.loadedBaseline.record?.schema_version ?? null,
        createdAt: input.loadedBaseline.record?.created_at ?? null,
        mode: input.loadedBaseline.record?.mode ?? null,
        suppressedFindings: input.rawFindings.length - input.filteredFindings.length,
        suppressedInventoryFindings: input.rawInventoryFindings.length - input.filteredInventoryFindings.length,
        suppressedGaps: input.rawGaps.length - input.filteredGaps.length,
    };
}
function applyPlanningValidationBaselineToChecks(input) {
    if (!input.baselineEvidence.present) {
        return input.checks;
    }
    return input.checks.map((check) => {
        switch (check.id) {
            case 'planning-artifact-metadata':
                return applyFindingBaselineToCheck(check, input.rawFindings, input.filteredFindings, (finding) => finding.code.startsWith('ARTIFACT_'));
            case 'planning-cli-provenance':
                return applyFindingBaselineToCheck(check, input.rawFindings, input.filteredFindings, (finding) => finding.code.startsWith('PLANNING_WORKFLOW_PROVENANCE_'));
            case 'lightweight-artifact-strictness':
                return applyFindingBaselineToCheck(check, input.rawFindings, input.filteredFindings, (finding) => finding.code.startsWith('LIGHTWEIGHT_'));
            case 'planning-inventory':
                if (input.rawInventoryFindings.length > 0 && input.filteredInventoryFindings.length === 0) {
                    return {
                        ...check,
                        status: 'ok',
                        message: `All ${input.rawInventoryFindings.length} planning inventory finding${input.rawInventoryFindings.length === 1 ? '' : 's'} are covered by the forward-only validation baseline.`,
                    };
                }
                return input.filteredInventoryFindings.length > 0
                    ? {
                        ...check,
                        status: 'warning',
                        message: `Found ${input.filteredInventoryFindings.length} new planning inventory finding${input.filteredInventoryFindings.length === 1 ? '' : 's'} beyond the forward-only validation baseline.`,
                    }
                    : check;
            case 'planning-gap-report':
                if (input.rawGaps.length > 0 && input.filteredGaps.length === 0) {
                    return {
                        ...check,
                        status: 'ok',
                        message: `All ${input.rawGaps.length} planning gap${input.rawGaps.length === 1 ? '' : 's'} are covered by the forward-only validation baseline.`,
                    };
                }
                return input.filteredGaps.length > 0
                    ? {
                        ...check,
                        status: statusFromPlanningGaps(input.filteredGaps),
                        message: `Found ${input.filteredGaps.length} new planning gap${input.filteredGaps.length === 1 ? '' : 's'} beyond the forward-only validation baseline.`,
                    }
                    : check;
            case 'cross-artifact-alignment':
                return applyFindingBaselineToCheck(check, input.rawFindings, input.filteredFindings, (finding) => finding.code.startsWith('CROSS_ARTIFACT_'));
            default:
                return check;
        }
    });
}
function applyFindingBaselineToCheck(check, rawFindings, filteredFindings, predicate) {
    const rawCount = rawFindings.filter(predicate).length;
    const filtered = filteredFindings.filter(predicate);
    if (rawCount > 0 && filtered.length === 0) {
        return {
            ...check,
            status: 'ok',
            message: `All ${rawCount} ${check.id} finding${rawCount === 1 ? '' : 's'} are covered by the forward-only validation baseline.`,
        };
    }
    if (filtered.length > 0) {
        return {
            ...check,
            status: statusFromFindings(filtered),
            message: `Found ${filtered.length} new ${check.id} finding${filtered.length === 1 ? '' : 's'} beyond the forward-only validation baseline.`,
        };
    }
    return check;
}
function fingerprintFinding(finding) {
    return stableFingerprint({
        kind: 'finding',
        code: finding.code,
        severity: finding.severity,
        path: finding.path ?? null,
        message: finding.message,
    });
}
function fingerprintInventoryFinding(message) {
    return stableFingerprint({
        kind: 'inventory-finding',
        message,
    });
}
function fingerprintGap(gap) {
    return stableFingerprint({
        kind: 'planning-gap',
        code: gap.code,
        path: gap.path,
        message: gap.message,
    });
}
function stableFingerprint(value) {
    return Buffer.from(JSON.stringify(value)).toString('base64url');
}
function derivePlanningValidationSmokeStatus(input) {
    if (input.checks.some((check) => check.status === 'error')) {
        return 'blocked';
    }
    if (input.checks.some((check) => check.status === 'warning') ||
        input.inventoryFindings.length > 0 ||
        input.gaps.length > 0) {
        return 'ready_with_warnings';
    }
    return 'ready';
}
function derivePlanningValidationSmokeNextActions(input) {
    const actions = [];
    if (input.checks.some((check) => check.status === 'error')) {
        actions.push('Repair blocking planning metadata or provenance findings before write-capable workflow steps.');
    }
    else if (input.gaps.length > 0 || input.inventoryFindings.length > 0) {
        actions.push('Review missing or stale paired planning artifacts before closeout or sync commands.');
    }
    else {
        actions.push('Planning smoke found no blocking issues in the current repository scope.');
    }
    if (input.nextAction?.command) {
        actions.push(`Artifact-specific next command: ${input.nextAction.command}`);
    }
    else if (input.nextAction !== null) {
        actions.push(`Artifact-specific next step: ${input.nextAction.recommendation}`);
    }
    else {
        actions.push('Use `day-shift planning next-action --artifact <path>` when you need artifact-specific guidance.');
    }
    return actions;
}
