"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskContractRevisionRecoveryError = void 0;
exports.discoverTaskContractRevisionRecoverySync = discoverTaskContractRevisionRecoverySync;
exports.previewTaskContractRevisionRecoverySync = previewTaskContractRevisionRecoverySync;
exports.applyTaskContractRevisionRecoverySync = applyTaskContractRevisionRecoverySync;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_util_1 = require("node:util");
const mutation_preconditions_1 = require("../filesystem/mutation-preconditions");
const repository_transaction_1 = require("../filesystem/repository-transaction");
const lifecycle_run_context_1 = require("../lifecycle-run-context");
const evidence_store_1 = require("../lifecycle-run-context/evidence-store");
const paths_1 = require("../paths");
const canonical_write_target_1 = require("../paths/canonical-write-target");
const sandbox_contract_1 = require("../paths/sandbox-contract");
const artifact_policy_projection_1 = require("./artifact-policy-projection");
const task_attempts_1 = require("./task-attempts");
const task_cycle_runtime_attribution_1 = require("./task-cycle-runtime-attribution");
const task_lifecycle_1 = require("./task-lifecycle");
const task_lifecycle_timestamps_1 = require("./task-lifecycle-timestamps");
const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/;
const SHA256_PATTERN = /^(?:sha256:)?[a-f0-9]{64}$/;
class TaskContractRevisionRecoveryError extends Error {
    code;
    constructor(code, message) {
        super(message);
        this.name = "TaskContractRevisionRecoveryError";
        this.code = code;
    }
}
exports.TaskContractRevisionRecoveryError = TaskContractRevisionRecoveryError;
const plans = new WeakMap();
function discoverTaskContractRevisionRecoverySync(repositoryRootValue, taskDefinitionPath) {
    const pair = resolvePair(repositoryRootValue, taskDefinitionPath);
    const taskText = readUtf8(pair.task.runtimePath, pair.task.repositoryRelativePath);
    const summaryText = readUtf8(pair.summary.runtimePath, pair.summary.repositoryRelativePath);
    const taskRevision = sha256(taskText);
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: taskText,
        implementationSummary: summaryText,
    });
    const current = projection.summary?.currentAttempt ?? null;
    if (current === null ||
        current.attemptId === null ||
        current.closedAt !== null ||
        current.reviewDisposition !== "pending" ||
        projection.task.readinessAuthorization.applicable)
        return [];
    const candidates = [];
    for (const runParent of [sandbox_contract_1.SANDBOX_PARENT_DIRECTORY, sandbox_contract_1.LEGACY_SANDBOX_PARENT_DIRECTORY]) {
        const sandboxPath = node_path_1.default.join(pair.repositoryRoot, runParent);
        let directories;
        try {
            directories = node_fs_1.default.readdirSync(sandboxPath, { withFileTypes: true });
        }
        catch {
            continue;
        }
        for (const entry of directories
            .filter((candidate) => candidate.isDirectory() && candidate.name.startsWith("run-"))
            .sort((left, right) => left.name.localeCompare(right.name))
            .slice(0, 512)) {
            const runDirectory = node_path_1.default.posix.join(runParent, entry.name);
            try {
                const inspection = (0, lifecycle_run_context_1.inspectLifecycleRunContext)({
                    repositoryRoot: pair.repositoryRoot,
                    runDirectory,
                    selectedArtifactPath: pair.task.repositoryRelativePath,
                });
                const checkpoint = currentTaskCheckpoint(pair.repositoryRoot, inspection, pair.task.repositoryRelativePath, taskRevision, current.attemptId);
                if (checkpoint === null)
                    continue;
                candidates.push({
                    runDirectory,
                    runId: inspection.manifest.runId,
                    checkpointId: checkpoint.checkpointId,
                    oldTaskRevision: rawSha(checkpoint.oldActiveArtifactHash),
                    newTaskRevision: rawSha(checkpoint.newActiveArtifactHash),
                });
            }
            catch {
                continue;
            }
        }
    }
    return candidates;
}
function previewTaskContractRevisionRecoverySync(request) {
    validateRequest(request);
    const pair = resolvePair(request.repositoryRoot, request.taskDefinitionPath);
    const taskText = readUtf8(pair.task.runtimePath, pair.task.repositoryRelativePath);
    const summaryText = readUtf8(pair.summary.runtimePath, pair.summary.repositoryRelativePath);
    const observedTaskRevision = sha256(taskText);
    const observedSummaryRevision = sha256(summaryText);
    const inspection = inspectRun(pair, request.runDirectory);
    const checkpoint = latestCheckpoint(inspection, request.checkpointId);
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: taskText,
        implementationSummary: summaryText,
    });
    const selected = projection.summary?.attempts.find((attempt) => attempt.attemptId === request.attemptId) ?? null;
    if (selected?.reviewDisposition === "contract_gap" && selected.contractRevisionRecoveryId !== null) {
        return replayResult({
            request,
            pair,
            taskText,
            summaryText,
            inspection,
            checkpoint,
            selected,
        });
    }
    const legacyIncomingRecoveryId = selected !== null &&
        selected.successorKind === "contract_revision" &&
        selected.predecessorContractRevisionRecoveryId === null &&
        selected.contractRevisionRecoveryId !== null &&
        [
            selected.contractRevisionCheckpointId,
            selected.contractRevisionRunId,
            selected.contractRevisionOldTaskRevision,
            selected.contractRevisionNewTaskRevision,
        ].every((value) => value === null)
        ? selected.contractRevisionRecoveryId
        : null;
    if (selected !== null && [
        legacyIncomingRecoveryId === null ? selected.contractRevisionRecoveryId : null,
        selected.contractRevisionCheckpointId,
        selected.contractRevisionRunId,
        selected.contractRevisionOldTaskRevision,
        selected.contractRevisionNewTaskRevision,
    ].some((value) => value !== null)) {
        throw new TaskContractRevisionRecoveryError("checkpoint-conflict", `Attempt '${request.attemptId}' already contains conflicting contract-revision recovery evidence.`);
    }
    assertCheckpointCurrent(pair.repositoryRoot, inspection, checkpoint, pair.task.repositoryRelativePath, observedTaskRevision, normalizeSha(request.taskRevision), request.attemptId);
    const structuralBlockers = projection.blockingFindings.filter((finding) => finding.code !== "stale-readiness-authorization" &&
        finding.code !== "stale-attempt-readiness-reference");
    if (structuralBlockers.length > 0) {
        throw new TaskContractRevisionRecoveryError("pair-invalid", `Task pair is not structurally recoverable: ${structuralBlockers.map((finding) => finding.message).join(" ")}`);
    }
    const current = projection.summary?.currentAttempt ?? null;
    if (projection.task.status !== "draft" ||
        projection.task.readiness !== "ready_to_implement" ||
        !projection.task.readinessAuthorization.complete ||
        !projection.task.readinessAuthorization.valid ||
        projection.task.readinessAuthorization.applicable ||
        current?.attemptId !== request.attemptId ||
        current.closedAt !== null ||
        current.reviewDisposition !== "pending" ||
        (current.implementationStatus !== "pending" &&
            current.implementationStatus !== "in_progress" &&
            current.implementationStatus !== "blocked")) {
        throw new TaskContractRevisionRecoveryError("attempt-mismatch", "Contract-revision recovery requires the exact open pending attempt on a draft task with valid but stale readiness authorization.");
    }
    if (current.openedAt !== null &&
        Date.parse(request.recordedAt) < Date.parse(current.openedAt)) {
        throw new TaskContractRevisionRecoveryError("invalid-input", "Contract-revision recovery cannot be recorded before the selected attempt opened.");
    }
    if (Date.parse(request.recordedAt) < Date.parse(checkpoint.recordedAt)) {
        throw new TaskContractRevisionRecoveryError("invalid-input", "Contract-revision recovery cannot be recorded before the accepted contract-revision checkpoint.");
    }
    const baseline = (0, task_cycle_runtime_attribution_1.readAttemptRuntimeBaseline)(pair.summary.repositoryRelativePath, summaryText, request.attemptId);
    const evidenceProfile = (0, artifact_policy_projection_1.projectArtifactPolicy)(taskText).evidenceProfile;
    if (baseline === null && evidenceProfile !== "contract") {
        throw new TaskContractRevisionRecoveryError("baseline-required", "Contract-revision recovery requires the exact complete attempt-owned pre-write baseline.");
    }
    if (baseline !== null && (baseline.taskCycleId !== request.attemptId ||
        baseline.checkpointId !== current.readinessReviewId ||
        !baseline.capturedBeforeImplementationWrites ||
        !baseline.observationsComplete)) {
        throw new TaskContractRevisionRecoveryError("baseline-required", "Contract-revision recovery requires the exact complete attempt-owned pre-write baseline.");
    }
    const evidenceKinds = (0, task_attempts_1.inspectTaskAttemptSupersessionEvidence)(summaryText, request.attemptId);
    let targetDelta = [];
    if (baseline !== null) {
        try {
            targetDelta = (0, task_attempts_1.inspectSupersessionTargetDelta)(pair.repositoryRoot, pair.task.repositoryRelativePath, taskText, baseline);
        }
        catch (error) {
            if (error instanceof task_attempts_1.TaskAttemptSupersedeError) {
                throw new TaskContractRevisionRecoveryError("evidence-required", error.message);
            }
            throw error;
        }
    }
    const completeEvidenceKinds = [...evidenceKinds, ...targetDelta];
    if (completeEvidenceKinds.length === 0) {
        throw new TaskContractRevisionRecoveryError("evidence-required", "The selected attempt is baseline-only; use task attempt-supersede for this contract revision.");
    }
    const oldTaskRevision = rawSha(checkpoint.oldActiveArtifactHash);
    const newTaskRevision = rawSha(checkpoint.newActiveArtifactHash);
    const recoveryId = createRecoveryId({
        runId: inspection.manifest.runId,
        checkpointId: checkpoint.checkpointId,
        attemptId: request.attemptId,
        oldTaskRevision,
        newTaskRevision,
        recordedAt: request.recordedAt,
    });
    const nextTaskText = resetTaskReadinessAuthorization(taskText);
    const nextSummaryText = closeContractRevisionAttempt(summaryText, request.attemptId, {
        recoveryId,
        checkpointId: checkpoint.checkpointId,
        runId: inspection.manifest.runId,
        oldTaskRevision,
        newTaskRevision,
        recordedBy: request.recordedBy.trim(),
        recordedAt: request.recordedAt,
        reason: request.reason.trim(),
        predecessorRecoveryId: current.predecessorContractRevisionRecoveryId ?? legacyIncomingRecoveryId,
    });
    const proposed = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: nextTaskText,
        implementationSummary: nextSummaryText,
    });
    if (proposed.blockingFindings.length > 0) {
        throw new TaskContractRevisionRecoveryError("pair-invalid", `Contract-revision recovery would violate pair invariants: ${proposed.blockingFindings.map((finding) => finding.message).join(" ")}`);
    }
    const preconditions = [];
    for (const target of [pair.task, pair.summary]) {
        preconditions.push((0, mutation_preconditions_1.captureDestinationPresencePreconditionSync)(target));
        preconditions.push((0, mutation_preconditions_1.captureContentRevisionPreconditionSync)(target));
    }
    const writes = [
        { path: pair.task.repositoryRelativePath, contents: nextTaskText },
        { path: pair.summary.repositoryRelativePath, contents: nextSummaryText },
    ].sort((left, right) => left.path.localeCompare(right.path));
    const snapshot = {
        recoveryId,
        taskId: projection.task.taskId ?? request.attemptId,
        taskDefinitionPath: pair.task.repositoryRelativePath,
        implementationSummaryPath: pair.summary.repositoryRelativePath,
        runDirectory: inspection.manifest.runDirectory,
        runId: inspection.manifest.runId,
        checkpointId: checkpoint.checkpointId,
        priorAttemptId: request.attemptId,
        recoveryState: "contract_gap",
        recordedBy: request.recordedBy.trim(),
        recordedAt: request.recordedAt,
        reason: request.reason.trim(),
        oldTaskRevision,
        newTaskRevision,
        taskRevision: observedTaskRevision,
        implementationSummaryRevision: observedSummaryRevision,
        nextTaskRevision: sha256(nextTaskText),
        nextImplementationSummaryRevision: sha256(nextSummaryText),
        baselineId: baseline?.baselineId ?? null,
        evidenceKinds: completeEvidenceKinds,
        idempotent: false,
        intendedPaths: writes.map((write) => write.path),
        changedPaths: [],
        expected: preconditions.map(toExpectedCondition),
        writeOutcome: "previewed",
        transactionId: null,
        conflicts: [],
        recoveryPath: null,
        failure: null,
    };
    plans.set(snapshot, {
        repositoryRoot: pair.repositoryRoot,
        writes,
        preconditions,
        snapshot: cloneResult(snapshot),
    });
    return snapshot;
}
function applyTaskContractRevisionRecoverySync(preview, options = {}) {
    if (preview.writeOutcome === "no-write" && preview.idempotent)
        return cloneResult(preview);
    const plan = plans.get(preview);
    if (plan === undefined) {
        throw new TaskContractRevisionRecoveryError("stale-preview", "Contract-revision recovery apply requires the original in-process preview. Refresh the preview.");
    }
    (0, task_lifecycle_timestamps_1.revalidateObservedLifecycleTimestamp)(plan.snapshot.recordedAt, "recorded_at", "Contract-revision recovery time");
    const transaction = (0, repository_transaction_1.executeRepositoryTransactionSync)(plan.repositoryRoot, { writes: plan.writes, preconditions: plan.preconditions }, options.transaction);
    return {
        ...cloneResult(plan.snapshot),
        changedPaths: transaction.entries.slice(0, transaction.canonicalWrites).map((entry) => entry.path),
        writeOutcome: transaction.outcome,
        transactionId: transaction.transactionId,
        conflicts: transaction.conflicts,
        recoveryPath: transaction.outcome === "incomplete" ? transaction.transactionPath : null,
        failure: transaction.failure ?? (transaction.cleanupErrors.length > 0 ? transaction.cleanupErrors.join(" ") : null),
    };
}
function inspectRun(pair, runDirectory) {
    let inspection;
    try {
        inspection = (0, lifecycle_run_context_1.inspectLifecycleRunContext)({
            repositoryRoot: pair.repositoryRoot,
            runDirectory,
            selectedArtifactPath: pair.task.repositoryRelativePath,
        });
    }
    catch (error) {
        throw new TaskContractRevisionRecoveryError("run-ineligible", error instanceof Error ? error.message : "Lifecycle run could not be inspected.");
    }
    if (inspection.state !== "fresh" ||
        !inspection.manifestComplete ||
        inspection.manifest.lifecycleProfile !== "full-cycle-strict" ||
        inspection.manifest.activeArtifactPath !== pair.task.repositoryRelativePath) {
        throw new TaskContractRevisionRecoveryError("run-ineligible", "Recovery requires one fresh, complete full-cycle-strict run whose active artifact is the selected task.");
    }
    return inspection;
}
function latestCheckpoint(inspection, checkpointId) {
    const checkpoint = inspection.manifest.contractRevisions.at(-1) ?? null;
    if (checkpoint === null || checkpoint.checkpointId !== checkpointId) {
        throw new TaskContractRevisionRecoveryError("checkpoint-conflict", `Checkpoint '${checkpointId}' is not the latest accepted contract-revision checkpoint in the selected run.`);
    }
    return checkpoint;
}
function currentTaskCheckpoint(repositoryRoot, inspection, taskPath, taskRevision, attemptId) {
    if (inspection.state !== "fresh" ||
        !inspection.manifestComplete ||
        inspection.manifest.lifecycleProfile !== "full-cycle-strict" ||
        inspection.manifest.activeArtifactPath !== taskPath ||
        rawSha(inspection.manifest.activeArtifactHash) !== taskRevision)
        return null;
    const checkpoint = inspection.manifest.contractRevisions.at(-1) ?? null;
    if (checkpoint === null ||
        checkpoint.rollbackGate !== "task-readiness" ||
        checkpoint.oldActiveArtifactPath !== taskPath ||
        checkpoint.newActiveArtifactPath !== taskPath ||
        rawSha(checkpoint.newActiveArtifactHash) !== taskRevision ||
        checkpoint.oldActiveArtifactHash === checkpoint.newActiveArtifactHash ||
        !checkpointHasOldTaskEvidence(repositoryRoot, inspection, checkpoint, taskPath, attemptId))
        return null;
    return checkpoint;
}
function assertCheckpointCurrent(repositoryRoot, inspection, checkpoint, taskPath, observedTaskRevision, requestedTaskRevision, attemptId) {
    if (observedTaskRevision !== requestedTaskRevision) {
        throw new TaskContractRevisionRecoveryError("stale-task-revision", `Recovery expected task revision '${requestedTaskRevision}', but current bytes are '${observedTaskRevision}'.`);
    }
    if (checkpoint.rollbackGate !== "task-readiness" ||
        checkpoint.oldActiveArtifactPath !== taskPath ||
        checkpoint.newActiveArtifactPath !== taskPath ||
        rawSha(checkpoint.newActiveArtifactHash) !== observedTaskRevision ||
        rawSha(inspection.manifest.activeArtifactHash) !== observedTaskRevision ||
        checkpoint.oldActiveArtifactHash === checkpoint.newActiveArtifactHash ||
        !checkpointHasOldTaskEvidence(repositoryRoot, inspection, checkpoint, taskPath, attemptId)) {
        throw new TaskContractRevisionRecoveryError("checkpoint-conflict", "The latest checkpoint does not bind distinct old/new revisions of the selected active task at task-readiness.");
    }
}
function checkpointHasOldTaskEvidence(repositoryRoot, inspection, checkpoint, taskPath, attemptId) {
    const invalidated = new Set(checkpoint.invalidatedEntryIds);
    return inspection.manifest.entries.some((entry) => invalidated.has(entry.entryId) &&
        entry.artifactPath === taskPath &&
        entry.artifactHash === checkpoint.oldActiveArtifactHash &&
        entry.status === "ok" &&
        lifecycleEntryBindsAttempt(repositoryRoot, inspection, entry, attemptId));
}
function lifecycleEntryBindsAttempt(repositoryRoot, inspection, entry, attemptId) {
    try {
        const evidence = (0, evidence_store_1.readLifecycleEvidenceBytesSync)({
            repositoryRoot,
            runDirectory: inspection.manifest.runDirectory,
            entry,
            maximumBytes: lifecycle_run_context_1.MAX_LIFECYCLE_RUN_RESULT_BYTES,
        });
        const document = JSON.parse(new node_util_1.TextDecoder("utf-8", { fatal: true }).decode(evidence.bytes));
        const root = record(document);
        const envelope = record(root?.result) ?? root;
        const data = record(envelope?.data);
        if (data === null)
            return false;
        return [data, record(data.attempt), record(data.baseline), record(data.validation), record(data.result)]
            .filter((value) => value !== null)
            .some((value) => value.attemptId === attemptId ||
            value.taskCycleId === attemptId ||
            value.priorAttemptId === attemptId);
    }
    catch {
        return false;
    }
}
function record(value) {
    return value !== null && typeof value === "object" && !Array.isArray(value)
        ? value
        : null;
}
function replayResult(options) {
    const { request, pair, taskText, summaryText, inspection, checkpoint, selected } = options;
    const newTaskRevision = normalizeSha(request.taskRevision);
    if (checkpoint.rollbackGate !== "task-readiness" ||
        checkpoint.oldActiveArtifactPath !== pair.task.repositoryRelativePath ||
        checkpoint.newActiveArtifactPath !== pair.task.repositoryRelativePath ||
        selected.closedAt !== request.recordedAt ||
        selected.dispositionBy !== request.recordedBy.trim() ||
        selected.dispositionAt !== request.recordedAt ||
        selected.dispositionReason !== request.reason.trim() ||
        selected.contractRevisionCheckpointId !== checkpoint.checkpointId ||
        selected.contractRevisionRunId !== inspection.manifest.runId ||
        selected.contractRevisionOldTaskRevision !== rawSha(checkpoint.oldActiveArtifactHash) ||
        selected.contractRevisionNewTaskRevision !== newTaskRevision ||
        rawSha(checkpoint.newActiveArtifactHash) !== newTaskRevision) {
        throw new TaskContractRevisionRecoveryError("checkpoint-conflict", `Attempt '${request.attemptId}' already has conflicting contract-revision recovery evidence.`);
    }
    const baseline = (0, task_cycle_runtime_attribution_1.readAttemptRuntimeBaseline)(pair.summary.repositoryRelativePath, summaryText, request.attemptId);
    if (baseline === null && (0, artifact_policy_projection_1.projectArtifactPolicy)(taskText).evidenceProfile !== "contract") {
        throw new TaskContractRevisionRecoveryError("baseline-required", "Recovered predecessor baseline is missing.");
    }
    return {
        recoveryId: selected.contractRevisionRecoveryId,
        taskId: (0, task_lifecycle_1.projectTaskLifecyclePair)({ taskDefinition: taskText, implementationSummary: summaryText }).task.taskId ?? request.attemptId,
        taskDefinitionPath: pair.task.repositoryRelativePath,
        implementationSummaryPath: pair.summary.repositoryRelativePath,
        runDirectory: inspection.manifest.runDirectory,
        runId: inspection.manifest.runId,
        checkpointId: checkpoint.checkpointId,
        priorAttemptId: request.attemptId,
        recoveryState: "contract_gap",
        recordedBy: request.recordedBy.trim(),
        recordedAt: request.recordedAt,
        reason: request.reason.trim(),
        oldTaskRevision: rawSha(checkpoint.oldActiveArtifactHash),
        newTaskRevision,
        taskRevision: sha256(taskText),
        implementationSummaryRevision: sha256(summaryText),
        nextTaskRevision: sha256(taskText),
        nextImplementationSummaryRevision: sha256(summaryText),
        baselineId: baseline?.baselineId ?? null,
        evidenceKinds: [],
        idempotent: true,
        intendedPaths: [],
        changedPaths: [],
        expected: [],
        writeOutcome: "no-write",
        transactionId: null,
        conflicts: [],
        recoveryPath: null,
        failure: null,
    };
}
function closeContractRevisionAttempt(summaryText, attemptId, evidence) {
    const newline = summaryText.includes("\r\n") ? "\r\n" : "\n";
    const hadFinalNewline = summaryText.endsWith("\n");
    const lines = summaryText.split(/\r?\n/);
    let closing = lines.findIndex((line, index) => index > 0 && line === "---");
    if (lines[0] !== "---" || closing < 0) {
        throw new TaskContractRevisionRecoveryError("artifact-unreadable", "Implementation summary has malformed YAML front matter.");
    }
    const starts = lines
        .map((line, index) => ({ line, index }))
        .filter(({ line, index }) => index > 0 && index < closing && /^\s{2}-\s+attempt_id:/.test(line));
    const startEntry = starts.find(({ line }) => parseScalar(line.replace(/^\s{2}-\s+attempt_id:\s*/, "")) === attemptId);
    if (startEntry === undefined) {
        throw new TaskContractRevisionRecoveryError("attempt-mismatch", `Attempt '${attemptId}' is not materialized.`);
    }
    let end = startEntry.index + 1;
    while (end < closing &&
        !/^\s{2}-\s+attempt_id:/.test(lines[end]) &&
        (lines[end].trim() === "" || /^\s/.test(lines[end])))
        end += 1;
    const updates = [
        ["closed_at", evidence.recordedAt, true],
        ["review_disposition", "contract_gap", false],
        ["disposition_by", evidence.recordedBy, true],
        ["disposition_at", evidence.recordedAt, true],
        ["disposition_reason", evidence.reason, true],
        ["contract_revision_recovery_id", evidence.recoveryId, true],
        ["contract_revision_checkpoint_id", evidence.checkpointId, true],
        ["contract_revision_run_id", evidence.runId, true],
        ["contract_revision_old_task_revision", evidence.oldTaskRevision, true],
        ["contract_revision_new_task_revision", evidence.newTaskRevision, true],
    ];
    if (evidence.predecessorRecoveryId !== null) {
        updates.unshift([
            "predecessor_contract_revision_recovery_id",
            evidence.predecessorRecoveryId,
            true,
        ]);
    }
    for (const [field, value, quoted] of updates) {
        const pattern = new RegExp(`^\\s{4}${field}:`);
        const index = lines.findIndex((line, lineIndex) => lineIndex > startEntry.index && lineIndex < end && pattern.test(line));
        const rendered = `    ${field}: ${quoted ? JSON.stringify(value) : value}`;
        if (index >= 0)
            lines[index] = rendered;
        else {
            lines.splice(end, 0, rendered);
            end += 1;
            closing += 1;
        }
    }
    const currentIndex = lines.findIndex((line, index) => index > 0 && index < closing && /^current_attempt_id:/.test(line));
    if (currentIndex < 0) {
        throw new TaskContractRevisionRecoveryError("pair-invalid", "Implementation summary current_attempt_id is missing.");
    }
    lines[currentIndex] = "current_attempt_id: null";
    const rendered = lines.join(newline);
    return hadFinalNewline && !rendered.endsWith(newline) ? `${rendered}${newline}` : rendered;
}
function resetTaskReadinessAuthorization(taskText) {
    const newline = taskText.includes("\r\n") ? "\r\n" : "\n";
    const hadFinalNewline = taskText.endsWith("\n");
    const lines = taskText.split(/\r?\n/);
    let closing = lines.findIndex((line, index) => index > 0 && line === "---");
    if (lines[0] !== "---" || closing < 0) {
        throw new TaskContractRevisionRecoveryError("artifact-unreadable", "Task definition has malformed YAML front matter.");
    }
    const removable = new Set([
        "readiness_review_id",
        "readiness_review_revision",
        "readiness_review_protocol_version",
        "readiness_reviewed_by",
        "readiness_reviewed_at",
        "readiness_waiver_id",
    ]);
    for (let index = closing - 1; index > 0; index -= 1) {
        const field = lines[index].match(/^([A-Za-z0-9_]+):/)?.[1];
        if (field !== undefined && removable.has(field))
            lines.splice(index, 1);
    }
    closing = lines.findIndex((line, index) => index > 0 && line === "---");
    const readinessIndex = lines.findIndex((line, index) => index > 0 && index < closing && /^readiness:/.test(line));
    if (readinessIndex < 0) {
        throw new TaskContractRevisionRecoveryError("pair-invalid", "Task definition readiness field is missing.");
    }
    lines[readinessIndex] = "readiness: needs_more_evidence";
    const rendered = lines.join(newline);
    return hadFinalNewline && !rendered.endsWith(newline) ? `${rendered}${newline}` : rendered;
}
function resolvePair(repositoryRootValue, taskDefinitionPath) {
    const repositoryRoot = node_fs_1.default.realpathSync(node_path_1.default.resolve(requireText(repositoryRootValue, "Repository root")));
    let parsed;
    try {
        parsed = (0, paths_1.parseRepositoryRelativePath)(taskDefinitionPath);
    }
    catch (error) {
        throw new TaskContractRevisionRecoveryError("invalid-input", error instanceof Error ? error.message : "Invalid task-definition path.");
    }
    if (parsed.segments.at(-1) !== "task-definition.md") {
        throw new TaskContractRevisionRecoveryError("invalid-input", "Recovery paths must end in task-definition.md.");
    }
    const task = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, parsed.normalizedPath);
    const summary = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, [...parsed.segments.slice(0, -1), "implementation-summary.md"].join("/"));
    if (!task.exists || !summary.exists) {
        throw new TaskContractRevisionRecoveryError("artifact-unreadable", "Both task-definition.md and implementation-summary.md must exist.");
    }
    return { repositoryRoot, task, summary };
}
function readUtf8(runtimePath, relativePath) {
    const stats = node_fs_1.default.statSync(runtimePath, { throwIfNoEntry: false });
    if (stats === undefined || !stats.isFile()) {
        throw new TaskContractRevisionRecoveryError("artifact-unreadable", `Artifact '${relativePath}' must be a regular file.`);
    }
    try {
        return new node_util_1.TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(node_fs_1.default.readFileSync(runtimePath));
    }
    catch (error) {
        throw new TaskContractRevisionRecoveryError("artifact-unreadable", `Artifact '${relativePath}' must contain valid UTF-8: ${error instanceof Error ? error.message : String(error)}`);
    }
}
function validateRequest(request) {
    if (request === null || typeof request !== "object") {
        throw new TaskContractRevisionRecoveryError("invalid-input", "A contract-revision recovery request is required.");
    }
    requireId(request.attemptId, "Attempt identity");
    requireId(request.checkpointId, "Contract-revision checkpoint identity");
    normalizeSha(request.taskRevision);
    requireText(request.recordedBy, "Recovery actor");
    requireText(request.reason, "Recovery reason");
    if (!(0, task_lifecycle_1.isTaskLifecycleTimestamp)(request.recordedAt)) {
        throw new TaskContractRevisionRecoveryError("invalid-input", "Recovery time must be an ISO-8601 timestamp with an explicit timezone.");
    }
    (0, task_lifecycle_timestamps_1.requireObservedLifecycleTimestamp)({
        value: request.recordedAt,
        field: "recorded_at",
        label: "Contract-revision recovery time",
    });
}
function requireId(value, label) {
    const identity = requireText(value, label);
    if (!ID_PATTERN.test(identity)) {
        throw new TaskContractRevisionRecoveryError("invalid-input", `${label} must use 3-128 machine-safe characters.`);
    }
    return identity;
}
function requireText(value, label) {
    if (typeof value !== "string" || value.trim() === "") {
        throw new TaskContractRevisionRecoveryError("invalid-input", `${label} must be non-empty.`);
    }
    return value.trim();
}
function normalizeSha(value) {
    const revision = requireText(value, "Task revision");
    if (!SHA256_PATTERN.test(revision)) {
        throw new TaskContractRevisionRecoveryError("invalid-input", "Task revision must be an exact SHA-256 revision.");
    }
    return rawSha(revision);
}
function rawSha(value) {
    return value.startsWith("sha256:") ? value.slice("sha256:".length) : value;
}
function sha256(contents) {
    return (0, node_crypto_1.createHash)("sha256").update(contents).digest("hex");
}
function createRecoveryId(input) {
    return `task-contract-revision-recovery-${sha256(JSON.stringify(input)).slice(0, 24)}`;
}
function parseScalar(value) {
    const trimmed = value.trim();
    if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
        try {
            const parsed = JSON.parse(trimmed);
            return typeof parsed === "string" ? parsed : trimmed;
        }
        catch {
            return trimmed.slice(1, -1);
        }
    }
    if (trimmed.startsWith("'") && trimmed.endsWith("'"))
        return trimmed.slice(1, -1).replace(/''/g, "'");
    return trimmed;
}
function toExpectedCondition(precondition) {
    return {
        path: precondition.repositoryRelativePath,
        kind: precondition.kind,
        expectedState: precondition.kind === "content-revision"
            ? precondition.expectedRevision
            : precondition.expectedPresence,
    };
}
function cloneResult(result) {
    return {
        ...result,
        evidenceKinds: [...result.evidenceKinds],
        intendedPaths: [...result.intendedPaths],
        changedPaths: [...result.changedPaths],
        expected: result.expected.map((entry) => ({ ...entry })),
        conflicts: result.conflicts.map((entry) => ({ ...entry })),
    };
}
