"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectPlanningHeuristicFindings = collectPlanningHeuristicFindings;
const output_1 = require("../output");
const DEFAULT_RULE_FAMILIES = ["planning-language"];
const LIGHTWEIGHT_PRESSURE_RECOVERY_OPTIONS = [
    {
        decision: "promote-to-standard",
        guidance: "Promote to standard planning before implementation when the work needs phase or milestone decomposition.",
        workflow: ".day-shift/workflows/02-plan-from-spec.md"
    },
    {
        decision: "split-lightweight-scope",
        guidance: "Split the lightweight scope into separate implementation-sized tasks when one coherent lightweight unit no longer fits.",
        workflow: ".day-shift/workflows/lightweight-workflow-guide.md"
    },
    {
        decision: "remain-lightweight-with-rationale",
        guidance: "Document why lightweight handling remains valid when pressure evidence is accepted but still low risk.",
        workflow: ".day-shift/workflows/lightweight-workflow-guide.md"
    }
];
const LIGHTWEIGHT_PRESSURE_NO_WRITE_BOUNDARY = "This advisory finding is warning-only and does not create phase, milestone, task, reconciliation, or metadata promotion artifacts.";
const CONCRETE_CONTRACT_TERMS = [
    "artifact",
    "boundary",
    "command",
    "contract",
    "decision",
    "destination",
    "field",
    "finding",
    "interface",
    "metadata",
    "policy",
    "rule",
    "schema",
    "template",
    "vocabulary",
    "workflow"
];
const VAGUE_ACTION_VERBS = [
    "handle",
    "support",
    "improve",
    "clean up",
    "address",
    "align",
    "prepare"
];
const DOCUMENTATION_ONLY_TERMS = ["document", "describe", "record", "summarize", "review"];
function collectPlanningHeuristicFindings(options) {
    const enabledRuleFamilies = new Set(options.enabledRuleFamilies ?? DEFAULT_RULE_FAMILIES);
    const findings = [];
    if (enabledRuleFamilies.has("planning-language")) {
        findings.push(...collectPlanningLanguageFindings(options));
    }
    if (enabledRuleFamilies.has("decomposition")) {
        findings.push(...collectDecompositionFindings(options));
    }
    if (enabledRuleFamilies.has("target-path-hygiene")) {
        findings.push(...collectTargetPathHygieneFindings(options));
    }
    if (enabledRuleFamilies.has("dependency-hygiene")) {
        findings.push(...collectDependencyHygieneFindings(options));
    }
    if (enabledRuleFamilies.has("stale-spec-risk")) {
        findings.push(...collectStaleSpecRiskFindings(options));
    }
    if (enabledRuleFamilies.has("lightweight-promotion-pressure")) {
        findings.push(...collectLightweightPromotionPressureFindings(options));
    }
    return findings;
}
function collectPlanningLanguageFindings(options) {
    const findings = [];
    for (const candidate of collectLanguageWarningCandidates(options.sections)) {
        if (hasConcreteContractEvidence(candidate.evidence)) {
            continue;
        }
        const reason = candidate.verb === "define"
            ? "The artifact uses 'define' without naming a concrete contract, boundary, schema, interface, policy, rule, vocabulary, decision, or artifact target."
            : `The artifact uses vague action verb '${candidate.verb}' without naming a concrete deliverable target.`;
        const nextCommand = options.artifactFamily === "task_definition"
            ? `node dist/cli/index.js task readiness-review --task-definition ${options.artifactPath}`
            : `node dist/cli/index.js milestone review --milestone-overview ${options.artifactPath}`;
        findings.push((0, output_1.createAdvisoryValidationFinding)({
            code: "PLANNING_LANGUAGE_AMBIGUOUS_DELIVERABLE",
            message: `${options.artifactPath}: ${reason}`,
            path: options.artifactPath,
            advisoryReason: "weak-evidence",
            recovery: `Revise the planning artifact to name the concrete deliverable, then rerun '${nextCommand}'.`,
            evidence: {
                category: "planning-review",
                details: {
                    ruleFamily: "planning-language",
                    severity: "warning",
                    evidenceSource: `section:${candidate.sectionName}`,
                    reason,
                    phrase: candidate.evidence,
                    nextAction: "revise-planning",
                    nextCommand
                }
            },
            affectedPaths: [
                {
                    role: "finding-location",
                    path: options.artifactPath,
                    description: `Planning language warning in section '${candidate.sectionName}'.`
                }
            ]
        }));
    }
    return findings;
}
function collectLanguageWarningCandidates(sections) {
    const candidates = [];
    for (const [sectionName, sectionText] of sections) {
        const searchableText = stripMarkdownCode(sectionText);
        for (const sentence of splitIntoSentences(searchableText)) {
            for (const verb of VAGUE_ACTION_VERBS) {
                const expression = new RegExp(`\\b${escapeRegExp(verb)}\\b`, "i");
                if (!expression.test(sentence)) {
                    continue;
                }
                candidates.push({
                    verb,
                    sectionName,
                    evidence: normalizeEvidence(sentence)
                });
            }
        }
    }
    return candidates;
}
function collectDecompositionFindings(options) {
    if (options.artifactFamily !== "task_definition") {
        return [];
    }
    const targetPaths = extractBulletedValues(options.sections.get("Implementation Target Paths") ?? "");
    const objectiveText = [
        options.sections.get("Objective") ?? "",
        options.sections.get("Desired Behavior") ?? ""
    ].join("\n");
    if (targetPaths.length === 0 || !targetPaths.every(isDocumentationOrPlanningPath)) {
        return [];
    }
    const normalizedObjective = stripMarkdownCode(objectiveText).toLowerCase();
    if (!DOCUMENTATION_ONLY_TERMS.some((term) => normalizedObjective.includes(term))) {
        return [];
    }
    const reason = "Task decomposition appears documentation-only while the milestone scope expects executable planning-review behavior.";
    return [
        createPlanningHeuristicWarning({
            artifactPath: options.artifactPath,
            artifactFamily: options.artifactFamily,
            code: "PLANNING_DECOMPOSITION_DOCUMENTATION_ONLY",
            ruleFamily: "decomposition",
            evidenceSource: "sections:Objective,Desired Behavior,Implementation Target Paths",
            reason,
            phrase: targetPaths.join(", ")
        })
    ];
}
function collectTargetPathHygieneFindings(options) {
    if (options.artifactFamily !== "task_definition") {
        return [];
    }
    const targetPaths = extractBulletedValues(options.sections.get("Implementation Target Paths") ?? "");
    const weakTargets = targetPaths.filter((targetPath) => {
        return isBroadDirectoryPath(targetPath) || !isRepositoryLocalPath(targetPath);
    });
    if (weakTargets.length === 0) {
        return [];
    }
    const reason = `Implementation Target Paths include weak or broad target evidence: ${weakTargets.join(", ")}.`;
    return [
        createPlanningHeuristicWarning({
            artifactPath: options.artifactPath,
            artifactFamily: options.artifactFamily,
            code: "PLANNING_TARGET_PATH_WEAK_EVIDENCE",
            ruleFamily: "target-path-hygiene",
            evidenceSource: "section:Implementation Target Paths",
            reason,
            phrase: weakTargets.join(", ")
        })
    ];
}
function collectDependencyHygieneFindings(options) {
    if (options.artifactFamily !== "task_definition") {
        return [];
    }
    const dependencyValues = extractBulletedValues(options.sections.get("Dependencies") ?? "");
    if (dependencyValues.length === 0) {
        return [];
    }
    const concreteNonParentDependencies = dependencyValues.filter((dependencyValue) => {
        return isRepositoryLocalPath(dependencyValue) && !/\/(overview|reconciliation)\.md$/u.test(dependencyValue);
    });
    if (concreteNonParentDependencies.length > 0) {
        return [];
    }
    const reason = "Dependencies cite only generic parent or review context and do not name a concrete producer artifact.";
    return [
        createPlanningHeuristicWarning({
            artifactPath: options.artifactPath,
            artifactFamily: options.artifactFamily,
            code: "PLANNING_DEPENDENCY_WEAK_EVIDENCE",
            ruleFamily: "dependency-hygiene",
            evidenceSource: "section:Dependencies",
            reason,
            phrase: dependencyValues.join(", ")
        })
    ];
}
function collectStaleSpecRiskFindings(options) {
    const frontMatterText = (options.frontMatterLines ?? []).join("\n");
    if (!/source_type:\s*["']?message["']?/iu.test(frontMatterText)) {
        return [];
    }
    const sourceRefMatch = frontMatterText.match(/\bref:\s*["']?([^\n"']+)["']?/iu);
    const sourceRef = sourceRefMatch?.[1]?.trim() ?? "upstream message source";
    const datedSourceMatch = sourceRef.match(/\b(20\d{2})-(\d{2})-(\d{2})\b/u);
    const reason = datedSourceMatch === null
        ? "Upstream source-spec traceability points to message evidence that may have been overtaken by current repository behavior."
        : `Upstream source-spec traceability points to dated message evidence (${datedSourceMatch[0]}) that may be stale relative to current repository behavior.`;
    const currentLifecycleEvidence = options.currentLifecycleEvidence ?? [];
    if (currentLifecycleEvidence.length > 0) {
        return [
            createPlanningHeuristicWarning({
                artifactPath: options.artifactPath,
                artifactFamily: options.artifactFamily,
                code: "PLANNING_STALE_SPEC_RISK_DOWNGRADED",
                ruleFamily: "stale-spec-risk",
                evidenceSource: "front-matter:upstream.specs",
                reason: `${reason} Downgraded for this artifact because current lifecycle evidence narrows the active work: ${currentLifecycleEvidence.join("; ")}.`,
                phrase: sourceRef,
                severity: "informational",
                advisoryReason: "weak-evidence",
                nextAction: "continue-active-lifecycle",
                nextCommand: "Continue with the selected artifact lifecycle; review remains read-only and this downgrade does not update metadata or source text.",
                recovery: "Continue with the selected artifact lifecycle using the current lifecycle evidence named in this finding; no metadata, status, or source text is updated by this read-only review downgrade."
            })
        ];
    }
    return [
        createPlanningHeuristicWarning({
            artifactPath: options.artifactPath,
            artifactFamily: options.artifactFamily,
            code: "PLANNING_STALE_SPEC_RISK",
            ruleFamily: "stale-spec-risk",
            evidenceSource: "front-matter:upstream.specs",
            reason,
            phrase: sourceRef,
            nextAction: "review-scope",
            nextCommand: "Review .day-shift/prompts/user/specification.recertify.md or perform manual source-spec review before relying on stale planning assumptions."
        })
    ];
}
function collectLightweightPromotionPressureFindings(options) {
    if (options.artifactFamily !== "task_definition" || !isLightweightTask(options.frontMatterLines ?? [])) {
        return [];
    }
    const targetPaths = extractBulletedValues(options.sections.get("Implementation Target Paths") ?? "");
    const dependencyPaths = extractBulletedValues(options.sections.get("Dependencies") ?? "");
    const validationCommands = extractValidationCommands(options.sections.get("Validation Commands") ?? "");
    const searchableTaskText = [
        options.sections.get("Objective") ?? "",
        options.sections.get("Desired Behavior") ?? "",
        options.sections.get("Acceptance Criteria") ?? "",
        options.sections.get("Risks") ?? "",
        options.sections.get("Constraints") ?? "",
        options.sections.get("Lifecycle / Closure Expectations") ?? ""
    ].join("\n");
    const pressureEvidence = [];
    const broadPathSpread = detectBroadPathSpread(targetPaths);
    if (broadPathSpread !== null) {
        pressureEvidence.push({
            category: "path-spread",
            evidenceSource: "section:Implementation Target Paths",
            reason: broadPathSpread.reason,
            phrase: broadPathSpread.phrase
        });
    }
    const mixedScope = detectMixedScope(targetPaths);
    if (mixedScope !== null) {
        pressureEvidence.push({
            category: "mixed-scope",
            evidenceSource: "section:Implementation Target Paths",
            reason: mixedScope.reason,
            phrase: mixedScope.phrase
        });
    }
    const crossLayerDependencies = detectCrossLayerDependencies(dependencyPaths);
    if (crossLayerDependencies !== null) {
        pressureEvidence.push({
            category: "cross-layer-dependencies",
            evidenceSource: "section:Dependencies",
            reason: crossLayerDependencies.reason,
            phrase: crossLayerDependencies.phrase
        });
    }
    const coordinationCheckpoints = detectCoordinationCheckpoints(searchableTaskText);
    if (coordinationCheckpoints !== null) {
        pressureEvidence.push({
            category: "coordination-checkpoints",
            evidenceSource: "sections:Objective,Desired Behavior,Acceptance Criteria,Risks,Constraints,Lifecycle / Closure Expectations",
            reason: coordinationCheckpoints.reason,
            phrase: coordinationCheckpoints.phrase
        });
    }
    const validationBreadth = detectValidationBreadth(validationCommands);
    if (validationBreadth !== null) {
        pressureEvidence.push({
            category: "validation-breadth",
            evidenceSource: "section:Validation Commands",
            reason: validationBreadth.reason,
            phrase: validationBreadth.phrase
        });
    }
    return pressureEvidence.map((evidence) => {
        return createLightweightPromotionPressureWarning({
            artifactPath: options.artifactPath,
            category: evidence.category,
            evidenceSource: evidence.evidenceSource,
            reason: evidence.reason,
            phrase: evidence.phrase
        });
    });
}
function createPlanningHeuristicWarning(options) {
    const nextCommand = options.artifactFamily === "task_definition"
        ? `node dist/cli/index.js task readiness-review --task-definition ${options.artifactPath}`
        : `node dist/cli/index.js milestone review --milestone-overview ${options.artifactPath}`;
    return (0, output_1.createAdvisoryValidationFinding)({
        code: options.code,
        message: `${options.artifactPath}: ${options.reason}`,
        path: options.artifactPath,
        advisoryReason: options.advisoryReason ?? "weak-evidence",
        level: options.severity,
        recovery: options.recovery ?? `Revise the planning artifact to strengthen the cited evidence, then rerun '${nextCommand}'.`,
        evidence: {
            category: "planning-review",
            details: {
                ruleFamily: options.ruleFamily,
                severity: options.severity ?? "warning",
                evidenceSource: options.evidenceSource,
                reason: options.reason,
                phrase: options.phrase,
                nextAction: options.nextAction ?? "revise-planning",
                nextCommand: options.nextCommand ?? nextCommand
            }
        },
        affectedPaths: [
            {
                role: "finding-location",
                path: options.artifactPath,
                description: `Planning heuristic warning for rule family '${options.ruleFamily}'.`
            }
        ]
    });
}
function createLightweightPromotionPressureWarning(options) {
    const nextCommand = `node dist/cli/index.js task readiness-review --task-definition ${options.artifactPath}`;
    const recoveryOptions = LIGHTWEIGHT_PRESSURE_RECOVERY_OPTIONS.map((option) => ({ ...option }));
    const recovery = [
        `${options.reason}`,
        ...recoveryOptions.map((option) => `${option.decision}: ${option.guidance} Use ${option.workflow}.`),
        LIGHTWEIGHT_PRESSURE_NO_WRITE_BOUNDARY,
        `Rerun '${nextCommand}' after recording the promotion decision, scope split, or remain-lightweight rationale.`
    ].join(" ");
    return (0, output_1.createAdvisoryValidationFinding)({
        code: "LIGHTWEIGHT_PROMOTION_PRESSURE",
        message: `${options.artifactPath}: ${options.reason} ${LIGHTWEIGHT_PRESSURE_NO_WRITE_BOUNDARY}`,
        path: options.artifactPath,
        advisoryReason: "weak-evidence",
        recovery,
        evidence: {
            category: "planning-review",
            details: {
                ruleFamily: "lightweight-promotion-pressure",
                pressureCategory: options.category,
                severity: "warning",
                evidenceSource: options.evidenceSource,
                reason: options.reason,
                phrase: options.phrase,
                nextAction: "review-lightweight-promotion-decision",
                nextCommand,
                recoveryOptions,
                decisionRequiredBy: ["closeout", "reconciliation", "downstream-expansion"],
                warningOnlyUntilDecisionBoundary: true,
                noWriteBoundary: LIGHTWEIGHT_PRESSURE_NO_WRITE_BOUNDARY,
                blockingBoundary: "May become blocking only at closeout, reconciliation, or downstream expansion if no promotion decision, scope split, or remain-lightweight rationale is documented."
            }
        },
        affectedPaths: [
            {
                role: "finding-location",
                path: options.artifactPath,
                description: `Lightweight promotion-pressure warning for category '${options.category}'.`
            }
        ]
    });
}
function hasConcreteContractEvidence(value) {
    const lowerValue = value.toLowerCase();
    return CONCRETE_CONTRACT_TERMS.some((term) => lowerValue.includes(term));
}
function isLightweightTask(frontMatterLines) {
    return frontMatterLines.some((line) => /^planning_depth:\s*["']?lightweight["']?\s*$/iu.test(line.trim()));
}
function extractBulletedValues(sectionText) {
    const values = [];
    for (const line of sectionText.split(/\r?\n/)) {
        const trimmed = line.trim();
        if (!trimmed.startsWith("- ")) {
            continue;
        }
        const value = trimmed.slice(2).trim();
        if (/^none(?: currently)?\.?$/iu.test(value) || /^pending\b/iu.test(value)) {
            continue;
        }
        const backtickMatch = value.match(/^`([^`]+)`$/u);
        values.push(backtickMatch === null ? value : backtickMatch[1]);
    }
    return values;
}
function extractValidationCommands(sectionText) {
    return stripMarkdownCodeFenceMarkers(sectionText)
        .split(/\r?\n/u)
        .map((line) => line.trim())
        .filter((line) => line.length > 0 && !line.startsWith("#"));
}
function detectBroadPathSpread(targetPaths) {
    const pathRoots = new Set(targetPaths.map(readPathRoot).filter((entry) => entry !== null));
    const broadTargets = targetPaths.filter(isBroadDirectoryPath);
    if (targetPaths.length < 4 && pathRoots.size < 3 && broadTargets.length === 0) {
        return null;
    }
    const phrase = targetPaths.join(", ");
    return {
        reason: "Lightweight task target paths show broad path spread that may exceed one implementation-sized unit.",
        phrase
    };
}
function detectMixedScope(targetPaths) {
    const scopes = new Set(targetPaths.map(classifyImplementationScope).filter((scope) => scope !== null));
    if (scopes.size < 2) {
        return null;
    }
    return {
        reason: `Lightweight task mixes implementation scope categories: ${[...scopes].sort().join(", ")}.`,
        phrase: targetPaths.join(", ")
    };
}
function detectCrossLayerDependencies(dependencyPaths) {
    const planningLayers = new Set();
    const crossLayerDependencies = dependencyPaths.filter((dependencyPath) => {
        const layer = classifyPlanningLayer(dependencyPath);
        if (layer === null) {
            return false;
        }
        planningLayers.add(layer);
        return layer !== "slice" && layer !== "task";
    });
    if (crossLayerDependencies.length === 0 && planningLayers.size < 3) {
        return null;
    }
    return {
        reason: "Lightweight task dependencies cross planning layers that may need standard planning coordination.",
        phrase: dependencyPaths.join(", ")
    };
}
function detectCoordinationCheckpoints(value) {
    const searchableValue = stripMarkdownCode(value);
    const matches = searchableValue.match(/\b(coordination checkpoint|checkpoint|phase review|milestone review|reconciliation|promotion decision|scope split|downstream expansion|handoff)\b/giu) ?? [];
    const uniqueMatches = [...new Set(matches.map((match) => match.toLowerCase()))];
    if (uniqueMatches.length < 2) {
        return null;
    }
    return {
        reason: "Lightweight task text names multiple coordination checkpoints or downstream decision points.",
        phrase: uniqueMatches.join(", ")
    };
}
function detectValidationBreadth(validationCommands) {
    const broadValidationCommands = validationCommands.filter((command) => {
        return /\b(pnpm\s+(run\s+)?(build|check:all|test)|pnpm\s+exec\s+tsc|validation\s+(smoke|rollup)(?!.*--artifact)|node\s+--test(?!.*--test-name-pattern))\b/u.test(command);
    });
    if (validationCommands.length < 3 && broadValidationCommands.length === 0) {
        return null;
    }
    return {
        reason: "Lightweight task validation evidence is broad enough to suggest standard planning review may be needed.",
        phrase: validationCommands.join("; ")
    };
}
function readPathRoot(value) {
    const normalizedValue = value.replace(/^`|`$/gu, "");
    const firstSegment = normalizedValue.split("/").find((segment) => segment.length > 0);
    return firstSegment ?? null;
}
function classifyImplementationScope(value) {
    if (value.startsWith("src/")) {
        return "runtime";
    }
    if (value.startsWith("tests/")) {
        return "test";
    }
    if (value.startsWith(".day-shift/workflows/") || value.startsWith(".day-shift/prompts/")) {
        return "workflow";
    }
    if (value.endsWith(".md") || value.startsWith(".day-shift/specs/") || value.startsWith(".day-shift/templates/")) {
        return "documentation";
    }
    return null;
}
function classifyPlanningLayer(value) {
    if (!value.startsWith(".day-shift/planning/")) {
        return null;
    }
    if (value.includes("/phases/") && value.includes("/milestones/") && value.includes("/tasks/")) {
        return "task";
    }
    if (value.includes("/phases/") && value.includes("/milestones/")) {
        return "milestone";
    }
    if (value.includes("/phases/")) {
        return "phase";
    }
    return "slice";
}
function stripMarkdownCodeFenceMarkers(value) {
    return value
        .replace(/^\s*```[^\n]*$/gmu, "")
        .replace(/^\s*~~~[^\n]*$/gmu, "");
}
function isDocumentationOrPlanningPath(value) {
    return /\.md$/u.test(value)
        || value.startsWith(".day-shift/planning/")
        || value.startsWith(".day-shift/specs/")
        || value.startsWith(".day-shift/workflows/")
        || value.startsWith(".day-shift/prompts/");
}
function isBroadDirectoryPath(value) {
    return !/\.[A-Za-z0-9]+$/u.test(value) && !value.endsWith(".gitkeep");
}
function isRepositoryLocalPath(value) {
    return value.startsWith(".") || value.includes("/");
}
function splitIntoSentences(value) {
    return value
        .split(/(?<=[.!?])\s+|\r?\n+/u)
        .map((entry) => entry.trim())
        .filter((entry) => entry.length > 0);
}
function normalizeEvidence(value) {
    return value.replace(/\s+/g, " ").trim();
}
function stripMarkdownCode(value) {
    return value
        .replace(/(^|\n)(```|~~~)[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g, "\n")
        .replace(/`+[^`\n]+`+/g, "");
}
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
