"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkspaceMigrationPlanningError = exports.WORKSPACE_STRUCTURE_MIGRATION_PROTOCOL_VERSION = void 0;
exports.parseWorkspaceMigrationSelections = parseWorkspaceMigrationSelections;
exports.applyWorkspaceMigrationOrderingSelections = applyWorkspaceMigrationOrderingSelections;
exports.planWorkspaceStructureMigrationSync = planWorkspaceStructureMigrationSync;
exports.rewriteKnownRepositoryPaths = rewriteKnownRepositoryPaths;
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 paths_1 = require("../paths");
const canonical_write_target_1 = require("../paths/canonical-write-target");
const legacy_compatibility_projection_1 = require("./legacy-compatibility-projection");
const ordered_root_identity_1 = require("./ordered-root-identity");
const planning_document_utils_1 = require("./planning-document-utils");
const task_lifecycle_1 = require("./task-lifecycle");
const work_unit_1 = require("./workspace-migration-planning/work-unit");
exports.WORKSPACE_STRUCTURE_MIGRATION_PROTOCOL_VERSION = 1;
const ORDERED_SLICE_ID_PATTERN = /^(?:0\d|[1-9]\d+)-[a-z0-9]+(?:-[a-z0-9]+)*$/;
const SOURCE_INVENTORY_FILE_LIMIT = 50_000;
class WorkspaceMigrationPlanningError extends Error {
    code;
    constructor(message) {
        super(message);
        this.name = "WorkspaceMigrationPlanningError";
        this.code = "invalid-request";
    }
}
exports.WorkspaceMigrationPlanningError = WorkspaceMigrationPlanningError;
function parseWorkspaceMigrationSelections(values) {
    if (!Array.isArray(values)) {
        throw new WorkspaceMigrationPlanningError("Migration selections must be an array.");
    }
    return values.map((value) => {
        const match = value.match(/^([^=]+)=(basic|structured|governed)$/);
        if (match === null) {
            throw new WorkspaceMigrationPlanningError(`Migration mapping '${value}' must use <work-unit-id>=<basic|structured|governed>.`);
        }
        return { workUnitId: match[1], workflowLevel: match[2] };
    });
}
function applyWorkspaceMigrationOrderingSelections(selections, rootOrders, taskSequences) {
    const byUnit = new Map(selections.map((selection) => [selection.workUnitId, {
            ...selection,
            taskSequences: { ...(selection.taskSequences ?? {}) }
        }]));
    for (const value of rootOrders) {
        const match = value.match(/^([^=]+)=((?:0[1-9])|(?:[1-9]\d+))$/u);
        if (match === null || !byUnit.has(match[1])) {
            throw new WorkspaceMigrationPlanningError(`Root-order mapping '${value}' requires a selected work-unit id and a positive order rendered with at least two digits.`);
        }
        const selection = byUnit.get(match[1]);
        if (selection.implementationOrder !== undefined) {
            throw new WorkspaceMigrationPlanningError(`Root-order mapping for '${match[1]}' was supplied more than once.`);
        }
        selection.implementationOrder = match[2];
    }
    for (const value of taskSequences) {
        const match = value.match(/^([^:]+):([^=]+)=([1-9]\d?)$/u);
        if (match === null || !byUnit.has(match[1]) || !validId(match[2])) {
            throw new WorkspaceMigrationPlanningError(`Task-sequence mapping '${value}' requires <selected-work-unit-id>:<task-id>=<1..99>.`);
        }
        const selection = byUnit.get(match[1]);
        const sequence = Number.parseInt(match[3], 10);
        if (selection.taskSequences[match[2]] !== undefined) {
            throw new WorkspaceMigrationPlanningError(`Task-sequence mapping for '${match[1]}:${match[2]}' was supplied more than once.`);
        }
        selection.taskSequences[match[2]] = sequence;
    }
    return [...byUnit.values()].map((selection) => ({
        ...selection,
        ...(Object.keys(selection.taskSequences ?? {}).length === 0
            ? { taskSequences: undefined }
            : {})
    }));
}
/**
 * Builds the complete authorization preview for structural workspace migration.
 *
 * This service intentionally performs no writes. Its digest covers all selected
 * mappings, path/revision preconditions, preservation promises, and blockers.
 */
function planWorkspaceStructureMigrationSync(options) {
    const repositoryRoot = requireRepositoryRoot(options.repositoryRoot);
    const planningRootPath = (0, paths_1.parseRepositoryRelativePath)(options.planningRootPath ?? ".day-shift/planning").normalizedPath;
    const explicitSelections = normalizeSelections(options.selections ?? []);
    const projection = (0, legacy_compatibility_projection_1.projectLegacyPlanningCompatibility)({
        repositoryRoot,
        planningRootPath,
        generatedAt: "not-recorded"
    });
    const knownUnitIds = new Set(projection.workUnits.map((unit) => unit.workUnitId));
    const findings = [];
    if (projection.historicalCompatibilityDisposition.status === "invalid") {
        findings.push(finding("INVALID_COMPATIBILITY_DISPOSITION", [projection.historicalCompatibilityDisposition.path], projection.historicalCompatibilityDisposition.invalidReason
            ?? "Historical compatibility disposition is invalid.", "Repair or remove the invalid disposition, then refresh the no-write migration plan."));
    }
    for (const selection of explicitSelections) {
        if (!knownUnitIds.has(selection.workUnitId)) {
            findings.push(finding("INVALID_SELECTION", [], `Explicit mapping references unknown work unit '${selection.workUnitId}'.`, "Refresh the migration plan and select one of its reported work-unit ids."));
        }
    }
    if (!projection.discovery.complete) {
        findings.push(finding("INCOMPLETE_DISCOVERY", projection.discovery.omission?.nextPath === undefined
            ? []
            : [joinRepositoryPath(planningRootPath, projection.discovery.omission.nextPath)], "Legacy planning discovery is incomplete; structural migration cannot be authorized.", "Increase the bounded discovery budget or reduce the selected planning root, then refresh the plan."));
    }
    const selectionMap = new Map(explicitSelections.map((selection) => [selection.workUnitId, selection]));
    const orderingAssignments = buildOrderingAssignments(repositoryRoot, planningRootPath, projection.workUnits, selectionMap, findings);
    const workUnits = projection.workUnits.map((unit) => planWorkUnit(repositoryRoot, planningRootPath, unit, selectionMap.get(unit.workUnitId), orderingAssignments.get(unit.workUnitId) ?? null, findings));
    enforceFormerContainerAtomicity(workUnits, findings);
    const sharedEvidenceArchives = uniqueSharedEvidenceArchives(workUnits);
    const pathReplacements = buildPathReplacements(planningRootPath, workUnits, sharedEvidenceArchives);
    const retirementPaths = uniqueSorted(workUnits.flatMap((unit) => unit.actionable
        ? [
            ...unit.sourcePaths,
            ...(unit.sharedEvidence === null ? [] : [unit.sharedEvidence.sourcePath])
        ]
        : []));
    const repositoryUpdates = buildRepositoryUpdates(repositoryRoot, pathReplacements, new Set(retirementPaths), findings);
    const metadataInsertions = collectMetadataInsertions(workUnits);
    const revisionRebindings = collectRevisionRebindings(repositoryRoot, workUnits, repositoryUpdates);
    const recoveryActions = [
        "stage-complete-nested-destinations",
        "rewrite-canonical-references",
        "rebind-path-only-lifecycle-revisions",
        "verify-staged-destinations",
        "retire-source-task-directories-before-roots",
        "roll-forward-with-exact-recovery-fingerprint",
        "restore-with-exact-recovery-fingerprint"
    ];
    const migrationIdentity = (0, node_crypto_1.createHash)("sha256").update(stableJson({
        planningRootPath,
        explicitSelections,
        workUnits: workUnits.map((unit) => ({
            workUnitId: unit.workUnitId,
            workId: unit.workId,
            sliceId: unit.sliceId,
            sourceRootPath: unit.sourceRootPath,
            destinationRootPath: unit.destinationRootPath,
            sourcePaths: unit.sourcePaths
        })),
        sharedEvidenceArchives,
        pathReplacements,
        repositoryUpdates,
        metadataInsertions,
        revisionRebindings,
        recoveryActions
    })).digest("hex").slice(0, 16);
    const migrationRecordPath = `.day-shift/state/migrations/workspace-structure-${migrationIdentity}.json`;
    const recordTarget = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, migrationRecordPath);
    if (recordTarget.exists) {
        findings.push(finding("DESTINATION_COLLISION", [migrationRecordPath], `Migration record '${migrationRecordPath}' already exists.`, "Inspect the prior migration record and refresh the plan after resolving duplicate active state."));
    }
    const expected = uniqueExpected([
        ...workUnits.flatMap((unit) => unit.expected),
        ...sharedEvidenceArchives.map((evidence) => ({
            path: evidence.archivePath,
            kind: "destination-presence",
            expectedState: "absent"
        })),
        ...repositoryUpdates.flatMap((update) => [
            {
                path: update.path,
                kind: "destination-presence",
                expectedState: "present"
            },
            {
                path: update.path,
                kind: "content-revision",
                expectedState: update.beforeRevision
            }
        ]),
        {
            path: migrationRecordPath,
            kind: "destination-presence",
            expectedState: "absent"
        }
    ]);
    const affectedPaths = uniqueSorted([
        ...workUnits.flatMap((unit) => [
            ...unit.sourcePaths,
            ...unit.destinationPaths
        ]),
        ...retirementPaths,
        ...sharedEvidenceArchives.map((evidence) => evidence.archivePath),
        ...repositoryUpdates.map((update) => update.path),
        migrationRecordPath
    ]);
    const status = findings.length > 0
        ? "blocked"
        : workUnits.every((unit) => unit.alreadyCanonical || !unit.actionable)
            ? "not-needed"
            : "ready";
    const digestInput = {
        protocolVersion: exports.WORKSPACE_STRUCTURE_MIGRATION_PROTOCOL_VERSION,
        status,
        planningRootPath,
        migrationRecordPath,
        explicitSelections,
        workUnits,
        historicalCompatibilityDisposition: projection.historicalCompatibilityDisposition,
        sharedEvidenceArchives,
        pathReplacements,
        repositoryUpdates,
        metadataInsertions,
        revisionRebindings,
        recoveryActions,
        retirementPaths,
        findings: sortFindings(findings),
        expected,
        affectedPaths,
        recoveryGuidance: recoveryGuidance(status)
    };
    const digestHex = (0, node_crypto_1.createHash)("sha256")
        .update(stableJson(digestInput))
        .digest("hex");
    return {
        planId: `workspace-migration-${digestHex.slice(0, 16)}`,
        planDigest: `sha256:${digestHex}`,
        ...digestInput,
        readOnly: true,
        migrationApplied: false
    };
}
function buildOrderingAssignments(repositoryRoot, planningRootPath, units, selections, findings) {
    const assignments = new Map();
    const occupiedByLevel = new Map([
        ["basic", new Set()],
        ["structured", new Set()],
        ["governed", new Set()]
    ]);
    const candidates = units.map((unit) => {
        const sourceRootPath = sourceRootForUnit(planningRootPath, unit);
        const physicalLevel = physicalWorkflowLevelFor(unit, planningRootPath, sourceRootPath);
        const selection = selections.get(unit.workUnitId);
        const level = selection?.workflowLevel ?? unit.proposedWorkflowLevel ?? physicalLevel;
        const workId = deriveWorkId(repositoryRoot, planningRootPath, unit, sourceRootPath);
        const stableId = level === "basic"
            ? requireTaskId(unit.taskPairs[0], unit.workUnitId)
            : workId;
        const artifactPath = level === "basic"
            ? unit.taskPairs[0]?.taskDefinitionPath
            : level === "structured"
                ? joinRepositoryPath(sourceRootPath, "overview.md")
                : undefined;
        const recordedOrder = artifactPath === undefined
            ? null
            : readOptionalScalar(repositoryRoot, artifactPath, "implementation_order");
        let parsedOrder = null;
        let parsedStableId = null;
        try {
            const parsed = (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)(level === "basic" ? "basic-root" : "structured-root", node_path_1.default.posix.basename(sourceRootPath));
            parsedOrder = parsed.orderPrefix;
            parsedStableId = parsed.stableId;
        }
        catch {
            // The legacy source name is intentionally not treated as ordering evidence.
        }
        return {
            unit,
            selection,
            level,
            sourceRootPath,
            stableId,
            recordedOrder,
            parsedOrder,
            parsedStableId
        };
    }).filter((candidate) => candidate.level === "basic" || candidate.level === "structured")
        .sort((left, right) => `${left.level}\0${left.stableId}\0${left.unit.workUnitId}`
        .localeCompare(`${right.level}\0${right.stableId}\0${right.unit.workUnitId}`));
    const reservedOwners = new Map();
    for (const candidate of candidates) {
        if (candidate.parsedOrder === null
            || candidate.parsedStableId !== candidate.stableId
            || candidate.recordedOrder !== candidate.parsedOrder)
            continue;
        const key = `${candidate.level}:${candidate.parsedOrder}`;
        const prior = reservedOwners.get(key);
        if (prior !== undefined && prior !== candidate.unit.workUnitId) {
            findings.push(finding("ROOT_ORDER_COLLISION", [candidate.sourceRootPath], `${candidate.level} implementation order '${candidate.parsedOrder}' is already owned by '${prior}'.`, "Repair duplicate numbered roots before refreshing the migration preview."));
            continue;
        }
        reservedOwners.set(key, candidate.unit.workUnitId);
        occupiedByLevel.get(candidate.level).add(candidate.parsedOrder);
    }
    for (const candidate of candidates) {
        const level = candidate.level;
        const occupied = occupiedByLevel.get(level);
        let order = null;
        let source = "deterministic";
        const requested = candidate.selection?.implementationOrder;
        if (candidate.parsedOrder !== null
            && candidate.recordedOrder !== null
            && candidate.parsedOrder !== candidate.recordedOrder) {
            findings.push(finding("INVALID_ROOT_ORDER", [candidate.sourceRootPath], `Numbered root prefix '${candidate.parsedOrder}' disagrees with implementation_order '${candidate.recordedOrder}'.`, "Repair the physical/metadata order mismatch before refreshing the migration preview."));
        }
        try {
            if (requested !== undefined) {
                order = (0, ordered_root_identity_1.normalizeImplementationOrder)(requested, `migration root order for '${candidate.unit.workUnitId}'`);
                source = "explicit";
            }
            else if (candidate.parsedOrder !== null
                && candidate.parsedStableId === candidate.stableId
                && candidate.recordedOrder === candidate.parsedOrder) {
                order = candidate.parsedOrder;
                source = "existing-prefix";
            }
            else if (candidate.recordedOrder !== null) {
                order = (0, ordered_root_identity_1.normalizeImplementationOrder)(candidate.recordedOrder, `recorded implementation_order for '${candidate.unit.workUnitId}'`);
                source = "existing-metadata";
            }
        }
        catch (error) {
            findings.push(finding("INVALID_ROOT_ORDER", [candidate.sourceRootPath], error instanceof Error ? error.message : "Migration root order is invalid.", "Supply one explicit unused root order of at least two digits and refresh the preview."));
        }
        if (order === null) {
            order = nextMigrationOrder(occupied);
            source = "deterministic";
        }
        const reservedOwner = reservedOwners.get(`${level}:${order}`);
        if (occupied.has(order) && reservedOwner !== candidate.unit.workUnitId) {
            findings.push(finding("ROOT_ORDER_COLLISION", [candidate.sourceRootPath], `${level} implementation order '${order}' is assigned more than once.`, "Supply explicit non-colliding root-order mappings and refresh the preview."));
        }
        else if (!occupied.has(order)) {
            occupied.add(order);
        }
        const taskSequences = level === "structured"
            ? buildTaskSequenceAssignments(repositoryRoot, candidate.unit, candidate.selection, findings)
            : {};
        assignments.set(candidate.unit.workUnitId, {
            implementationOrder: order,
            implementationOrderSource: source,
            orderedRootName: (0, ordered_root_identity_1.composeOrderedDirectoryName)(level === "basic" ? "basic-root" : "structured-root", candidate.stableId, order),
            taskSequences
        });
    }
    return assignments;
}
function buildTaskSequenceAssignments(repositoryRoot, unit, selection, findings) {
    const result = {};
    const occupied = new Set();
    for (const pair of [...unit.taskPairs].sort((left, right) => left.taskDefinitionPath.localeCompare(right.taskDefinitionPath))) {
        const taskId = requireTaskId(pair, unit.workUnitId);
        const explicit = selection?.taskSequences?.[taskId];
        const recorded = readOptionalScalar(repositoryRoot, pair.taskDefinitionPath, "sequence");
        const value = explicit ?? (recorded !== null && /^\d+$/u.test(recorded)
            ? Number.parseInt(recorded, 10)
            : Number.NaN);
        if (!Number.isInteger(value) || value < 1 || value > 99) {
            findings.push(finding("INVALID_TASK_SEQUENCE", [pair.taskDefinitionPath], `Structured task '${taskId}' has no valid parent-local sequence from 1 through 99.`, `Repair sequence metadata or supply an explicit task-sequence mapping for '${taskId}'.`));
            continue;
        }
        try {
            const sourceDirectory = node_path_1.default.posix.basename(node_path_1.default.posix.dirname(pair.taskDefinitionPath));
            const parsed = (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)("structured-task", sourceDirectory);
            if (parsed.order !== null
                && parsed.stableId === taskId
                && parsed.order !== value) {
                findings.push(finding("INVALID_TASK_SEQUENCE", [pair.taskDefinitionPath], `Structured task prefix '${parsed.orderPrefix}' disagrees with sequence '${value}'.`, "Repair the parent-local sequence/prefix mismatch before refreshing the preview."));
                continue;
            }
        }
        catch {
            // Legacy basenames are mapped from their semantic task id and sequence metadata.
        }
        if (occupied.has(value)) {
            findings.push(finding("TASK_SEQUENCE_COLLISION", [pair.taskDefinitionPath], `Structured parent '${unit.workUnitId}' repeats task sequence '${value}'.`, "Supply explicit unique parent-local task sequences and refresh the preview."));
            continue;
        }
        occupied.add(value);
        result[taskId] = {
            sequence: value,
            source: explicit === undefined ? "existing-metadata" : "explicit"
        };
    }
    return result;
}
function nextMigrationOrder(occupied) {
    for (let order = 1; Number.isSafeInteger(order); order += 1) {
        const candidate = String(order).padStart(2, "0");
        if (!occupied.has(candidate))
            return candidate;
    }
    throw new WorkspaceMigrationPlanningError("No migration root order remains within the supported integer range.");
}
function readOptionalScalar(repositoryRoot, repositoryPath, field) {
    try {
        const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, repositoryPath);
        if (!target.exists || !node_fs_1.default.statSync(target.runtimePath).isFile())
            return null;
        return (0, planning_document_utils_1.readTopLevelField)((0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(target.runtimePath, "utf8")).frontMatterLines, field) ?? null;
    }
    catch {
        return null;
    }
}
function planWorkUnit(repositoryRoot, planningRootPath, unit, explicitSelection, ordering, planFindings) {
    const sourceInventory = inventoryWorkUnitSources(repositoryRoot, planningRootPath, unit);
    const sourcePaths = sourceInventory.sourcePaths;
    const sourceRootPath = sourceInventory.sourceRootPath;
    const canonicalWorkflowLevel = canonicalWorkflowLevelFor(unit, planningRootPath, sourceRootPath);
    const physicalWorkflowLevel = physicalWorkflowLevelFor(unit, planningRootPath, sourceRootPath);
    const { selectedWorkflowLevel, selectionSource, alreadyCanonical, governedSliceIdentityValid } = (0, work_unit_1.projectWorkUnitDisposition)({
        explicitWorkflowLevel: explicitSelection?.workflowLevel,
        proposedWorkflowLevel: unit.proposedWorkflowLevel,
        canonicalWorkflowLevel,
        physicalWorkflowLevel,
        implementationOrderSource: ordering?.implementationOrderSource ?? null,
        structuredTaskPathsCanonical: unit.taskPairs.every((pair) => {
            const taskId = requireTaskId(pair, unit.workUnitId);
            const sequence = ordering?.taskSequences[taskId]?.sequence;
            if (sequence === undefined)
                return false;
            return node_path_1.default.posix.basename(node_path_1.default.posix.dirname(pair.taskDefinitionPath))
                === (0, ordered_root_identity_1.composeOrderedDirectoryName)("structured-task", taskId, sequence);
        }),
        sliceId: unit.sliceId
    });
    const workId = deriveWorkId(repositoryRoot, planningRootPath, unit, sourceRootPath);
    const destinationRootPath = selectedWorkflowLevel === null || alreadyCanonical
        ? null
        : selectedWorkflowLevel === "governed" && unit.sliceId !== null
            ? joinRepositoryPath(planningRootPath, "governed", unit.sliceId)
            : selectedWorkflowLevel === "structured" && ordering !== null
                ? joinRepositoryPath(planningRootPath, "structured", ordering.orderedRootName)
                : selectedWorkflowLevel === "basic" && ordering !== null
                    ? joinRepositoryPath(planningRootPath, "basic/tasks", ordering.orderedRootName)
                    : null;
    const sharedEvidence = selectedWorkflowLevel === "governed"
        && unit.layoutKind === "former-governed-container-slice"
        && unit.formerContainerIdentity !== null
        && unit.sliceId !== null
        ? planSharedEvidence(repositoryRoot, planningRootPath, unit.formerContainerIdentity, unit.sliceId, planFindings)
        : null;
    (0, work_unit_1.appendInitialWorkUnitFindings)(unit, selectedWorkflowLevel, governedSliceIdentityValid, sourcePaths, sourceInventory.unsafePaths, planFindings, finding);
    const artifactMappings = selectedWorkflowLevel === null
        || alreadyCanonical
        || !governedSliceIdentityValid
        ? []
        : buildArtifactMappings(planningRootPath, unit, workId, selectedWorkflowLevel, sourceRootPath, sourcePaths, ordering);
    const unsupportedContent = (0, work_unit_1.findUnsupportedWorkUnitContent)(sourcePaths, artifactMappings, selectedWorkflowLevel);
    if (!alreadyCanonical && selectedWorkflowLevel !== null && unsupportedContent.length > 0) {
        planFindings.push(finding("UNSUPPORTED_CONTENT", unsupportedContent.flatMap((entry) => entry.paths), `Work unit '${unit.workUnitId}' contains source content that cannot be preserved at the selected level.`, "Select a level that can preserve the complete source hierarchy or migrate the unsupported content separately."));
    }
    const specifications = readSpecifications(repositoryRoot, sourcePaths);
    const unsafePaths = [...sourceInventory.unsafePaths];
    for (const sourcePath of sourcePaths) {
        if (!isSafeSourceFile(repositoryRoot, sourcePath))
            unsafePaths.push(sourcePath);
    }
    const completeMappings = alreadyCanonical
        ? []
        : [...artifactMappings];
    for (const mapping of completeMappings) {
        try {
            const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, mapping.destinationPath);
            if (target.exists) {
                planFindings.push(finding("DESTINATION_COLLISION", [mapping.destinationPath], `Migration destination '${mapping.destinationPath}' already exists.`, "Inspect the active destination and refresh the plan after resolving the collision."));
            }
        }
        catch {
            unsafePaths.push(mapping.destinationPath);
        }
    }
    if (unsafePaths.length > 0) {
        planFindings.push(finding("UNSAFE_PATH", unsafePaths, `Work unit '${unit.workUnitId}' contains an unsafe, missing, non-file, or escaping path.`, "Repair the path or symlink boundary and refresh the migration plan."));
    }
    const taskMappings = unit.taskPairs.map((pair) => taskMappingFor(pair, artifactMappings, ordering)).filter((entry) => entry !== null);
    const expected = (0, work_unit_1.buildWorkUnitExpectedConditions)({
        alreadyCanonical,
        sourcePaths,
        sharedEvidenceSourcePath: sharedEvidence?.sourcePath ?? null,
        mappings: completeMappings
    }, (sourcePath) => safeRevision(repositoryRoot, sourcePath), uniqueExpected);
    const relationshipChanges = selectedWorkflowLevel === null
        ? []
        : buildRelationshipChanges(taskMappings, selectedWorkflowLevel, workId, unit.sliceId);
    const unitBlocked = (0, work_unit_1.workUnitIsBlocked)({
        selectedWorkflowLevel,
        orderingPresent: ordering !== null,
        structuredTaskSequenceMissing: unit.taskPairs.some((pair) => ordering?.taskSequences[requireTaskId(pair, unit.workUnitId)] === undefined),
        duplicateActiveIdentity: unit.ambiguityReasons.some((reason) => reason.code === "duplicate-active-identity"),
        governedSliceIdentityValid,
        sourceInventoryUnsafe: sourceInventory.unsafePaths.length > 0,
        governedSharedEvidenceMissing: selectedWorkflowLevel === "governed"
            && unit.layoutKind === "former-governed-container-slice"
            && sharedEvidence === null,
        unsupportedContentPresent: unsupportedContent.length > 0,
        unsafePathsPresent: unsafePaths.length > 0
    });
    return {
        workUnitId: unit.workUnitId,
        actionable: !alreadyCanonical && !unitBlocked,
        proposedWorkflowLevel: unit.proposedWorkflowLevel,
        selectedWorkflowLevel,
        selectionSource,
        workId,
        layoutKind: unit.layoutKind,
        sliceId: unit.sliceId,
        formerContainerIdentity: unit.formerContainerIdentity,
        alreadyCanonical,
        sourceRootPath,
        destinationRootPath,
        implementationOrder: ordering?.implementationOrder ?? null,
        implementationOrderSource: ordering?.implementationOrderSource ?? null,
        orderedRootName: ordering?.orderedRootName ?? null,
        sourcePaths,
        destinationPaths: uniqueSorted(completeMappings.map((mapping) => mapping.destinationPath)),
        artifactMappings: completeMappings.sort(compareMappings),
        taskMappings,
        specifications,
        compatibilityEvidence: unit.compatibilityEvidence,
        evidenceGaps: unit.evidenceGaps,
        ambiguityReasons: unit.ambiguityReasons,
        relationshipChanges,
        sharedEvidence,
        expected,
        unsupportedContent,
        preservation: {
            authoredBodies: "preserved",
            safeUnknownFrontMatter: "preserved",
            taskIdentity: "preserved",
            evidenceHistory: "preserved",
            specifications: "referenced-in-place",
            missingAuthorityOrEvidence: "not-fabricated",
            sourceRetirement: "only-after-verified-staging"
        },
        recoveryGuidance: unitBlocked
            ? "Resolve every work-unit blocker and refresh the no-write preview."
            : "Apply only with this exact plan digest; any changed precondition requires a refreshed preview."
    };
}
function buildArtifactMappings(planningRootPath, unit, workId, level, sourceRoot, sourcePaths, ordering) {
    const result = [];
    if (level === "basic") {
        for (const pair of unit.taskPairs) {
            const taskId = requireTaskId(pair, unit.workUnitId);
            if (ordering === null)
                continue;
            const root = joinRepositoryPath(planningRootPath, "basic/tasks", (0, ordered_root_identity_1.composeOrderedDirectoryName)("basic-root", taskId, ordering.implementationOrder));
            result.push(mapping(pair.taskDefinitionPath, joinRepositoryPath(root, "task-definition.md"), "task-definition"), mapping(pair.implementationSummaryPath, joinRepositoryPath(root, "implementation-summary.md"), "implementation-summary"));
        }
        return result;
    }
    const destinationRoot = level === "governed"
        ? joinRepositoryPath(planningRootPath, "governed", requireGovernedSliceId(unit))
        : ordering === null
            ? joinRepositoryPath(planningRootPath, level, workId)
            : joinRepositoryPath(planningRootPath, level, ordering.orderedRootName);
    const overviewSource = [
        joinRepositoryPath(sourceRoot, "slice-overview.md"),
        joinRepositoryPath(sourceRoot, "overview.md")
    ].find((candidate) => sourcePaths.includes(candidate)) ?? null;
    if (level === "structured") {
        result.push({
            sourcePath: overviewSource,
            destinationPath: joinRepositoryPath(destinationRoot, "overview.md"),
            role: "work-overview"
        });
    }
    else if (overviewSource !== null) {
        result.push({
            sourcePath: overviewSource,
            destinationPath: joinRepositoryPath(destinationRoot, "slice-overview.md"),
            role: "planning-artifact"
        });
    }
    for (const pair of unit.taskPairs) {
        const taskId = requireTaskId(pair, unit.workUnitId);
        if (level === "structured") {
            const sequence = ordering?.taskSequences[taskId];
            if (sequence === undefined)
                continue;
            const root = joinRepositoryPath(destinationRoot, "tasks", (0, ordered_root_identity_1.composeOrderedDirectoryName)("structured-task", taskId, sequence.sequence));
            result.push(mapping(pair.taskDefinitionPath, joinRepositoryPath(root, "task-definition.md"), "task-definition"), mapping(pair.implementationSummaryPath, joinRepositoryPath(root, "implementation-summary.md"), "implementation-summary"));
            continue;
        }
        const relativeTaskPath = canonicalGovernedRelativePath(relativeFromRoot(pair.taskDefinitionPath, sourceRoot));
        const relativeSummaryPath = canonicalGovernedRelativePath(relativeFromRoot(pair.implementationSummaryPath, sourceRoot));
        result.push(mapping(pair.taskDefinitionPath, joinRepositoryPath(destinationRoot, relativeTaskPath), "task-definition"), mapping(pair.implementationSummaryPath, joinRepositoryPath(destinationRoot, relativeSummaryPath), "implementation-summary"));
    }
    if (level === "structured") {
        for (const sourcePath of sourcePaths) {
            if (result.some((entry) => entry.sourcePath === sourcePath))
                continue;
            result.push(mapping(sourcePath, joinRepositoryPath(destinationRoot, "migration-residuals", relativeFromRoot(sourcePath, sourceRoot)), "planning-artifact"));
        }
    }
    if (level === "governed") {
        for (const sourcePath of sourcePaths) {
            if (result.some((entry) => entry.sourcePath === sourcePath))
                continue;
            result.push(mapping(sourcePath, joinRepositoryPath(destinationRoot, canonicalGovernedRelativePath(relativeFromRoot(sourcePath, sourceRoot))), "planning-artifact"));
        }
    }
    return result;
}
function taskMappingFor(pair, artifactMappings, ordering) {
    const taskDefinition = artifactMappings.find((entry) => entry.sourcePath === pair.taskDefinitionPath);
    const implementationSummary = artifactMappings.find((entry) => entry.sourcePath === pair.implementationSummaryPath);
    if (taskDefinition === undefined || implementationSummary === undefined || pair.taskId === null) {
        return null;
    }
    const sequence = ordering?.taskSequences[pair.taskId] ?? null;
    return {
        taskId: pair.taskId,
        sourceTaskDefinitionPath: pair.taskDefinitionPath,
        sourceImplementationSummaryPath: pair.implementationSummaryPath,
        destinationTaskDefinitionPath: taskDefinition.destinationPath,
        destinationImplementationSummaryPath: implementationSummary.destinationPath,
        sequence: sequence?.sequence ?? null,
        sequencePrefix: sequence === null ? null : String(sequence.sequence).padStart(2, "0"),
        sequenceSource: sequence?.source ?? null,
        lifecycle: pair.lifecycle,
        compatibilityEvidence: pair.compatibilityEvidence,
        evidenceGaps: pair.evidenceGaps
    };
}
function buildRelationshipChanges(mappings, level, workId, sliceId) {
    return mappings.flatMap((entry) => {
        const workOverview = level === "structured"
            ? `${entry.destinationTaskDefinitionPath.split("/tasks/")[0]}/overview.md`
            : `.day-shift/planning/${level}/${workId}/overview.md`;
        const common = [
            {
                path: entry.destinationTaskDefinitionPath,
                field: "workflow_level",
                before: null,
                after: level
            },
            {
                path: entry.destinationImplementationSummaryPath,
                field: "workflow_level",
                before: null,
                after: level
            },
            {
                path: entry.destinationImplementationSummaryPath,
                field: "upstream.task_definition",
                before: entry.sourceTaskDefinitionPath,
                after: entry.destinationTaskDefinitionPath
            }
        ];
        if (level === "structured") {
            return [
                ...common,
                {
                    path: entry.destinationTaskDefinitionPath,
                    field: "upstream.work_overview",
                    before: null,
                    after: workOverview
                },
                {
                    path: entry.destinationImplementationSummaryPath,
                    field: "upstream.work_overview",
                    before: null,
                    after: workOverview
                }
            ];
        }
        if (level !== "governed")
            return common;
        if (sliceId === null)
            return common;
        const taskPrefix = entry.destinationTaskDefinitionPath.split("/tasks/")[0];
        const milestoneOverview = joinRepositoryPath(taskPrefix, "milestone-overview.md");
        const phaseRoot = taskPrefix.split("/milestones/")[0];
        const phaseOverview = joinRepositoryPath(phaseRoot, "phase-overview.md");
        const sliceRoot = phaseRoot.split("/phases/")[0];
        const sliceOverview = joinRepositoryPath(sliceRoot, "slice-overview.md");
        return [
            ...common,
            ...[entry.destinationTaskDefinitionPath, entry.destinationImplementationSummaryPath]
                .flatMap((artifactPath) => [
                {
                    path: artifactPath,
                    field: "upstream.slice_overview",
                    before: null,
                    after: sliceOverview
                },
                {
                    path: artifactPath,
                    field: "upstream.phase_overview",
                    before: null,
                    after: phaseOverview
                },
                {
                    path: artifactPath,
                    field: "upstream.milestone_overview",
                    before: null,
                    after: milestoneOverview
                }
            ])
        ];
    }).sort((left, right) => `${left.path}\0${left.field}`.localeCompare(`${right.path}\0${right.field}`));
}
function inventoryWorkUnitSources(repositoryRoot, planningRootPath, unit) {
    const sourceRootPath = sourceRootForUnit(planningRootPath, unit);
    const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, sourceRootPath);
    const sourcePaths = [];
    const unsafePaths = [];
    if (!target.exists) {
        return {
            sourceRootPath,
            sourcePaths: uniqueSorted(unit.sourcePaths),
            unsafePaths: [sourceRootPath]
        };
    }
    const rootStats = node_fs_1.default.lstatSync(target.runtimePath);
    if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
        return {
            sourceRootPath,
            sourcePaths: uniqueSorted(unit.sourcePaths),
            unsafePaths: [sourceRootPath]
        };
    }
    const visit = (absoluteDirectory) => {
        for (const entry of node_fs_1.default.readdirSync(absoluteDirectory, { withFileTypes: true })
            .sort((left, right) => left.name.localeCompare(right.name))) {
            const absolutePath = node_path_1.default.join(absoluteDirectory, entry.name);
            const relativePath = joinRepositoryPath(sourceRootPath, node_path_1.default.relative(target.runtimePath, absolutePath).split(node_path_1.default.sep).join("/"));
            const stats = node_fs_1.default.lstatSync(absolutePath);
            if (stats.isSymbolicLink()) {
                unsafePaths.push(relativePath);
                continue;
            }
            if (stats.isDirectory()) {
                visit(absolutePath);
                continue;
            }
            if (!stats.isFile()) {
                unsafePaths.push(relativePath);
                continue;
            }
            sourcePaths.push(relativePath);
            if (sourcePaths.length > SOURCE_INVENTORY_FILE_LIMIT) {
                unsafePaths.push(sourceRootPath);
                return;
            }
        }
    };
    visit(target.runtimePath);
    return {
        sourceRootPath,
        sourcePaths: uniqueSorted([...sourcePaths, ...unit.sourcePaths]),
        unsafePaths: uniqueSorted(unsafePaths)
    };
}
function sourceRootForUnit(planningRootPath, unit) {
    const first = unit.sourcePaths[0] ?? planningRootPath;
    if (unit.layoutKind === "basic-task-root") {
        const taskRoot = first.match(new RegExp(`^${escapeRegExp(planningRootPath)}/(?:basic/)?tasks/[^/]+`));
        if (taskRoot !== null)
            return taskRoot[0];
        const legacyTaskRoot = first.match(new RegExp(`^${escapeRegExp(planningRootPath)}/[^/]+/tasks/[^/]+`));
        if (legacyTaskRoot !== null)
            return legacyTaskRoot[0];
    }
    if (unit.layoutKind === "structured-work-root") {
        const structuredRoot = first.match(new RegExp(`^${escapeRegExp(planningRootPath)}/structured/[^/]+`));
        if (structuredRoot !== null)
            return structuredRoot[0];
    }
    const formerMatch = first.match(new RegExp(`^${escapeRegExp(planningRootPath)}/governed/[^/]+/slices/[^/]+`));
    if (formerMatch !== null)
        return formerMatch[0];
    const canonicalMatch = first.match(new RegExp(`^${escapeRegExp(planningRootPath)}/governed/[^/]+`));
    if (canonicalMatch !== null)
        return canonicalMatch[0];
    const relative = node_path_1.default.posix.relative(planningRootPath, first);
    const firstSegment = relative.split("/")[0];
    return firstSegment === "" || firstSegment === "." || firstSegment === ".."
        ? planningRootPath
        : joinRepositoryPath(planningRootPath, firstSegment);
}
function planSharedEvidence(repositoryRoot, planningRootPath, formerContainerIdentity, sliceId, planFindings) {
    const containerRoot = joinRepositoryPath(planningRootPath, "governed", formerContainerIdentity);
    const sourcePath = joinRepositoryPath(containerRoot, "overview.md");
    const archivePath = joinRepositoryPath(".day-shift/migrations/phase-08/shared-governed-overviews", formerContainerIdentity, "overview.md");
    if (!isSafeSourceFile(repositoryRoot, sourcePath)) {
        planFindings.push(finding("AMBIGUOUS_SHARED_EVIDENCE", [sourcePath], `Former Governed container '${formerContainerIdentity}' has no safe regular shared overview.`, "Restore or explicitly disposition the shared overview before migrating any child slice."));
        return null;
    }
    const sourceTarget = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, sourcePath);
    const markdownText = node_fs_1.default.readFileSync(sourceTarget.runtimePath, "utf8");
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(markdownText);
    const artifactType = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "artifact_type");
    const declaredId = (0, planning_document_utils_1.readTopLevelField)(document.frontMatterLines, "id");
    const recognizedGeneratedOverview = artifactType === "work_overview"
        && declaredId === formerContainerIdentity
        && /^# Migrated Governed Work:/m.test(document.bodyText)
        && /^## Migration Evidence$/m.test(document.bodyText);
    if (!recognizedGeneratedOverview) {
        planFindings.push(finding("AMBIGUOUS_SHARED_EVIDENCE", [sourcePath], `Former Governed container '${formerContainerIdentity}' contains shared authored evidence whose per-slice applicability is not deterministic.`, "Record an explicit per-slice shared-evidence disposition before refreshing the migration plan."));
        return null;
    }
    const slicesRoot = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, joinRepositoryPath(containerRoot, "slices"));
    if (!slicesRoot.exists || !node_fs_1.default.statSync(slicesRoot.runtimePath).isDirectory()) {
        planFindings.push(finding("AMBIGUOUS_SHARED_EVIDENCE", [joinRepositoryPath(containerRoot, "slices")], `Former Governed container '${formerContainerIdentity}' has no complete slices directory.`, "Restore the complete child-slice inventory before refreshing the migration plan."));
        return null;
    }
    const applicableSliceIds = [];
    for (const entry of node_fs_1.default.readdirSync(slicesRoot.runtimePath, { withFileTypes: true })
        .sort((left, right) => left.name.localeCompare(right.name))) {
        const childPath = joinRepositoryPath(containerRoot, "slices", entry.name);
        if (!entry.isDirectory() || entry.isSymbolicLink() || !ORDERED_SLICE_ID_PATTERN.test(entry.name)) {
            planFindings.push(finding("AMBIGUOUS_SHARED_EVIDENCE", [childPath], `Former Governed container '${formerContainerIdentity}' contains a non-canonical child entry.`, "Repair the child identity or unsafe entry before refreshing the migration plan."));
            return null;
        }
        applicableSliceIds.push(entry.name);
    }
    if (!applicableSliceIds.includes(sliceId)) {
        planFindings.push(finding("AMBIGUOUS_SHARED_EVIDENCE", [sourcePath], `Shared evidence for '${formerContainerIdentity}' does not enumerate slice '${sliceId}'.`, "Refresh compatibility discovery and the shared-evidence disposition together."));
        return null;
    }
    const archiveTarget = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, archivePath);
    if (archiveTarget.exists) {
        planFindings.push(finding("DESTINATION_COLLISION", [archivePath], `Shared-evidence archive '${archivePath}' already exists.`, "Inspect the prior archive and durable migration record before retrying."));
        return null;
    }
    return {
        sourcePath,
        archivePath,
        formerContainerIdentity,
        disposition: "archive-and-reference",
        applicableSliceIds,
        contentRevision: (0, mutation_preconditions_1.computeContentRevisionSync)(sourceTarget.runtimePath),
        authoredBodyDigest: (0, node_crypto_1.createHash)("sha256").update(document.bodyText).digest("hex"),
        recognizedGeneratedOverview: true
    };
}
function uniqueSharedEvidenceArchives(workUnits) {
    const bySource = new Map();
    for (const unit of workUnits) {
        if (!unit.actionable || unit.sharedEvidence === null)
            continue;
        bySource.set(unit.sharedEvidence.sourcePath, unit.sharedEvidence);
    }
    return [...bySource.values()].sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
}
function enforceFormerContainerAtomicity(workUnits, findings) {
    const groups = new Map();
    for (const unit of workUnits) {
        if (unit.formerContainerIdentity === null)
            continue;
        const group = groups.get(unit.formerContainerIdentity) ?? [];
        group.push(unit);
        groups.set(unit.formerContainerIdentity, group);
    }
    for (const [container, group] of groups) {
        const shared = group.map((unit) => unit.sharedEvidence)
            .filter((entry) => entry !== null);
        const discoveredSliceIds = uniqueSorted(group.flatMap((unit) => unit.sliceId === null ? [] : [unit.sliceId]));
        const expectedSliceIds = uniqueSorted(shared.flatMap((entry) => entry.applicableSliceIds));
        if (group.some((unit) => !unit.actionable)
            || shared.length !== group.length
            || stableJson(discoveredSliceIds) !== stableJson(expectedSliceIds)) {
            findings.push(finding("AMBIGUOUS_SHARED_EVIDENCE", uniqueSorted(group.flatMap((unit) => unit.sourcePaths)), `Former Governed container '${container}' cannot be retired atomically because its child or shared-evidence inventory is incomplete.`, "Resolve every sibling slice and shared-evidence blocker, then refresh one complete plan for the whole container."));
        }
    }
}
function buildPathReplacements(planningRootPath, workUnits, sharedEvidenceArchives) {
    const replacements = [];
    for (const unit of workUnits) {
        if (!unit.actionable || unit.destinationRootPath === null)
            continue;
        for (const task of unit.taskMappings) {
            const sourceTaskDirectory = node_path_1.default.posix.dirname(task.sourceTaskDefinitionPath);
            const destinationTaskDirectory = node_path_1.default.posix.dirname(task.destinationTaskDefinitionPath);
            if (sourceTaskDirectory === destinationTaskDirectory)
                continue;
            replacements.push({
                sourcePrefix: sourceTaskDirectory,
                destinationPrefix: destinationTaskDirectory,
                kind: "task-directory"
            });
        }
        replacements.push({
            sourcePrefix: unit.sourceRootPath,
            destinationPrefix: unit.destinationRootPath,
            kind: "active-root"
        });
        if (unit.sliceId !== null) {
            const legacyAlias = joinRepositoryPath(planningRootPath, unit.sliceId);
            if (legacyAlias !== unit.sourceRootPath) {
                replacements.push({
                    sourcePrefix: legacyAlias,
                    destinationPrefix: unit.destinationRootPath,
                    kind: "legacy-numbered-alias"
                });
            }
        }
    }
    for (const shared of sharedEvidenceArchives) {
        replacements.push({
            sourcePrefix: shared.sourcePath,
            destinationPrefix: shared.archivePath,
            kind: "shared-evidence"
        });
    }
    const bySource = new Map();
    for (const replacement of replacements) {
        bySource.set(replacement.sourcePrefix, replacement);
    }
    return [...bySource.values()].sort((left, right) => right.sourcePrefix.length - left.sourcePrefix.length
        || left.sourcePrefix.localeCompare(right.sourcePrefix));
}
function buildRepositoryUpdates(repositoryRoot, replacements, retirementPaths, findings) {
    if (replacements.length === 0)
        return [];
    const dayShiftRoot = node_path_1.default.join(repositoryRoot, ".day-shift");
    const updates = [];
    const visit = (directory) => {
        for (const entry of node_fs_1.default.readdirSync(directory, { withFileTypes: true })
            .sort((left, right) => left.name.localeCompare(right.name))) {
            const absolutePath = node_path_1.default.join(directory, entry.name);
            const repositoryPath = node_path_1.default.relative(repositoryRoot, absolutePath)
                .split(node_path_1.default.sep).join("/");
            if (repositoryPath === ".day-shift/state/write.lock"
                || repositoryPath.startsWith(".day-shift/state/transactions/")
                || repositoryPath === ".day-shift/runs"
                || repositoryPath.startsWith(".day-shift/runs/")
                || repositoryPath === ".day-shift/rehearsals"
                || repositoryPath.startsWith(".day-shift/rehearsals/")) {
                continue;
            }
            const stats = node_fs_1.default.lstatSync(absolutePath);
            if (stats.isSymbolicLink()) {
                findings.push(finding("UNSAFE_PATH", [repositoryPath], "Relationship-bearing .day-shift traversal encountered a symlink.", "Replace the symlink with regular repository-owned content before migration."));
                continue;
            }
            if (stats.isDirectory()) {
                visit(absolutePath);
                continue;
            }
            if (!stats.isFile() || retirementPaths.has(repositoryPath))
                continue;
            const bytes = node_fs_1.default.readFileSync(absolutePath);
            if (bytes.includes(0))
                continue;
            const before = bytes.toString("utf8");
            const rewritten = rewriteKnownRepositoryPaths(before, replacements, {
                sourceArtifactPath: repositoryPath,
                destinationArtifactPath: repositoryPath
            });
            if (rewritten.text === before)
                continue;
            const recordedReadinessRevision = before.match(/^readiness_review_revision:\s*["']?([a-f0-9]{64})["']?\s*$/mu)?.[1] ?? null;
            const readinessWasApplicable = repositoryPath.endsWith("/task-definition.md")
                && (0, task_lifecycle_1.isTaskReadinessAuthorizationRevisionApplicable)(before, recordedReadinessRevision);
            const rebound = readinessWasApplicable
                ? (0, task_lifecycle_1.rebindTaskReadinessAuthorizationRevision)(rewritten.text)
                : { text: rewritten.text, changed: false };
            updates.push({
                path: repositoryPath,
                beforeRevision: (0, mutation_preconditions_1.computeContentRevisionSync)(absolutePath),
                afterRevision: (0, node_crypto_1.createHash)("sha256").update(rebound.text).digest("hex"),
                replacementCount: rewritten.replacementCount,
                readinessRevisionRebound: rebound.changed
            });
        }
    };
    visit(dayShiftRoot);
    return updates.sort((left, right) => left.path.localeCompare(right.path));
}
function rewriteKnownRepositoryPaths(input, replacements, context) {
    let text = input;
    let replacementCount = 0;
    for (const replacement of replacements) {
        let cursor = 0;
        let next = "";
        while (cursor < text.length) {
            const index = text.indexOf(replacement.sourcePrefix, cursor);
            if (index < 0) {
                next += text.slice(cursor);
                break;
            }
            const before = index === 0 ? "" : text[index - 1];
            const afterIndex = index + replacement.sourcePrefix.length;
            const after = afterIndex >= text.length ? "" : text[afterIndex];
            const beforeBoundary = before === "" || !/[A-Za-z0-9._-]/.test(before);
            const afterBoundary = after === "" || /[/"'`)\]}\s:#?]/.test(after);
            next += text.slice(cursor, index);
            if (beforeBoundary && afterBoundary) {
                next += replacement.destinationPrefix;
                replacementCount += 1;
            }
            else {
                next += replacement.sourcePrefix;
            }
            cursor = afterIndex;
        }
        text = next;
    }
    if (context !== undefined) {
        const relative = rewriteRelativeMarkdownLinkPaths(text, replacements, context);
        text = relative.text;
        replacementCount += relative.replacementCount;
    }
    return { text, replacementCount };
}
function rewriteRelativeMarkdownLinkPaths(input, replacements, context) {
    const sourceDirectory = node_path_1.default.posix.dirname(context.sourceArtifactPath);
    const destinationDirectory = node_path_1.default.posix.dirname(context.destinationArtifactPath);
    let activeFence = null;
    let replacementCount = 0;
    const segments = input.split(/(?<=\n)/u);
    const rewritten = segments.map((segment) => {
        const newline = segment.match(/\r?\n$/u)?.[0] ?? "";
        const line = newline === "" ? segment : segment.slice(0, -newline.length);
        const fence = line.match(/^\s*(`{3,}|~{3,})/u)?.[1] ?? null;
        if (fence !== null) {
            const marker = fence[0];
            if (activeFence === null) {
                activeFence = { marker, length: fence.length };
            }
            else if (activeFence.marker === marker && fence.length >= activeFence.length) {
                activeFence = null;
            }
            return segment;
        }
        if (activeFence !== null)
            return segment;
        const codeRanges = [...line.matchAll(/(`+).*?\1/gu)].map((match) => ({
            start: match.index,
            end: match.index + match[0].length
        }));
        const linkPattern = /\]\(\s*(?:<)?([^\s)>]+)(?:>)?/gu;
        let cursor = 0;
        let result = "";
        for (const match of line.matchAll(linkPattern)) {
            const destination = match[1];
            const destinationOffset = match[0].indexOf(destination);
            const destinationStart = match.index + destinationOffset;
            const destinationEnd = destinationStart + destination.length;
            if (codeRanges.some((range) => match.index >= range.start && match.index < range.end)) {
                continue;
            }
            const mapped = rewriteRelativeLinkDestination(destination, sourceDirectory, destinationDirectory, replacements);
            if (mapped === destination)
                continue;
            result += line.slice(cursor, destinationStart);
            result += mapped;
            cursor = destinationEnd;
            replacementCount += 1;
        }
        return `${result}${line.slice(cursor)}${newline}`;
    }).join("");
    return { text: rewritten, replacementCount };
}
function rewriteRelativeLinkDestination(destination, sourceDirectory, destinationDirectory, replacements) {
    const suffixIndex = [destination.indexOf("?"), destination.indexOf("#")]
        .filter((index) => index >= 0)
        .reduce((lowest, index) => Math.min(lowest, index), destination.length);
    const linkPath = destination.slice(0, suffixIndex);
    const suffix = destination.slice(suffixIndex);
    if (linkPath === ""
        || linkPath.startsWith("/")
        || linkPath.startsWith("\\")
        || linkPath.startsWith(".day-shift/")
        || linkPath.includes("\\")
        || /^[A-Za-z][A-Za-z0-9+.-]*:/u.test(linkPath)) {
        return destination;
    }
    if (replacements.some((replacement) => linkPath === replacement.sourcePrefix
        || linkPath.startsWith(`${replacement.sourcePrefix}/`)
        || linkPath === replacement.destinationPrefix
        || linkPath.startsWith(`${replacement.destinationPrefix}/`))) {
        return destination;
    }
    const resolvedSourcePath = node_path_1.default.posix.normalize(node_path_1.default.posix.join(sourceDirectory, linkPath));
    if (resolvedSourcePath === ".." || resolvedSourcePath.startsWith("../")) {
        return destination;
    }
    const replacement = [...replacements]
        .sort((left, right) => right.sourcePrefix.length - left.sourcePrefix.length)
        .find((candidate) => resolvedSourcePath === candidate.sourcePrefix
        || resolvedSourcePath.startsWith(`${candidate.sourcePrefix}/`));
    if (replacement === undefined)
        return destination;
    const mappedPath = `${replacement.destinationPrefix}${resolvedSourcePath.slice(replacement.sourcePrefix.length)}`;
    let relativePath = node_path_1.default.posix.relative(destinationDirectory, mappedPath);
    if (relativePath === "")
        relativePath = node_path_1.default.posix.basename(mappedPath);
    if (linkPath.startsWith("./") && !relativePath.startsWith(".")) {
        relativePath = `./${relativePath}`;
    }
    return `${relativePath}${suffix}`;
}
function readSpecifications(repositoryRoot, sourcePaths) {
    const byRef = new Map();
    const canonicalRefs = readCanonicalSpecificationRefs(repositoryRoot);
    for (const sourcePath of sourcePaths) {
        const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(sourcePath));
        if (!node_fs_1.default.statSync(absolutePath, { throwIfNoEntry: false })?.isFile())
            continue;
        const markdownText = node_fs_1.default.readFileSync(absolutePath, "utf8");
        const references = readSpecificationReferences(markdownText);
        for (const reference of references) {
            const sourceRef = reference.ref;
            const ref = canonicalRefs.get(reference.id) ?? sourceRef;
            const key = `${reference.id}\0${sourceRef}`;
            if (sourceRef === "" || byRef.has(key))
                continue;
            const specificationPath = safeRepositoryPath(ref);
            const specificationAbsolute = specificationPath === null
                ? null
                : (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(specificationPath));
            byRef.set(key, {
                id: reference.id,
                ref,
                sourceRef,
                revision: specificationAbsolute !== null
                    && node_fs_1.default.statSync(specificationAbsolute, { throwIfNoEntry: false })?.isFile()
                    ? (0, mutation_preconditions_1.computeContentRevisionSync)(specificationAbsolute)
                    : null
            });
        }
    }
    return [...byRef.values()].sort((left, right) => left.ref.localeCompare(right.ref));
}
function readSpecificationReferences(markdownText) {
    const references = [];
    for (const match of markdownText.matchAll(/^\s{4}-\s+id:\s*["']?([^"'\r\n]+)["']?\s*$([\s\S]*?)(?=^\s{4}-\s+id:|^---\s*$)/gm)) {
        const ref = match[2].match(/^\s{6}ref:\s*["']?([^"'\r\n]+)["']?\s*$/m);
        if (ref === null)
            continue;
        references.push({ id: match[1].trim(), ref: ref[1].trim() });
    }
    return references;
}
function readCanonicalSpecificationRefs(repositoryRoot) {
    const indexPath = node_path_1.default.join(repositoryRoot, ".day-shift", "specs", "index.json");
    if (!node_fs_1.default.statSync(indexPath, { throwIfNoEntry: false })?.isFile())
        return new Map();
    try {
        const parsed = JSON.parse(node_fs_1.default.readFileSync(indexPath, "utf8"));
        return new Map((parsed.specs ?? []).flatMap((entry) => typeof entry.id === "string" && typeof entry.spec_path === "string"
            ? [[entry.id, entry.spec_path]]
            : []));
    }
    catch {
        return new Map();
    }
}
function canonicalWorkflowLevelFor(unit, planningRootPath, sourceRootPath) {
    if (unit.layoutKind === "basic-task-root"
        && sourceRootPath.startsWith(`${planningRootPath}/basic/`)) {
        try {
            return (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)("basic-root", node_path_1.default.posix.basename(sourceRootPath)).shape
                === "canonical" ? "basic" : null;
        }
        catch {
            return null;
        }
    }
    if (unit.layoutKind === "structured-work-root"
        && sourceRootPath.startsWith(`${planningRootPath}/structured/`)) {
        try {
            return (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)("structured-root", node_path_1.default.posix.basename(sourceRootPath)).shape === "canonical" ? "structured" : null;
        }
        catch {
            return null;
        }
    }
    if (unit.layoutKind === "canonical-governed-numbered-root")
        return "governed";
    return null;
}
function physicalWorkflowLevelFor(unit, planningRootPath, sourceRootPath) {
    if (unit.layoutKind === "basic-task-root"
        && sourceRootPath.startsWith(`${planningRootPath}/basic/`))
        return "basic";
    if (unit.layoutKind === "structured-work-root"
        && sourceRootPath.startsWith(`${planningRootPath}/structured/`))
        return "structured";
    if (unit.layoutKind === "canonical-governed-numbered-root")
        return "governed";
    return null;
}
function deriveWorkId(repositoryRoot, planningRootPath, unit, knownSourceRootPath) {
    const explicit = unit.sourcePaths.flatMap((sourcePath) => {
        try {
            const text = node_fs_1.default.readFileSync((0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(sourcePath)), "utf8");
            const lines = (0, planning_document_utils_1.parseMarkdownFrontMatter)(text).frontMatterLines;
            return [(0, planning_document_utils_1.readTopLevelField)(lines, "work_id"), (0, planning_document_utils_1.readTopLevelField)(lines, "id")]
                .filter((value) => value !== undefined && validId(value));
        }
        catch {
            return [];
        }
    })[0];
    const overviewExplicit = unit.sourcePaths.flatMap((sourcePath) => sourcePath.endsWith("/overview.md")
        ? (() => {
            try {
                const text = node_fs_1.default.readFileSync((0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(sourcePath)), "utf8");
                const value = (0, planning_document_utils_1.readTopLevelField)((0, planning_document_utils_1.parseMarkdownFrontMatter)(text).frontMatterLines, "id");
                return value !== undefined && validId(value) ? [value] : [];
            }
            catch {
                return [];
            }
        })()
        : [])[0];
    if (overviewExplicit !== undefined)
        return overviewExplicit;
    if (unit.layoutKind === "basic-task-root" && explicit !== undefined)
        return explicit;
    const sourceRoot = knownSourceRootPath ?? commonLegacyRoot(planningRootPath, unit.sourcePaths);
    const rootSlug = node_path_1.default.posix.basename(sourceRoot).replace(/^\d+-/, "");
    if (validId(rootSlug))
        return rootSlug;
    if (explicit !== undefined)
        return explicit;
    return `migrated-${(0, node_crypto_1.createHash)("sha256").update(unit.workUnitId).digest("hex").slice(0, 12)}`;
}
function commonLegacyRoot(planningRootPath, sourcePaths) {
    const first = sourcePaths[0] ?? planningRootPath;
    const relative = node_path_1.default.posix.relative(planningRootPath, first);
    const firstSegment = relative.split("/")[0];
    return firstSegment === "" || firstSegment === "." || firstSegment === ".."
        ? planningRootPath
        : joinRepositoryPath(planningRootPath, firstSegment);
}
function isSafeSourceFile(repositoryRoot, repositoryPath) {
    try {
        const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, repositoryPath);
        const stats = node_fs_1.default.lstatSync(target.runtimePath, { throwIfNoEntry: false });
        return target.exists && stats?.isFile() === true && !stats.isSymbolicLink();
    }
    catch {
        return false;
    }
}
function safeRevision(repositoryRoot, repositoryPath) {
    try {
        const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, (0, paths_1.parseRepositoryRelativePath)(repositoryPath));
        return node_fs_1.default.statSync(absolutePath, { throwIfNoEntry: false })?.isFile()
            ? (0, mutation_preconditions_1.computeContentRevisionSync)(absolutePath)
            : null;
    }
    catch {
        return null;
    }
}
function collectMetadataInsertions(workUnits) {
    return workUnits.flatMap((unit) => {
        if (!unit.actionable || unit.selectedWorkflowLevel === null)
            return [];
        const insertions = [];
        if (unit.implementationOrder !== null) {
            const rootArtifacts = unit.selectedWorkflowLevel === "structured"
                ? unit.artifactMappings.filter((entry) => entry.role === "work-overview")
                : unit.artifactMappings.filter((entry) => entry.role === "task-definition" || entry.role === "implementation-summary");
            for (const rootArtifact of rootArtifacts) {
                insertions.push({
                    path: rootArtifact.destinationPath,
                    field: "implementation_order",
                    value: unit.implementationOrder,
                    provenance: "numbered-root-migration"
                });
            }
        }
        for (const task of unit.taskMappings) {
            if (task.sequence === null)
                continue;
            insertions.push({
                path: task.destinationTaskDefinitionPath,
                field: "sequence",
                value: String(task.sequence),
                provenance: "numbered-root-migration"
            });
        }
        return insertions;
    }).sort((left, right) => `${left.path}\0${left.field}`.localeCompare(`${right.path}\0${right.field}`));
}
function collectRevisionRebindings(repositoryRoot, workUnits, repositoryUpdates) {
    const mapped = workUnits.flatMap((unit) => unit.actionable
        ? unit.taskMappings.map((task) => ({
            taskId: task.taskId,
            path: task.destinationTaskDefinitionPath,
            field: "readiness_review_revision",
            reason: "path-and-ordering-only",
            provenance: "workspace-numbered-root-migration"
        }))
        : []);
    const repositoryRebindings = repositoryUpdates.flatMap((update) => {
        if (!update.readinessRevisionRebound)
            return [];
        try {
            const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, update.path);
            const lines = (0, planning_document_utils_1.parseMarkdownFrontMatter)(node_fs_1.default.readFileSync(target.runtimePath, "utf8"))
                .frontMatterLines;
            const taskId = (0, planning_document_utils_1.readTopLevelField)(lines, "id") ?? (0, planning_document_utils_1.readTopLevelField)(lines, "task");
            return taskId === undefined
                ? []
                : [{
                        taskId,
                        path: update.path,
                        field: "readiness_review_revision",
                        reason: "path-and-ordering-only",
                        provenance: "workspace-numbered-root-migration"
                    }];
        }
        catch {
            return [];
        }
    });
    const byPath = new Map();
    for (const entry of [...mapped, ...repositoryRebindings])
        byPath.set(entry.path, entry);
    return [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path));
}
function normalizeSelections(selections) {
    const byUnit = new Map();
    for (const selection of selections) {
        if (selection === null
            || typeof selection !== "object"
            || !validId(selection.workUnitId)
            || !["basic", "structured", "governed"].includes(selection.workflowLevel)) {
            throw new WorkspaceMigrationPlanningError("Every migration selection requires a valid work-unit id and workflow level.");
        }
        let implementationOrder;
        if (selection.implementationOrder !== undefined) {
            try {
                implementationOrder = (0, ordered_root_identity_1.normalizeImplementationOrder)(selection.implementationOrder);
            }
            catch (error) {
                throw new WorkspaceMigrationPlanningError(error instanceof Error ? error.message : "Migration implementation order is invalid.");
            }
        }
        const taskSequences = selection.taskSequences === undefined
            ? undefined
            : Object.fromEntries(Object.entries(selection.taskSequences)
                .sort(([left], [right]) => left.localeCompare(right))
                .map(([taskId, sequence]) => {
                if (!validId(taskId) || !Number.isInteger(sequence) || sequence < 1 || sequence > 99) {
                    throw new WorkspaceMigrationPlanningError(`Task-sequence mapping '${taskId}=${String(sequence)}' must use a valid task id and value from 1 through 99.`);
                }
                return [taskId, sequence];
            }));
        const prior = byUnit.get(selection.workUnitId);
        if (prior !== undefined) {
            throw new WorkspaceMigrationPlanningError(prior.workflowLevel === selection.workflowLevel
                ? `Migration mapping for '${selection.workUnitId}' was supplied more than once.`
                : `Migration mapping for '${selection.workUnitId}' conflicts (${prior.workflowLevel} versus ${selection.workflowLevel}).`);
        }
        byUnit.set(selection.workUnitId, {
            workUnitId: selection.workUnitId,
            workflowLevel: selection.workflowLevel,
            ...(implementationOrder === undefined ? {} : { implementationOrder }),
            ...(taskSequences === undefined ? {} : { taskSequences })
        });
    }
    return [...byUnit.values()].sort((left, right) => left.workUnitId.localeCompare(right.workUnitId));
}
function requireRepositoryRoot(repositoryRoot) {
    if (typeof repositoryRoot !== "string" || repositoryRoot.trim() === "") {
        throw new WorkspaceMigrationPlanningError("A repository root is required.");
    }
    const resolved = node_fs_1.default.realpathSync(node_path_1.default.resolve(repositoryRoot));
    if (!node_fs_1.default.statSync(node_path_1.default.join(resolved, ".day-shift/config.toml"), { throwIfNoEntry: false })?.isFile()) {
        throw new WorkspaceMigrationPlanningError(`Repository root '${repositoryRoot}' has no .day-shift/config.toml marker.`);
    }
    return resolved;
}
function mapping(sourcePath, destinationPath, role) {
    return { sourcePath, destinationPath, role };
}
function compareMappings(left, right) {
    return `${left.destinationPath}\0${left.sourcePath ?? ""}`
        .localeCompare(`${right.destinationPath}\0${right.sourcePath ?? ""}`);
}
function requireTaskId(pair, workUnitId) {
    if (pair.taskId === null || !validId(pair.taskId)) {
        return `migrated-${(0, node_crypto_1.createHash)("sha256")
            .update(`${workUnitId}\0${pair.taskDefinitionPath}`)
            .digest("hex")
            .slice(0, 12)}`;
    }
    return pair.taskId;
}
function safeRepositoryPath(value) {
    try {
        return (0, paths_1.parseRepositoryRelativePath)(value).normalizedPath;
    }
    catch {
        return null;
    }
}
function relativeFromRoot(repositoryPath, root) {
    const relative = node_path_1.default.posix.relative(root, repositoryPath);
    return relative.startsWith("../") || relative === ".."
        ? node_path_1.default.posix.basename(repositoryPath)
        : relative;
}
function canonicalGovernedRelativePath(relativePath) {
    let relative = relativePath;
    if (relative === "overview.md" || relative === "slice-overview.md") {
        relative = "slice-overview.md";
    }
    else if (/^phases\/[^/]+\/overview\.md$/.test(relative)) {
        relative = relative.replace(/\/overview\.md$/, "/phase-overview.md");
    }
    else if (/\/milestones\/[^/]+\/overview\.md$/.test(relative)) {
        relative = relative.replace(/\/overview\.md$/, "/milestone-overview.md");
    }
    return relative;
}
function requireGovernedSliceId(unit) {
    if (unit.sliceId === null || !ORDERED_SLICE_ID_PATTERN.test(unit.sliceId)) {
        throw new WorkspaceMigrationPlanningError(`Governed work unit '${unit.workUnitId}' has no canonical ordered slice identity.`);
    }
    return unit.sliceId;
}
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function finding(code, paths, message, recovery) {
    return { code, severity: "error", paths: uniqueSorted(paths), message, recovery };
}
function sortFindings(findings) {
    return [...findings].sort((left, right) => `${left.paths[0] ?? ""}\0${left.code}\0${left.message}`
        .localeCompare(`${right.paths[0] ?? ""}\0${right.code}\0${right.message}`));
}
function uniqueExpected(expected) {
    const byKey = new Map();
    for (const condition of expected) {
        byKey.set(`${condition.kind}\0${condition.path}`, condition);
    }
    return [...byKey.values()].sort((left, right) => `${left.path}\0${left.kind}`.localeCompare(`${right.path}\0${right.kind}`));
}
function uniqueSorted(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function joinRepositoryPath(...segments) {
    return node_path_1.default.posix.join(...segments).split(node_path_1.default.sep).join("/");
}
function validId(value) {
    return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
}
function recoveryGuidance(status) {
    if (status === "blocked") {
        return "Resolve every reported blocker, then refresh --check or --plan. No repository files were written.";
    }
    if (status === "not-needed") {
        return "All detected task pairs are already under canonical workflow-level roots; no structural apply is needed.";
    }
    return "Review the complete plan and apply only with its exact digest and the same explicit selections.";
}
function stableJson(value) {
    if (value === undefined)
        return "null";
    if (Array.isArray(value))
        return `[${value.map(stableJson).join(",")}]`;
    if (value !== null && typeof value === "object") {
        const record = value;
        return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
    }
    return JSON.stringify(value);
}
