"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PromotionPlanningError = void 0;
exports.planPromotionSync = planPromotionSync;
exports.applyPromotionMetadataChanges = applyPromotionMetadataChanges;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const mutation_preconditions_1 = require("../filesystem/mutation-preconditions");
const canonical_write_target_1 = require("../paths/canonical-write-target");
const spec_readiness_1 = require("../spec-readiness");
const work_routing_1 = require("../work-routing");
const ordered_root_identity_1 = require("./ordered-root-identity");
const planning_document_utils_1 = require("./planning-document-utils");
const promotion_records_1 = require("./promotion-records");
const task_lifecycle_1 = require("./task-lifecycle");
const workflow_topology_1 = require("./workflow-topology");
class PromotionPlanningError extends Error {
    code;
    collisions;
    ambiguities;
    duplicateActiveLocations;
    refreshPreviewRequired;
    topologyFindings;
    constructor(code, message, options = {}) {
        super(message);
        this.name = "PromotionPlanningError";
        this.code = code;
        this.collisions = [...(options.collisions ?? [])].sort();
        this.ambiguities = [...(options.ambiguities ?? [])].sort();
        this.duplicateActiveLocations = [...(options.duplicateActiveLocations ?? [])];
        this.refreshPreviewRequired = options.refreshPreviewRequired ?? true;
        this.topologyFindings = [...(options.topologyFindings ?? [])];
    }
}
exports.PromotionPlanningError = PromotionPlanningError;
const ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const ORDERED_ROOT_SLUG_PATTERN = /^(?:0\d|[1-9]\d+)-[a-z0-9]+(?:-[a-z0-9]+)*$/;
const ORDERED_CHILD_SLUG_PATTERN = /^\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*$/;
/** Builds a complete promotion authorization plan without writing repository state. */
function planPromotionSync(request) {
    const repositoryRoot = requireRepositoryRoot(request.repositoryRoot);
    validateTransition(request.sourceLevel, request.targetLevel);
    const targetTopology = projectPromotionTargetTopology(request.targetLevel, request.targetTopologyInput);
    const destinationWorkId = request.targetLevel === "structured"
        ? requireId(request.destinationWorkId, "Destination work id")
        : undefined;
    if (request.targetLevel === "governed" && request.destinationWorkId !== undefined) {
        throw new PromotionPlanningError("invalid-request", "Governed promotion must not receive a destination work identity; use ordered slice identity evidence.");
    }
    const source = request.sourceLevel === "basic"
        ? readBasicSource(repositoryRoot, request.sourceTaskDefinitionPath)
        : readStructuredSource(repositoryRoot, request.sourceOverviewPath);
    const specifications = request.sourceLevel === "basic"
        ? resolveBasicSpecifications(repositoryRoot, request.specificationPaths)
        : source.specifications;
    const sourceWorkId = request.sourceLevel === "structured" ? source.workId : null;
    const governed = request.targetLevel === "governed"
        ? normalizeGovernedDestination(repositoryRoot, request.governed, source.tasks, specifications)
        : null;
    const destinationSliceId = governed?.sliceId;
    let structuredAllocation = null;
    let rootInventoryFingerprint;
    if (request.targetLevel === "structured") {
        try {
            const inventory = (0, ordered_root_identity_1.inspectOrderedRootCollectionSync)({
                repositoryRoot,
                collectionPath: ".day-shift/planning/structured",
                context: "structured-root"
            });
            rootInventoryFingerprint = inventory.fingerprint;
            const stableCollision = inventory.entries.find((entry) => entry.stableId === destinationWorkId);
            if (stableCollision !== undefined) {
                const collisionPath = node_path_1.default.posix.join(inventory.collectionPath, stableCollision.basename, "overview.md");
                throw new PromotionPlanningError("destination-conflict", `Structured work identity '${destinationWorkId}' already owns '${collisionPath}'.`, { collisions: [collisionPath] });
            }
            structuredAllocation = (0, ordered_root_identity_1.allocateOrderedRoot)({
                context: "structured-root",
                stableId: destinationWorkId,
                requestedOrder: request.implementationOrder ?? "next",
                inventory
            });
        }
        catch (error) {
            if (error instanceof PromotionPlanningError)
                throw error;
            throw normalizeOrderedPromotionError(error);
        }
    }
    const destinationRootPath = request.targetLevel === "structured"
        ? `.day-shift/planning/structured/${structuredAllocation.orderedRootName}`
        : `.day-shift/planning/governed/${destinationSliceId}`;
    const requiredParents = buildRequiredParents(targetTopology, destinationRootPath, governed);
    const taskMappings = source.tasks.map((task, index) => buildTaskMapping(task, request.targetLevel, destinationRootPath, governed, index + 1));
    const promotionRecord = (0, promotion_records_1.buildPromotionRecordLocation)({
        sourceLevel: request.sourceLevel,
        targetLevel: request.targetLevel,
        sourceWorkId,
        ...(destinationWorkId === undefined ? {} : { destinationWorkId }),
        ...(destinationSliceId === undefined ? {} : { destinationSliceId }),
        taskMappings
    });
    const duplicateActiveLocations = (0, promotion_records_1.findDuplicateActivePromotionLocationsSync)({
        repositoryRoot,
        targetLevel: request.targetLevel,
        destinationWorkId,
        destinationSliceId,
        taskMappings,
        selectedSourceOverviewPath: source.overviewPath
    });
    if (duplicateActiveLocations.length > 0) {
        const collisions = duplicateActiveLocations.map((entry) => entry.path);
        throw new PromotionPlanningError("duplicate-active-location", "The destination work/task identity is already active in another canonical location.", { collisions, duplicateActiveLocations });
    }
    const preconditions = [
        ...sourcePreconditions(repositoryRoot, source, specifications),
        ...destinationPreconditions(repositoryRoot, requiredParents, taskMappings, promotionRecord)
    ];
    const collisions = preconditions
        .filter((condition) => condition.kind === "destination-presence" && condition.expectedPresence === "present")
        .map((condition) => condition.repositoryRelativePath)
        .sort();
    if (collisions.length > 0) {
        throw new PromotionPlanningError("destination-conflict", "Promotion destinations already exist; refresh the preview after resolving every collision.", { collisions });
    }
    const baseMetadataChanges = buildMetadataChanges(source, taskMappings, request.targetLevel, destinationWorkId, destinationSliceId, requiredParents, specifications);
    const readiness = buildPromotionReadinessTransitions(repositoryRoot, request.sourceLevel, request.targetLevel, source, taskMappings, baseMetadataChanges);
    const metadataChanges = [...baseMetadataChanges, ...readiness.metadataChanges].sort((left, right) => left.path.localeCompare(right.path) || left.field.localeCompare(right.field));
    const readinessTransitions = readiness.transitions;
    const affectedPaths = buildAffectedPaths(source, specifications, requiredParents, taskMappings, promotionRecord);
    const expected = preconditions
        .map(publicCondition)
        .sort(comparePathThenKind);
    const stablePayload = {
        sourceLevel: request.sourceLevel,
        targetLevel: request.targetLevel,
        targetTopology,
        sourceWorkId,
        ...(destinationWorkId === undefined ? {} : { destinationWorkId }),
        ...(structuredAllocation === null ? {} : {
            destinationImplementationOrder: structuredAllocation.implementationOrder,
            destinationImplementationOrderSource: structuredAllocation.implementationOrderSource,
            rootInventoryFingerprint
        }),
        ...(destinationSliceId === undefined ? {} : { destinationSliceId }),
        specifications,
        requiredParents,
        promotionRecord,
        taskMappings,
        metadataChanges,
        readinessTransitions,
        expected,
        affectedPaths
    };
    return {
        planId: `promotion-${(0, node_crypto_1.createHash)("sha256").update(JSON.stringify(stablePayload)).digest("hex").slice(0, 24)}`,
        readOnly: true,
        sourceLevel: request.sourceLevel,
        targetLevel: request.targetLevel,
        targetTopology,
        sourceWorkId,
        ...(destinationWorkId === undefined ? {} : { destinationWorkId }),
        ...(structuredAllocation === null ? {} : {
            destinationImplementationOrder: structuredAllocation.implementationOrder,
            destinationImplementationOrderSource: structuredAllocation.implementationOrderSource,
            rootInventoryFingerprint
        }),
        ...(destinationSliceId === undefined ? {} : { destinationSliceId }),
        sourceRootPath: request.sourceLevel === "basic"
            ? node_path_1.default.posix.dirname(source.tasks[0].taskDefinitionPath)
            : node_path_1.default.posix.dirname(source.overviewPath),
        destinationRootPath,
        specifications,
        requiredParents,
        promotionRecord,
        taskMappings,
        metadataChanges,
        readinessTransitions,
        expected,
        collisions: [],
        ambiguities: [],
        affectedPaths,
        preservation: {
            taskIdentity: "preserved",
            workIdentity: request.targetLevel === "structured"
                ? "assigned"
                : sourceWorkId === null
                    ? "not-applicable"
                    : "source-provenance",
            specificationArtifacts: "referenced-in-place",
            taskArtifactBytes: "preserved-except-declared-front-matter",
            evidenceHistory: "preserved"
        },
        nextAction: "Review this no-write plan, then invoke the separate explicit apply operation with this exact plan identity.",
        recoveryGuidance: "If any expected revision or destination-presence precondition changes, discard this plan and generate a refreshed preview before apply."
    };
}
function readBasicSource(repositoryRoot, inputPath) {
    const taskDefinitionPath = requireText(inputPath, "Basic source task-definition path");
    const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, taskDefinitionPath);
    if (!target.exists || !node_fs_1.default.statSync(target.runtimePath).isFile()) {
        throw new PromotionPlanningError("invalid-source", `Basic task definition '${target.repositoryRelativePath}' is missing.`);
    }
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(target.runtimePath, "utf8"));
    const taskId = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "task")
        ?? (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "id");
    const level = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "workflow_level");
    const taskRoot = node_path_1.default.posix.dirname(target.repositoryRelativePath);
    const parsedRoot = parsePromotionDirectory("basic-root", node_path_1.default.posix.basename(taskRoot));
    const canonical = `.day-shift/planning/basic/tasks/${parsedRoot.basename}/task-definition.md`;
    if (!document.hasFrontMatter ||
        level !== "basic" ||
        taskId === undefined ||
        !ID_PATTERN.test(taskId) ||
        parsedRoot.shape !== "canonical" ||
        parsedRoot.stableId !== taskId ||
        (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "implementation_order") !== parsedRoot.orderPrefix ||
        target.repositoryRelativePath !== canonical) {
        throw new PromotionPlanningError("invalid-source", `'${target.repositoryRelativePath}' is not a canonical Basic task definition.`);
    }
    const summaryPath = node_path_1.default.posix.join(node_path_1.default.posix.dirname(target.repositoryRelativePath), "implementation-summary.md");
    requireExistingFile(repositoryRoot, summaryPath, "Basic implementation summary");
    return {
        workId: "",
        overviewPath: "",
        specifications: [],
        tasks: [{
                taskId,
                taskDefinitionPath: target.repositoryRelativePath,
                implementationSummaryPath: summaryPath
            }]
    };
}
function readStructuredSource(repositoryRoot, inputPath) {
    const overviewPath = requireText(inputPath, "Structured source overview path");
    const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, overviewPath);
    if (!target.exists || !node_fs_1.default.statSync(target.runtimePath).isFile()) {
        throw new PromotionPlanningError("invalid-source", `Structured overview '${target.repositoryRelativePath}' is missing.`);
    }
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(target.runtimePath, "utf8"));
    const workId = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "id");
    const level = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "workflow_level");
    const workRoot = node_path_1.default.posix.dirname(target.repositoryRelativePath);
    const parsedRoot = parsePromotionDirectory("structured-root", node_path_1.default.posix.basename(workRoot));
    const canonical = `.day-shift/planning/structured/${parsedRoot.basename}/overview.md`;
    if (!document.hasFrontMatter ||
        level !== "structured" ||
        workId === undefined ||
        !ID_PATTERN.test(workId) ||
        parsedRoot.shape !== "canonical" ||
        parsedRoot.stableId !== workId ||
        (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "implementation_order") !== parsedRoot.orderPrefix ||
        target.repositoryRelativePath !== canonical) {
        throw new PromotionPlanningError("invalid-source", `'${target.repositoryRelativePath}' is not a canonical Structured work overview.`);
    }
    const specifications = parseStructuredSpecifications(document.frontMatterLines)
        .map((specification) => validateStructuredSpecification(repositoryRoot, specification))
        .sort((left, right) => left.specId.localeCompare(right.specId));
    if (specifications.length === 0) {
        throw new PromotionPlanningError("invalid-source", "Structured promotion requires at least one recorded revision-bound specification.");
    }
    const tasksRootPath = node_path_1.default.posix.join(node_path_1.default.posix.dirname(target.repositoryRelativePath), "tasks");
    const tasksRoot = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, tasksRootPath);
    if (!tasksRoot.exists || !node_fs_1.default.statSync(tasksRoot.runtimePath).isDirectory()) {
        throw new PromotionPlanningError("invalid-source", "Structured promotion requires at least one direct task pair.");
    }
    const tasks = node_fs_1.default.readdirSync(tasksRoot.runtimePath, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .map((entry) => readStructuredTaskPair(repositoryRoot, tasksRootPath, entry.name))
        .sort((left, right) => left.taskId.localeCompare(right.taskId));
    if (tasks.length === 0) {
        throw new PromotionPlanningError("invalid-source", "Structured promotion requires at least one direct task pair.");
    }
    return { workId, overviewPath: target.repositoryRelativePath, specifications, tasks };
}
function resolveBasicSpecifications(repositoryRoot, input) {
    try {
        return [...(0, work_routing_1.normalizeSpecificationReferencesSync)(repositoryRoot, {
                level: "structured",
                specificationPaths: input ?? []
            })].sort((left, right) => left.specId.localeCompare(right.specId));
    }
    catch (error) {
        throw new PromotionPlanningError("ineligible-specification", error instanceof Error ? error.message : "Basic promotion requires a planning-ready canonical specification.");
    }
}
function normalizeGovernedDestination(repositoryRoot, input, tasks, specifications) {
    if (input === undefined) {
        throw new PromotionPlanningError("invalid-request", "Governed promotion requires explicit implementation order, slice, phase, milestone, and task mapping inputs.");
    }
    let identity;
    try {
        identity = (0, work_routing_1.allocateGovernedSliceIdentity)({
            specificationReferences: specifications,
            implementationOrders: input.implementationOrders,
            sliceSlug: input.sliceSlug,
            requestedSliceId: input.requestedSliceId,
            existingSliceIds: listExistingGovernedSliceIds(repositoryRoot)
        });
    }
    catch (error) {
        if (error instanceof work_routing_1.WorkRoutingError) {
            throw new PromotionPlanningError(error.code === "slice-identity-collision" ? "destination-conflict" : "invalid-request", error.message, error.code === "slice-identity-collision"
                ? { collisions: [`.day-shift/planning/governed/${input.requestedSliceId ?? `${input.implementationOrders[0] ?? ""}-${input.sliceSlug}`}`] }
                : {});
        }
        throw error;
    }
    const phaseSlug = requireOrderedSlug(input.phaseSlug, "Governed phase slug");
    const milestoneSlugs = [...new Set(input.milestoneSlugs.map((slug) => requireOrderedSlug(slug, "Governed milestone slug")))].sort();
    if (milestoneSlugs.length === 0) {
        throw new PromotionPlanningError("invalid-request", "Governed promotion requires at least one destination milestone.");
    }
    const mapping = input.taskToMilestone ?? {};
    const knownTaskIds = new Set(tasks.map((task) => task.taskId));
    const unknown = Object.keys(mapping).filter((taskId) => !knownTaskIds.has(taskId)).sort();
    if (unknown.length > 0) {
        throw new PromotionPlanningError("incomplete-mapping", `Task mapping contains unknown source task ids: ${unknown.join(", ")}.`);
    }
    const normalized = {};
    const missing = [];
    const ambiguous = [];
    for (const task of tasks) {
        const value = mapping[task.taskId];
        const choices = typeof value === "string" ? [value] : Array.isArray(value) ? [...value] : [];
        if (choices.length === 0) {
            missing.push(task.taskId);
            continue;
        }
        if (choices.length !== 1) {
            ambiguous.push(`${task.taskId}: ${choices.join(", ")}`);
            continue;
        }
        const milestone = requireOrderedSlug(choices[0], `Destination milestone for '${task.taskId}'`);
        if (!milestoneSlugs.includes(milestone)) {
            throw new PromotionPlanningError("incomplete-mapping", `Destination milestone '${milestone}' for task '${task.taskId}' is not declared.`);
        }
        normalized[task.taskId] = milestone;
    }
    if (ambiguous.length > 0) {
        throw new PromotionPlanningError("ambiguous-mapping", "Every promoted task must have exactly one destination milestone.", { ambiguities: ambiguous });
    }
    if (missing.length > 0) {
        throw new PromotionPlanningError("incomplete-mapping", `Promotion is missing destination milestones for: ${missing.join(", ")}.`);
    }
    return {
        sliceId: identity.sliceId,
        sliceSlug: identity.sliceSlug,
        implementationOrder: identity.implementationOrder,
        phaseSlug,
        milestoneSlugs,
        taskToMilestone: normalized
    };
}
function buildRequiredParents(targetTopology, destinationRootPath, governed) {
    if (targetTopology.organizationParents.includes("work_overview")) {
        return [{
                kind: "work-overview",
                path: node_path_1.default.posix.join(destinationRootPath, "overview.md")
            }];
    }
    const parents = [];
    if (governed === null) {
        throw new PromotionPlanningError("invalid-topology", "Governed promotion topology requires an explicit Governed destination.");
    }
    const hierarchy = governed;
    const phaseRoot = node_path_1.default.posix.join(destinationRootPath, "phases", hierarchy.phaseSlug);
    if (targetTopology.organizationParents.includes("slice_overview")) {
        parents.push({
            kind: "slice-overview",
            path: node_path_1.default.posix.join(destinationRootPath, "slice-overview.md")
        });
    }
    if (targetTopology.organizationParents.includes("phase_overview")) {
        parents.push({
            kind: "phase-overview",
            path: node_path_1.default.posix.join(phaseRoot, "phase-overview.md")
        });
    }
    for (const milestoneSlug of hierarchy.milestoneSlugs) {
        const milestoneRoot = node_path_1.default.posix.join(phaseRoot, "milestones", milestoneSlug);
        if (targetTopology.organizationParents.includes("milestone_overview")) {
            parents.push({
                kind: "milestone-overview",
                path: node_path_1.default.posix.join(milestoneRoot, "milestone-overview.md")
            });
        }
        if (targetTopology.reconciliationArtifactKind === "milestone-reconciliation") {
            parents.push({
                kind: "reconciliation",
                path: node_path_1.default.posix.join(milestoneRoot, "reconciliation.md")
            });
        }
    }
    return parents;
}
function projectPromotionTargetTopology(targetLevel, override) {
    const defaultInput = targetLevel === "structured"
        ? {
            workflowLevel: "structured",
            planningDepth: "standard",
            organizationParents: ["work_overview"]
        }
        : {
            workflowLevel: "governed",
            planningDepth: "standard",
            organizationParents: [
                "slice_overview",
                "phase_overview",
                "milestone_overview"
            ]
        };
    const projection = (0, workflow_topology_1.projectWorkflowTopology)(override ?? defaultInput);
    const mismatchFinding = projection.topology !== null && projection.topology.workflowLevel !== targetLevel
        ? {
            code: "promotion-target-topology-mismatch",
            field: "workflow_level",
            message: `Promotion target '${targetLevel}' conflicts with projected topology level '${projection.topology.workflowLevel}'.`
        }
        : null;
    const findings = [
        ...projection.blockingFindings,
        ...(mismatchFinding === null ? [] : [mismatchFinding])
    ];
    if (projection.topology === null || findings.length > 0) {
        throw new PromotionPlanningError("invalid-topology", "Promotion target does not project a usable canonical workflow topology.", { topologyFindings: findings });
    }
    return projection.topology;
}
function buildTaskMapping(task, targetLevel, destinationRootPath, governed, structuredSequence) {
    let destinationMilestoneSlug = null;
    let destinationSequence = null;
    let destinationSequencePrefix = null;
    let destinationTaskRoot;
    if (targetLevel === "structured") {
        destinationSequence = structuredSequence;
        destinationSequencePrefix = String(structuredSequence).padStart(2, "0");
        destinationTaskRoot = node_path_1.default.posix.join(destinationRootPath, "tasks", (0, ordered_root_identity_1.composeOrderedDirectoryName)("structured-task", task.taskId, structuredSequence));
    }
    else {
        destinationMilestoneSlug = governed.taskToMilestone[task.taskId];
        destinationTaskRoot = node_path_1.default.posix.join(destinationRootPath, "phases", governed.phaseSlug, "milestones", destinationMilestoneSlug, "tasks", task.taskId);
    }
    return {
        taskId: task.taskId,
        sourceTaskDefinitionPath: task.taskDefinitionPath,
        sourceImplementationSummaryPath: task.implementationSummaryPath,
        destinationMilestoneSlug,
        destinationSequence,
        destinationSequencePrefix,
        destinationTaskDefinitionPath: node_path_1.default.posix.join(destinationTaskRoot, "task-definition.md"),
        destinationImplementationSummaryPath: node_path_1.default.posix.join(destinationTaskRoot, "implementation-summary.md")
    };
}
function sourcePreconditions(repositoryRoot, source, specifications) {
    const paths = [
        ...(source.overviewPath === "" ? [] : [source.overviewPath]),
        ...source.tasks.flatMap((task) => [task.taskDefinitionPath, task.implementationSummaryPath]),
        ...specifications.flatMap((specification) => {
            const readiness = (0, spec_readiness_1.resolveSpecificationPlanningReadinessSync)(repositoryRoot, specification.specPath);
            return [
                specification.specPath,
                readiness.dispositionPath,
                ...(readiness.reviewPath === null ? [] : [readiness.reviewPath])
            ];
        })
    ];
    return [...new Set(paths)].sort().map((inputPath) => {
        const target = requireExistingFile(repositoryRoot, inputPath, "Promotion source");
        return (0, mutation_preconditions_1.captureContentRevisionPreconditionSync)(target);
    });
}
function destinationPreconditions(repositoryRoot, parents, mappings, promotionRecord) {
    const paths = [
        ...parents.map((parent) => parent.path),
        promotionRecord.path,
        ...mappings.flatMap((mapping) => [
            mapping.destinationTaskDefinitionPath,
            mapping.destinationImplementationSummaryPath
        ])
    ];
    return [...new Set(paths)].sort().map((inputPath) => (0, mutation_preconditions_1.captureDestinationPresencePreconditionSync)((0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, inputPath)));
}
function buildMetadataChanges(source, mappings, targetLevel, workId, sliceId, parents, specifications) {
    const workOverview = parents.find((parent) => parent.kind === "work-overview")?.path;
    const sliceOverview = parents.find((parent) => parent.kind === "slice-overview")?.path;
    const phaseOverview = parents.find((parent) => parent.kind === "phase-overview")?.path;
    const resolvedSliceId = sliceOverview?.match(/^\.day-shift\/planning\/governed\/([^/]+)\/slice-overview\.md$/)?.[1];
    const phaseSlug = phaseOverview?.match(/\/phases\/([^/]+)\/phase-overview\.md$/)?.[1];
    const specificationValue = JSON.stringify(specifications.map((specification) => ({
        id: specification.specId,
        ref: specification.specPath,
        revision: specification.revision,
        readiness: specification.readiness
    })));
    const overviewChanges = source.overviewPath === ""
        || workOverview === undefined
        ? []
        : [{
                path: workOverview,
                field: "workflow_level",
                before: "structured",
                after: targetLevel
            }];
    return [...overviewChanges, ...source.tasks.flatMap((task) => {
            const mapping = mappings.find((candidate) => candidate.taskId === task.taskId);
            const milestoneOverview = mapping.destinationMilestoneSlug === null
                ? null
                : parents.find((parent) => parent.kind === "milestone-overview" &&
                    parent.path.includes(`/milestones/${mapping.destinationMilestoneSlug}/`))?.path ?? null;
            const commonTaskChanges = [
                {
                    path: mapping.destinationTaskDefinitionPath,
                    field: "workflow_level",
                    before: null,
                    after: targetLevel
                },
                ...(targetLevel === "structured" ? [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "work_id",
                        before: null,
                        after: workId ?? null
                    }, {
                        path: mapping.destinationTaskDefinitionPath,
                        field: "sequence",
                        before: null,
                        after: String(mapping.destinationSequence)
                    }] : source.workId === "" ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "work_id",
                        before: source.workId,
                        after: null
                    }]),
                {
                    path: mapping.destinationTaskDefinitionPath,
                    field: "upstream.specs",
                    before: null,
                    after: specificationValue
                },
                ...(targetLevel === "structured" ? [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "upstream.work_overview",
                        before: null,
                        after: workOverview
                    }] : []),
                ...(targetLevel === "governed" && source.overviewPath !== "" ? [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "upstream.work_overview",
                        before: source.overviewPath,
                        after: null
                    }] : []),
                ...(resolvedSliceId === undefined ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "slice",
                        before: null,
                        after: sliceId ?? null
                    }]),
                ...(phaseSlug === undefined ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "phase",
                        before: null,
                        after: phaseSlug
                    }]),
                ...(mapping.destinationMilestoneSlug === null ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "milestone",
                        before: null,
                        after: mapping.destinationMilestoneSlug
                    }, {
                        path: mapping.destinationTaskDefinitionPath,
                        field: "planning_depth",
                        before: null,
                        after: "standard"
                    }]),
                ...(sliceOverview === undefined ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "upstream.slice_overview",
                        before: null,
                        after: sliceOverview
                    }]),
                ...(phaseOverview === undefined ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "upstream.phase_overview",
                        before: null,
                        after: phaseOverview
                    }]),
                ...(milestoneOverview === null ? [] : [{
                        path: mapping.destinationTaskDefinitionPath,
                        field: "upstream.milestone_overview",
                        before: null,
                        after: milestoneOverview
                    }])
            ];
            const summaryChanges = commonTaskChanges
                .filter((change) => change.field !== "sequence")
                .map((change) => ({
                ...change,
                path: mapping.destinationImplementationSummaryPath
            }));
            return [
                ...commonTaskChanges,
                ...summaryChanges,
                {
                    path: mapping.destinationImplementationSummaryPath,
                    field: "upstream.task_definition",
                    before: task.taskDefinitionPath,
                    after: mapping.destinationTaskDefinitionPath
                }
            ];
        })].sort((left, right) => left.path.localeCompare(right.path) || left.field.localeCompare(right.field));
}
function buildPromotionReadinessTransitions(repositoryRoot, sourceLevel, targetLevel, source, mappings, metadataChanges) {
    if (sourceLevel !== "structured" || targetLevel !== "governed") {
        return { transitions: [], metadataChanges: [] };
    }
    const transitions = [];
    const readinessChanges = [];
    for (const task of source.tasks) {
        const mapping = mappings.find((candidate) => candidate.taskId === task.taskId);
        if (mapping === undefined) {
            throw new PromotionPlanningError("incomplete-mapping", `Promotion readiness translation is missing task mapping '${task.taskId}'.`);
        }
        const sourceTask = node_fs_1.default.readFileSync(requireExistingFile(repositoryRoot, task.taskDefinitionPath, "Promotion readiness source task").runtimePath, "utf8");
        const sourceSummary = node_fs_1.default.readFileSync(requireExistingFile(repositoryRoot, task.implementationSummaryPath, "Promotion readiness source summary").runtimePath, "utf8");
        const rewrittenTask = applyPromotionMetadataChanges(sourceTask, metadataChanges.filter((change) => change.path === mapping.destinationTaskDefinitionPath));
        let translated;
        try {
            translated = (0, task_lifecycle_1.translatePromotedTaskReadinessAuthorization)(sourceTask, sourceSummary, rewrittenTask);
        }
        catch (error) {
            throw new PromotionPlanningError("invalid-source", `Task '${task.taskId}' readiness evidence cannot be promoted safely: ${error instanceof Error ? error.message : "unknown readiness translation failure"}`);
        }
        transitions.push({
            taskId: task.taskId,
            sourceTaskDefinitionPath: task.taskDefinitionPath,
            destinationTaskDefinitionPath: mapping.destinationTaskDefinitionPath,
            action: translated.action,
            readinessReviewId: translated.readinessReviewId,
            sourceRevision: translated.sourceRevision,
            destinationRevision: translated.destinationRevision
        });
        if (translated.action === "translated-review-revision"
            && translated.sourceRevision !== null
            && translated.destinationRevision !== null) {
            readinessChanges.push({
                path: mapping.destinationTaskDefinitionPath,
                field: "readiness_review_revision",
                before: translated.sourceRevision,
                after: translated.destinationRevision
            });
        }
    }
    return {
        transitions: transitions.sort((left, right) => left.taskId.localeCompare(right.taskId)),
        metadataChanges: readinessChanges
    };
}
function applyPromotionMetadataChanges(markdownText, changes) {
    let result = markdownText;
    for (const change of [...changes].sort((left, right) => left.field.localeCompare(right.field))) {
        if (change.after === null) {
            result = change.field.startsWith("upstream.")
                ? removeUpstreamScalar(result, change.field.slice("upstream.".length))
                : removeTopLevelScalar(result, change.field);
        }
        else if (change.field === "upstream.specs") {
            result = replaceUpstreamSpecs(result, parseSpecificationChange(change.after));
        }
        else if (change.field.startsWith("upstream.")) {
            result = upsertUpstreamScalar(result, change.field.slice("upstream.".length), change.after);
        }
        else {
            result = upsertTopLevelScalar(result, change.field, change.after);
        }
    }
    return result;
}
function removeTopLevelScalar(markdownText, field) {
    const lines = splitFrontMatter(markdownText);
    const fieldPattern = new RegExp(`^${escapeRegExp(field)}:\\s*`);
    lines.frontMatter = lines.frontMatter.filter((line) => !fieldPattern.test(line));
    return joinFrontMatter(lines);
}
function removeUpstreamScalar(markdownText, field) {
    const lines = splitFrontMatter(markdownText);
    const range = ensureUpstreamBlock(lines.frontMatter);
    const pattern = new RegExp(`^  ${escapeRegExp(field)}:\\s*`);
    lines.frontMatter = lines.frontMatter.filter((line, lineIndex) => !(lineIndex > range.start && lineIndex < range.end && pattern.test(line)));
    return joinFrontMatter(lines);
}
function upsertTopLevelScalar(markdownText, field, value) {
    const lines = splitFrontMatter(markdownText);
    const fieldPattern = new RegExp(`^${escapeRegExp(field)}:\\s*`);
    const index = lines.frontMatter.findIndex((line) => fieldPattern.test(line));
    const rendered = `${field}: ${JSON.stringify(value)}`;
    if (index >= 0) {
        lines.frontMatter[index] = rendered;
    }
    else {
        const workflowIndex = lines.frontMatter.findIndex((line) => /^workflow_level:\s*/.test(line));
        lines.frontMatter.splice(workflowIndex >= 0 ? workflowIndex + 1 : lines.frontMatter.length, 0, rendered);
    }
    return joinFrontMatter(lines);
}
function upsertUpstreamScalar(markdownText, field, value) {
    const lines = splitFrontMatter(markdownText);
    const range = ensureUpstreamBlock(lines.frontMatter);
    const pattern = new RegExp(`^  ${escapeRegExp(field)}:\\s*`);
    const index = lines.frontMatter.findIndex((line, lineIndex) => lineIndex > range.start && lineIndex < range.end && pattern.test(line));
    const rendered = `  ${field}: ${JSON.stringify(value)}`;
    if (index >= 0) {
        lines.frontMatter[index] = rendered;
    }
    else {
        lines.frontMatter.splice(range.end, 0, rendered);
    }
    return joinFrontMatter(lines);
}
function replaceUpstreamSpecs(markdownText, specifications) {
    const lines = splitFrontMatter(markdownText);
    let range = ensureUpstreamBlock(lines.frontMatter);
    const specsIndex = lines.frontMatter.findIndex((line, lineIndex) => lineIndex > range.start && lineIndex < range.end && /^ {2}specs:\s*$/.test(line));
    if (specsIndex >= 0) {
        let end = specsIndex + 1;
        while (end < range.end && /^\s{4,}/.test(lines.frontMatter[end]))
            end += 1;
        lines.frontMatter.splice(specsIndex, end - specsIndex);
        range = ensureUpstreamBlock(lines.frontMatter);
    }
    const block = [
        "  specs:",
        ...specifications.flatMap((specification) => [
            `    - id: ${JSON.stringify(specification.id)}`,
            `      ref: ${JSON.stringify(specification.ref)}`,
            `      revision: ${JSON.stringify(specification.revision)}`,
            `      readiness: ${JSON.stringify(specification.readiness)}`
        ])
    ];
    lines.frontMatter.splice(range.start + 1, 0, ...block);
    return joinFrontMatter(lines);
}
function splitFrontMatter(markdownText) {
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(markdownText);
    if (!document.hasFrontMatter) {
        throw new PromotionPlanningError("invalid-source", "Promotion task artifacts require YAML front matter.");
    }
    return {
        frontMatter: [...document.frontMatterLines],
        bodyText: document.bodyText,
        trailingNewline: markdownText.endsWith("\n")
    };
}
function joinFrontMatter(input) {
    const text = ["---", ...input.frontMatter, "---", input.bodyText].join("\n");
    return input.trailingNewline && !text.endsWith("\n") ? `${text}\n` : text;
}
function ensureUpstreamBlock(lines) {
    let start = lines.findIndex((line) => /^upstream:\s*$/.test(line));
    if (start < 0) {
        lines.push("upstream:");
        start = lines.length - 1;
    }
    let end = start + 1;
    while (end < lines.length && /^\s/.test(lines[end]))
        end += 1;
    return { start, end };
}
function parseSpecificationChange(value) {
    let parsed;
    try {
        parsed = JSON.parse(value);
    }
    catch {
        throw new PromotionPlanningError("invalid-source", "Promotion plan contains malformed upstream specification metadata.");
    }
    if (!Array.isArray(parsed) || parsed.some((entry) => entry === null
        || typeof entry !== "object"
        || typeof entry.id !== "string"
        || typeof entry.ref !== "string"
        || typeof entry.revision !== "string"
        || typeof entry.readiness !== "string")) {
        throw new PromotionPlanningError("invalid-source", "Promotion plan contains invalid upstream specification metadata.");
    }
    return parsed;
}
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function listExistingGovernedSliceIds(repositoryRoot) {
    const root = node_path_1.default.join(repositoryRoot, ".day-shift", "planning", "governed");
    if (!node_fs_1.default.statSync(root, { throwIfNoEntry: false })?.isDirectory())
        return [];
    return node_fs_1.default.readdirSync(root, { withFileTypes: true })
        .filter((entry) => entry.isDirectory() &&
        !entry.isSymbolicLink() &&
        ORDERED_ROOT_SLUG_PATTERN.test(entry.name))
        .map((entry) => entry.name)
        .sort();
}
function buildAffectedPaths(source, specifications, parents, mappings, promotionRecord) {
    const entries = [
        ...(source.overviewPath === "" ? [] : [{ path: source.overviewPath, role: "source" }]),
        ...source.tasks.flatMap((task) => [
            { path: task.taskDefinitionPath, role: "source" },
            { path: task.implementationSummaryPath, role: "source" }
        ]),
        ...specifications.map((specification) => ({
            path: specification.specPath,
            role: "source-specification"
        })),
        ...parents.map((parent) => ({ path: parent.path, role: "required-parent" })),
        { path: promotionRecord.path, role: "promotion-record" },
        ...mappings.flatMap((mapping) => [
            { path: mapping.destinationTaskDefinitionPath, role: "destination" },
            { path: mapping.destinationImplementationSummaryPath, role: "destination" }
        ])
    ];
    const unique = new Map(entries.map((entry) => [`${entry.role}:${entry.path}`, entry]));
    return [...unique.values()].sort((left, right) => left.path.localeCompare(right.path) || left.role.localeCompare(right.role));
}
function parseStructuredSpecifications(lines) {
    const result = [];
    let current = null;
    const finish = () => {
        if (current !== null &&
            current.specId !== undefined &&
            current.specPath !== undefined &&
            current.revision !== undefined &&
            (current.readiness === "current-ready" || current.readiness === "current-waived")) {
            result.push({
                specId: current.specId,
                specPath: current.specPath,
                revision: current.revision,
                readiness: current.readiness
            });
        }
        current = null;
    };
    for (const line of lines) {
        const id = line.match(/^\s{4}-\s+id:\s*"?([^"]+)"?\s*$/);
        if (id !== null) {
            finish();
            current = { specId: id[1] };
            continue;
        }
        const field = line.match(/^\s{6}(ref|revision|readiness):\s*"?([^"]+)"?\s*$/);
        if (field === null || current === null)
            continue;
        if (field[1] === "ref")
            current.specPath = field[2];
        if (field[1] === "revision")
            current.revision = field[2];
        if (field[1] === "readiness")
            current.readiness = field[2];
    }
    finish();
    return result;
}
function validateStructuredSpecification(repositoryRoot, specification) {
    const readiness = (0, spec_readiness_1.resolveSpecificationPlanningReadinessSync)(repositoryRoot, specification.specPath);
    if (readiness.specId !== specification.specId ||
        readiness.currentSpecRevision !== specification.revision) {
        throw new PromotionPlanningError("stale-source", `Structured specification '${specification.specId}' no longer matches its recorded identity or revision.`);
    }
    return {
        specId: specification.specId,
        specPath: specification.specPath,
        revision: specification.revision,
        readiness: specification.readiness
    };
}
function readStructuredTaskPair(repositoryRoot, tasksRootPath, taskDirectoryName) {
    const taskDefinitionPath = node_path_1.default.posix.join(tasksRootPath, taskDirectoryName, "task-definition.md");
    const implementationSummaryPath = node_path_1.default.posix.join(tasksRootPath, taskDirectoryName, "implementation-summary.md");
    const target = requireExistingFile(repositoryRoot, taskDefinitionPath, "Structured task definition");
    requireExistingFile(repositoryRoot, implementationSummaryPath, "Structured implementation summary");
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(target.runtimePath, "utf8"));
    const taskId = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "task")
        ?? (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "id");
    const parsedRoot = parsePromotionDirectory("structured-task", taskDirectoryName);
    const sequenceText = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "sequence");
    if (taskId === undefined ||
        parsedRoot.shape !== "canonical" ||
        parsedRoot.stableId !== taskId ||
        sequenceText !== String(parsedRoot.order) ||
        !ID_PATTERN.test(taskId)) {
        throw new PromotionPlanningError("invalid-source", `Structured task directory '${taskDirectoryName}' does not match its task identity.`);
    }
    return { taskId, taskDefinitionPath, implementationSummaryPath };
}
function parsePromotionDirectory(context, basename) {
    try {
        return (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)(context, basename);
    }
    catch (error) {
        throw normalizeOrderedPromotionError(error);
    }
}
function normalizeOrderedPromotionError(error) {
    return new PromotionPlanningError(error instanceof Error && /already|duplicate|occupied|collision/i.test(error.message)
        ? "destination-conflict"
        : "invalid-request", error instanceof Error ? error.message : "Ordered promotion identity is invalid.");
}
function requireExistingFile(repositoryRoot, inputPath, label) {
    const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, inputPath);
    if (!target.exists || !node_fs_1.default.statSync(target.runtimePath).isFile()) {
        throw new PromotionPlanningError("invalid-source", `${label} '${target.repositoryRelativePath}' is missing.`);
    }
    return target;
}
function publicCondition(condition) {
    return condition.kind === "content-revision"
        ? {
            path: condition.repositoryRelativePath,
            kind: condition.kind,
            expectedState: condition.expectedRevision
        }
        : {
            path: condition.repositoryRelativePath,
            kind: condition.kind,
            expectedState: condition.expectedPresence
        };
}
function comparePathThenKind(left, right) {
    return left.path.localeCompare(right.path) || left.kind.localeCompare(right.kind);
}
function validateTransition(sourceLevel, targetLevel) {
    if ((sourceLevel === "basic" && (targetLevel === "structured" || targetLevel === "governed")) ||
        (sourceLevel === "structured" && targetLevel === "governed"))
        return;
    throw new PromotionPlanningError("unsupported-transition", `Unsupported promotion transition '${sourceLevel}' to '${targetLevel}'. Demotion is not supported.`);
}
function requireRepositoryRoot(value) {
    const root = requireText(value, "Repository root");
    const resolved = node_path_1.default.resolve(root);
    const stats = node_fs_1.default.statSync(resolved, { throwIfNoEntry: false });
    if (stats === undefined || !stats.isDirectory()) {
        throw new PromotionPlanningError("invalid-request", `Repository root '${value}' is not a directory.`);
    }
    return resolved;
}
function requireText(value, label) {
    if (typeof value !== "string" || value.trim() === "") {
        throw new PromotionPlanningError("invalid-request", `${label} is required.`);
    }
    return value.trim();
}
function requireId(value, label) {
    const normalized = requireText(value, label);
    if (!ID_PATTERN.test(normalized)) {
        throw new PromotionPlanningError("invalid-request", `${label} must be lowercase kebab-case.`);
    }
    return normalized;
}
function requireOrderedSlug(value, label) {
    const normalized = requireText(value, label);
    if (!ORDERED_CHILD_SLUG_PATTERN.test(normalized)) {
        throw new PromotionPlanningError("invalid-request", `${label} must be an ordered lowercase kebab-case slug.`);
    }
    return normalized;
}
