"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskLifecycleTransitionError = void 0;
exports.previewTaskLifecycleTransitionSync = previewTaskLifecycleTransitionSync;
exports.applyTaskLifecycleTransitionSync = applyTaskLifecycleTransitionSync;
exports.previewTaskReadinessAuthorizationSync = previewTaskReadinessAuthorizationSync;
exports.previewTaskRetryAuthorizationSync = previewTaskRetryAuthorizationSync;
exports.previewTaskReviewRepairSync = previewTaskReviewRepairSync;
exports.previewTaskDispositionSync = previewTaskDispositionSync;
exports.previewTaskBlockSync = previewTaskBlockSync;
exports.previewTaskCancellationSync = previewTaskCancellationSync;
exports.previewTaskCloseSync = previewTaskCloseSync;
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 paths_1 = require("../paths");
const canonical_write_target_1 = require("../paths/canonical-write-target");
const implementation_summary_reconciliation_1 = require("./implementation-summary-reconciliation");
const task_attempts_1 = require("./task-attempts");
const task_lifecycle_1 = require("./task-lifecycle");
const task_readiness_review_1 = require("./task-readiness-review");
const task_waivers_1 = require("./task-waivers");
const task_lifecycle_timestamps_1 = require("./task-lifecycle-timestamps");
class TaskLifecycleTransitionError extends Error {
    code;
    constructor(code, message) {
        super(message);
        this.name = "TaskLifecycleTransitionError";
        this.code = code;
    }
}
exports.TaskLifecycleTransitionError = TaskLifecycleTransitionError;
const plans = new WeakMap();
/** Builds a deterministic owner-routed transition plan without writing any file. */
function previewTaskLifecycleTransitionSync(request) {
    if (request === null || typeof request !== "object") {
        throw new TaskLifecycleTransitionError("invalid-input", "A task lifecycle transition request is required.");
    }
    const pair = resolvePair(request.repositoryRoot, request.taskDefinitionPath);
    const taskText = readArtifact(pair.task.runtimePath, pair.task.repositoryRelativePath);
    const summaryText = readArtifact(pair.summary.runtimePath, pair.summary.repositoryRelativePath);
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: taskText,
        implementationSummary: summaryText
    });
    assertMutationCompatible(projection, request.kind);
    const lifecycleTimestamps = transitionTimestamps(request);
    for (const timestamp of lifecycleTimestamps) {
        if ((0, task_lifecycle_1.isTaskLifecycleTimestamp)(timestamp.value)) {
            (0, task_lifecycle_timestamps_1.requireObservedLifecycleTimestamp)(timestamp);
        }
    }
    const writes = renderTransition(request, projection, taskText, summaryText, pair.task.repositoryRelativePath);
    validateProposedPair(request.kind, writes.taskText, writes.summaryText);
    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 intendedWrites = [
        ...(writes.taskText === taskText ? [] : [{ path: pair.task.repositoryRelativePath, contents: writes.taskText }]),
        ...(writes.summaryText === summaryText ? [] : [{ path: pair.summary.repositoryRelativePath, contents: writes.summaryText }])
    ].sort((left, right) => left.path.localeCompare(right.path));
    const idempotent = (request.kind === "close" || request.kind === "readiness-authorize")
        && intendedWrites.length === 0;
    if (intendedWrites.length === 0 && !idempotent) {
        throw new TaskLifecycleTransitionError("transition-invalid", "The requested lifecycle transition would not change canonical evidence.");
    }
    const result = {
        transition: request.kind,
        taskId: requireTaskId(projection),
        taskDefinitionPath: pair.task.repositoryRelativePath,
        implementationSummaryPath: pair.summary.repositoryRelativePath,
        taskRevision: sha256(taskText),
        implementationSummaryRevision: sha256(summaryText),
        idempotent,
        intendedPaths: intendedWrites.map((write) => write.path),
        changedPaths: [],
        expected: preconditions.map(toExpectedCondition),
        writeOutcome: "previewed",
        transactionId: null,
        conflicts: [],
        recoveryPath: null,
        failure: null
    };
    plans.set(result, {
        repositoryRoot: pair.repositoryRoot,
        writes: intendedWrites,
        preconditions,
        taskPath: pair.task.runtimePath,
        summaryPath: pair.summary.runtimePath,
        transition: request.kind,
        readinessReview: request.kind === "readiness-authorize"
            || request.kind === "retry-authorize"
            || request.kind === "review-repair"
            ? cloneReadinessReview(request.reviewResult)
            : null,
        implementationReviewReceipt: request.kind === "close"
            ? request.reviewReceipt === undefined
                ? null
                : cloneImplementationReviewReceipt(request.reviewReceipt)
            : request.kind === "disposition" && request.reviewReceipt !== undefined
                ? cloneImplementationReviewReceipt(request.reviewReceipt)
                : null,
        resultSnapshot: cloneTransitionResult(result),
        lifecycleTimestamps
    });
    return result;
}
/** Applies only its original in-process preview through the shared repository transaction. */
function applyTaskLifecycleTransitionSync(preview, options = {}) {
    const plan = plans.get(preview);
    if (plan === undefined) {
        throw new TaskLifecycleTransitionError("stale-preview", "Task lifecycle apply requires the original in-process preview. Refresh the preview.");
    }
    const snapshot = plan.resultSnapshot;
    if (snapshot.idempotent && plan.writes.length === 0) {
        return {
            ...cloneTransitionResult(snapshot),
            writeOutcome: "no-write"
        };
    }
    for (const timestamp of plan.lifecycleTimestamps) {
        (0, task_lifecycle_timestamps_1.revalidateObservedLifecycleTimestamp)(timestamp.value, timestamp.field, timestamp.label);
    }
    const callerHook = options.transaction?.hook;
    const lateConflicts = [];
    let observedAppliedWrites = 0;
    const verifyPairRevisions = (appliedWrites) => {
        const taskText = readArtifact(plan.taskPath, snapshot.taskDefinitionPath);
        const summaryText = readArtifact(plan.summaryPath, snapshot.implementationSummaryPath);
        for (const [artifactPath, previewRevision, observedText] of [
            [snapshot.taskDefinitionPath, snapshot.taskRevision, taskText],
            [snapshot.implementationSummaryPath, snapshot.implementationSummaryRevision, summaryText]
        ]) {
            const writeIndex = plan.writes.findIndex((write) => write.path === artifactPath);
            const expectedRevision = writeIndex >= 0 && writeIndex < appliedWrites
                ? sha256(plan.writes[writeIndex].contents)
                : previewRevision;
            const observedRevision = sha256(observedText);
            if (observedRevision !== expectedRevision) {
                lateConflicts.push({
                    path: artifactPath,
                    expectationKind: "content-revision",
                    expectedState: expectedRevision,
                    observedState: observedRevision,
                    refreshPreviewRequired: true,
                    guidance: appliedWrites === 0
                        ? "The task pair changed before the first replacement; preserve the current bytes and refresh the preview."
                        : "The task pair changed during the transaction; preserve retained evidence and follow the recovery record."
                });
            }
        }
        if (lateConflicts.length > 0) {
            throw new Error("Task lifecycle pair changed after its transaction expectations were checked; no further canonical write was attempted.");
        }
        return { taskText, summaryText };
    };
    const transaction = (0, repository_transaction_1.executeRepositoryTransactionSync)(plan.repositoryRoot, { writes: plan.writes, preconditions: plan.preconditions }, {
        ...options.transaction,
        hook(context) {
            callerHook?.(context);
            if (lateConflicts.length > 0)
                return;
            if (context.phase === "after-replacement" || context.phase === "before-postcondition") {
                observedAppliedWrites = Math.max(observedAppliedWrites, (context.entryIndex ?? -1) + 1);
            }
            const appliedWrites = context.phase === "after-preconditions" || context.phase === "after-staging"
                ? 0
                : context.phase === "before-replacement"
                    ? context.entryIndex ?? 0
                    : context.phase === "after-replacement" || context.phase === "before-postcondition"
                        ? (context.entryIndex ?? -1) + 1
                        : context.phase === "before-cleanup"
                            ? observedAppliedWrites === plan.writes.length ? observedAppliedWrites : null
                            : null;
            if (appliedWrites !== null) {
                const { taskText, summaryText } = verifyPairRevisions(appliedWrites);
                if (appliedWrites !== 0)
                    return;
                const current = (0, task_lifecycle_1.projectTaskLifecyclePair)({
                    taskDefinition: taskText,
                    implementationSummary: summaryText
                });
                assertMutationCompatible(current, plan.transition);
                if (plan.readinessReview !== null) {
                    verifyReadinessReview(plan.readinessReview, plan.repositoryRoot, snapshot.taskDefinitionPath, {
                        allowStaleAuthorizationReplacement: plan.transition === "readiness-authorize"
                            && plan.readinessReview.authorizationState === "stale"
                    });
                }
                if (plan.implementationReviewReceipt !== null) {
                    verifyImplementationReviewReceipt(plan.implementationReviewReceipt, plan.repositoryRoot, snapshot.taskDefinitionPath, taskText, summaryText, plan.transition === "close" ? "close" : "disposition");
                }
            }
        }
    });
    const reportedOutcome = lateConflicts.length > 0 && transaction.outcome === "no-write"
        ? "conflict"
        : lateConflicts.length > 0 && transaction.outcome === "committed"
            ? "incomplete"
            : transaction.outcome;
    return {
        ...cloneTransitionResult(snapshot),
        writeOutcome: reportedOutcome,
        changedPaths: transaction.entries.slice(0, transaction.canonicalWrites).map((entry) => entry.path),
        transactionId: transaction.transactionId,
        conflicts: lateConflicts.length > 0 ? lateConflicts : transaction.conflicts,
        recoveryPath: reportedOutcome === "incomplete" ? transaction.transactionPath : null,
        failure: transaction.failure ?? (transaction.cleanupErrors.length > 0 ? transaction.cleanupErrors.join(" ") : null)
    };
}
function cloneTransitionResult(result) {
    return {
        ...result,
        intendedPaths: [...result.intendedPaths],
        changedPaths: [...result.changedPaths],
        expected: result.expected.map((entry) => ({ ...entry })),
        conflicts: result.conflicts.map((entry) => ({ ...entry }))
    };
}
function cloneReadinessReview(review) {
    return JSON.parse(JSON.stringify(review));
}
function cloneImplementationReviewReceipt(receipt) {
    return JSON.parse(JSON.stringify(receipt));
}
function previewTaskReadinessAuthorizationSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "readiness-authorize" });
}
function previewTaskRetryAuthorizationSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "retry-authorize" });
}
function previewTaskReviewRepairSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "review-repair" });
}
function previewTaskDispositionSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "disposition" });
}
function previewTaskBlockSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "block" });
}
function previewTaskCancellationSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "cancel" });
}
function previewTaskCloseSync(request) {
    return previewTaskLifecycleTransitionSync({ ...request, kind: "close" });
}
function renderTransition(request, projection, taskText, summaryText, canonicalTaskPath) {
    if (request.kind === "readiness-authorize") {
        if (projection.task.status === "draft" && projection.task.readiness === "ready_to_implement") {
            if (projection.task.readinessAuthorization.applicable) {
                verifyReadinessAuthorizationReplay(request.reviewResult, projection, canonicalTaskPath);
                return { taskText, summaryText };
            }
            if ((projection.summary?.attempts.length ?? 0) > 0
                || projection.summary?.currentAttempt !== null) {
                throw new TaskLifecycleTransitionError("transition-invalid", "Stale readiness authorization can be replaced directly only when no attempt or material lifecycle evidence exists; baseline-only state must route through task attempt-supersede.");
            }
            const review = verifyReadinessReview(request.reviewResult, request.repositoryRoot, canonicalTaskPath, { allowStaleAuthorizationReplacement: true });
            return {
                taskText: updateTopLevelFields(taskText, {
                    readiness: "ready_to_implement",
                    readiness_review_id: review.readinessReviewId,
                    readiness_review_revision: review.authorizationRevision,
                    readiness_review_protocol_version: String(review.protocolVersion),
                    readiness_reviewed_by: review.reviewedBy,
                    readiness_reviewed_at: review.reviewedAt
                }),
                summaryText
            };
        }
        const review = verifyReadinessReview(request.reviewResult, request.repositoryRoot, canonicalTaskPath);
        if (projection.task.status !== "draft" || projection.task.readiness !== "needs_more_evidence") {
            throw new TaskLifecycleTransitionError("transition-invalid", "Readiness authorization requires a draft task whose readiness is needs_more_evidence.");
        }
        const existingAuthorization = projection.task.readinessAuthorization;
        if (existingAuthorization.id !== null
            || existingAuthorization.taskRevision !== null
            || existingAuthorization.protocolVersion !== null
            || existingAuthorization.reviewer !== null
            || existingAuthorization.reviewedAt !== null) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Readiness authorization refuses a partial or pre-existing authorization envelope.");
        }
        return {
            taskText: updateTopLevelFields(taskText, {
                readiness: "ready_to_implement",
                readiness_review_id: review.readinessReviewId,
                readiness_review_revision: review.authorizationRevision,
                readiness_review_protocol_version: String(review.protocolVersion),
                readiness_reviewed_by: review.reviewedBy,
                readiness_reviewed_at: review.reviewedAt
            }),
            summaryText
        };
    }
    if (request.kind === "retry-authorize") {
        const review = verifyReadinessReview(request.reviewResult, request.repositoryRoot, canonicalTaskPath);
        const current = projection.summary?.currentAttempt ?? null;
        if (projection.task.status !== "draft"
            || projection.task.readiness !== "needs_more_evidence"
            || current?.attemptId === null
            || current?.attemptId === undefined
            || current.closedAt === null
            || current.reviewDisposition !== "changes_requested") {
            throw new TaskLifecycleTransitionError("transition-invalid", "Retry authorization requires a draft task reset to needs_more_evidence and a closed changes-requested predecessor.");
        }
        if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/.test(request.attemptId)) {
            throw new TaskLifecycleTransitionError("invalid-input", "Retry attempt identity must use 3-128 machine-safe characters.");
        }
        const openedAt = requireTimestamp(request.openedAt, "Retry attempt open time");
        if (Date.parse(openedAt) < Date.parse(current.closedAt)) {
            throw new TaskLifecycleTransitionError("transition-invalid", "A retry cannot open before its predecessor closed.");
        }
        if ((projection.summary?.attempts ?? []).some((attempt) => attempt.attemptId === request.attemptId)) {
            throw new TaskLifecycleTransitionError("transition-invalid", `Attempt identity '${request.attemptId}' already exists.`);
        }
        return {
            taskText: updateTopLevelFields(taskText, {
                readiness: "ready_to_implement",
                readiness_review_id: review.readinessReviewId,
                readiness_review_revision: review.authorizationRevision,
                readiness_review_protocol_version: String(review.protocolVersion),
                readiness_reviewed_by: review.reviewedBy,
                readiness_reviewed_at: review.reviewedAt
            }),
            summaryText: (0, task_attempts_1.materializeTaskAttemptSummary)(summaryText, {
                attemptId: request.attemptId,
                readinessReviewId: review.readinessReviewId,
                readinessAuthorizationKind: "review",
                openedAt,
                predecessorAttemptId: current.attemptId,
                implementationStatus: "in_progress",
                legacyReviewDisposition: null
            })
        };
    }
    if (request.kind === "review-repair") {
        const review = verifyReadinessReview(request.reviewResult, request.repositoryRoot, canonicalTaskPath);
        const current = projection.summary?.currentAttempt ?? null;
        const authorization = projection.task.readinessAuthorization;
        if (projection.task.status !== "completed"
            || projection.task.readiness !== "completed"
            || projection.summary?.status !== "completed"
            || authorization.kind !== "review"
            || !authorization.complete
            || !authorization.applicable
            || current?.attemptId === null
            || current?.attemptId === undefined
            || current.closedAt === null
            || current.implementationStatus !== "completed"
            || (current.reviewDisposition !== "accepted"
                && current.reviewDisposition !== "accepted_with_follow_up")) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Review repair requires a completed task with applicable review readiness and a closed completed accepted predecessor.");
        }
        if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/.test(request.attemptId)) {
            throw new TaskLifecycleTransitionError("invalid-input", "Repair attempt identity must use 3-128 machine-safe characters.");
        }
        if ((projection.summary?.attempts ?? []).some((attempt) => attempt.attemptId === request.attemptId)) {
            throw new TaskLifecycleTransitionError("transition-invalid", `Attempt identity '${request.attemptId}' already exists.`);
        }
        const openedAt = requireTimestamp(request.openedAt, "Repair attempt open time");
        if (Date.parse(openedAt) < Date.parse(current.closedAt)
            || Date.parse(openedAt) < Date.parse(review.reviewedAt)) {
            throw new TaskLifecycleTransitionError("transition-invalid", "A review-repair successor cannot open before its predecessor closed or its repair readiness review completed.");
        }
        const readinessReviewId = authorization.id;
        if (readinessReviewId === null) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Review repair requires the predecessor task's canonical readiness-review identity.");
        }
        return {
            taskText: updateTopLevelFields(taskText, {
                status: "draft",
                readiness: "ready_to_implement"
            }),
            summaryText: (0, task_attempts_1.materializeTaskAttemptSummary)(removeTopLevelFields(summaryText, ["status", "completed_at"]), {
                attemptId: request.attemptId,
                readinessReviewId,
                readinessAuthorizationKind: "review",
                openedAt,
                predecessorAttemptId: current.attemptId,
                implementationStatus: "in_progress",
                legacyReviewDisposition: null,
                successorKind: "review_repair",
                repairReason: requireNonEmpty(request.reason, "Repair reason"),
                repairReadinessReviewId: review.readinessReviewId,
                repairTaskRevision: review.taskRevision,
                repairReviewedBy: review.reviewedBy,
                repairReviewedAt: review.reviewedAt
            })
        };
    }
    const current = projection.summary?.currentAttempt;
    const attemptId = projection.summary?.currentAttemptId;
    if (current === null || current === undefined || attemptId === null || attemptId === undefined) {
        throw new TaskLifecycleTransitionError("unmaterialized-attempt", "This lifecycle write requires an existing materialized current attempt; no history was invented. Materialize the legacy or new attempt through the preservation workflow first.");
    }
    if (projection.task.status !== "draft"
        && !(request.kind === "close" && projection.task.status === "completed")) {
        throw new TaskLifecycleTransitionError("transition-invalid", "Only a draft task can receive this lifecycle transition.");
    }
    if (request.kind === "disposition") {
        if (current.closedAt !== null
            || (current.reviewDisposition !== "pending" && current.reviewDisposition !== "blocked")) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Disposition requires an open current attempt whose review state is pending or blocked.");
        }
        const recordedBy = requireNonEmpty(request.recordedBy, "Disposition actor");
        const recordedAt = requireTimestamp(request.recordedAt, "Disposition time");
        if (!["changes_requested", "accepted", "accepted_with_follow_up"].includes(request.disposition)) {
            throw new TaskLifecycleTransitionError("invalid-input", "Unsupported task review disposition.");
        }
        if (request.disposition === "accepted" || request.disposition === "accepted_with_follow_up") {
            verifyImplementationReviewReceipt(request.reviewReceipt, request.repositoryRoot, canonicalTaskPath, taskText, summaryText, "disposition");
            if (request.reviewReceipt?.reviewDisposition !== "pending") {
                throw new TaskLifecycleTransitionError("review-invalid", "Accepted disposition requires a zero-blocker receipt for the current pending review state.");
            }
        }
        const reasonRequired = request.disposition === "changes_requested"
            || request.disposition === "accepted_with_follow_up";
        const reason = reasonRequired
            ? requireNonEmpty(request.reason, "Disposition reason")
            : optionalNonEmpty(request.reason, "Disposition reason");
        return {
            taskText,
            summaryText: updateCurrentAttempt(summaryText, attemptId, {
                review_disposition: request.disposition,
                closed_at: recordedAt,
                disposition_by: recordedBy,
                disposition_at: recordedAt,
                ...(reason === undefined ? {} : { disposition_reason: reason })
            })
        };
    }
    if (request.kind === "block") {
        if (current.closedAt !== null
            || (current.reviewDisposition !== "pending" && current.reviewDisposition !== "blocked")) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Blocking requires an active open current attempt with pending or blocked review state.");
        }
        const recordedBy = requireNonEmpty(request.recordedBy, "Blocker actor");
        const recordedAt = requireTimestamp(request.recordedAt, "Blocker time");
        if (request.dimension !== "implementation" && request.dimension !== "review") {
            throw new TaskLifecycleTransitionError("invalid-input", "Block dimension must be implementation or review.");
        }
        if (request.dimension === "implementation" && current.reviewDisposition !== "pending") {
            throw new TaskLifecycleTransitionError("transition-invalid", "Implementation blocking requires pending review state and preserves that independent dimension.");
        }
        if (request.dimension === "implementation"
            && current.implementationStatus !== "pending"
            && current.implementationStatus !== "in_progress"
            && current.implementationStatus !== "blocked") {
            throw new TaskLifecycleTransitionError("transition-invalid", "Implementation blocking cannot overwrite completed implementation evidence.");
        }
        if (request.dimension === "review" && current.implementationStatus !== "completed") {
            throw new TaskLifecycleTransitionError("transition-invalid", "Review blocking requires completed implementation evidence and preserves that independent dimension.");
        }
        return {
            taskText,
            summaryText: updateCurrentAttempt(summaryText, attemptId, {
                ...(request.dimension === "implementation"
                    ? { implementation_status: "blocked" }
                    : { review_disposition: "blocked" }),
                blocker_reason: requireNonEmpty(request.reason, "Blocker reason"),
                blocker_next: requireNonEmpty(request.nextDecision, "Blocker next decision or dependency"),
                blocked_by: recordedBy,
                blocked_at: recordedAt,
                closed_at: "null"
            })
        };
    }
    if (request.kind === "cancel") {
        if (current.closedAt !== null
            || (current.reviewDisposition !== "pending" && current.reviewDisposition !== "blocked")) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Task cancellation requires an active open current attempt with pending or blocked review state.");
        }
        const recordedBy = requireNonEmpty(request.recordedBy, "Cancellation actor");
        const recordedAt = requireTimestamp(request.recordedAt, "Cancellation time");
        return {
            taskText: updateTopLevelFields(taskText, {
                status: "cancelled",
                cancellation_actor: recordedBy,
                cancellation_reason: requireNonEmpty(request.reason, "Cancellation reason"),
                cancelled_at: recordedAt
            }),
            summaryText: updateCurrentAttempt(summaryText, attemptId, {
                review_disposition: "cancelled",
                closed_at: recordedAt,
                disposition_by: recordedBy,
                disposition_at: recordedAt,
                disposition_reason: requireNonEmpty(request.reason, "Cancellation reason")
            })
        };
    }
    if (!projection.task.readinessAuthorization.complete
        || (projection.task.readiness !== "ready_to_implement"
            && projection.task.readiness !== "completed")
        || !(0, task_lifecycle_1.isTaskReadinessAuthorizationRevisionApplicable)(taskText, projection.task.readinessAuthorization.taskRevision)
        || current.implementationStatus !== "completed") {
        throw new TaskLifecycleTransitionError("transition-invalid", "Task close requires applicable readiness authorization and a completed current attempt with accepted review evidence.");
    }
    if (current.reviewDisposition === "accepted" || current.reviewDisposition === "accepted_with_follow_up") {
        verifyImplementationReviewReceipt(request.reviewReceipt, request.repositoryRoot, canonicalTaskPath, taskText, summaryText, "close");
        if (current.closedAt === null) {
            throw new TaskLifecycleTransitionError("transition-invalid", "Task close requires the accepted current attempt to carry its disposition close timestamp.");
        }
        return {
            taskText: updateTopLevelFields(taskText, {
                status: "completed",
                readiness: "completed"
            }),
            summaryText: updateTopLevelFields(summaryText, {
                status: "completed",
                completed_at: current.closedAt
            })
        };
    }
    const reviewWaiver = (0, task_waivers_1.findApplicableTaskWaiver)("implementation-summary.md", summaryText, {
        waiverType: "review",
        subjectId: projection.task.taskId ?? "missing-task-identity",
        attemptId,
        evaluatedAt: new Date().toISOString(),
        authorityRequired: true
    });
    if (reviewWaiver?.applicable !== true) {
        throw new TaskLifecycleTransitionError("transition-invalid", "Task close requires accepted review evidence or a matching active review waiver.");
    }
    return {
        taskText: updateTopLevelFields(taskText, {
            status: "completed",
            readiness: "completed"
        }),
        summaryText: updateTopLevelFields(updateCurrentAttempt(summaryText, attemptId, {
            review_disposition: "waived",
            review_waiver_id: reviewWaiver.waiver.waiverId,
            closed_at: reviewWaiver.waiver.recordedAt,
            disposition_by: reviewWaiver.waiver.recordedBy,
            disposition_at: reviewWaiver.waiver.recordedAt,
            disposition_reason: `Review requirement waived by ${reviewWaiver.waiver.waiverId}: ${reviewWaiver.waiver.reason}`
        }), {
            status: "completed",
            completed_at: reviewWaiver.waiver.recordedAt
        })
    };
}
function verifyReadinessAuthorizationReplay(value, projection, canonicalTaskPath) {
    if (value === null || typeof value !== "object" || Array.isArray(value)) {
        throw new TaskLifecycleTransitionError("review-invalid", "Readiness replay requires a complete review result.");
    }
    if (value.protocolVersion !== task_readiness_review_1.TASK_READINESS_REVIEW_PROTOCOL_VERSION || value.readOnly !== true) {
        throw new TaskLifecycleTransitionError("review-invalid", "The readiness replay review protocol is incomplete or unsupported.");
    }
    const { readinessReviewId: _readinessReviewId, ...reviewWithoutIdentity } = value;
    if ((0, task_readiness_review_1.computeTaskReadinessReviewId)(reviewWithoutIdentity) !== value.readinessReviewId) {
        throw new TaskLifecycleTransitionError("review-invalid", "The readiness replay review identity does not match its complete evidence.");
    }
    const authorization = projection.task.readinessAuthorization;
    const reviewAuthorizesTransition = value.readinessStatus === "ready to implement"
        && value.handoffStatus === "ready for implementation handoff"
        && value.blockingIssues.length === 0
        && value.findings.every((finding) => finding.severity !== "error")
        || isStaleAuthorizationReplacementReview(value);
    const matchesStoredAuthorization = authorization.complete
        && authorization.valid
        && authorization.applicable
        && authorization.kind === "review"
        && authorization.id === value.readinessReviewId
        && authorization.taskRevision === value.authorizationRevision
        && authorization.protocolVersion === String(value.protocolVersion)
        && authorization.reviewer === value.reviewedBy
        && authorization.reviewedAt === value.reviewedAt
        && value.taskDefinitionPath === canonicalTaskPath
        && reviewAuthorizesTransition;
    if (!matchesStoredAuthorization) {
        throw new TaskLifecycleTransitionError("review-mismatch", "The supplied readiness review does not exactly match the complete current revision-applicable authorization.");
    }
}
function verifyReadinessReview(value, repositoryRoot, canonicalTaskPath, options = {}) {
    if (value === null || typeof value !== "object" || Array.isArray(value)) {
        throw new TaskLifecycleTransitionError("review-invalid", "Readiness authorization requires a complete review result.");
    }
    if (value.protocolVersion !== task_readiness_review_1.TASK_READINESS_REVIEW_PROTOCOL_VERSION) {
        throw new TaskLifecycleTransitionError("review-invalid", "The readiness review protocol version is unsupported.");
    }
    let rerun;
    try {
        rerun = (0, task_readiness_review_1.reviewTaskReadiness)(repositoryRoot, {
            taskDefinitionPath: canonicalTaskPath,
            reviewedBy: value.reviewedBy,
            reviewedAt: value.reviewedAt
        });
    }
    catch (error) {
        throw new TaskLifecycleTransitionError("review-mismatch", error instanceof Error ? error.message : "The readiness review could not be reproduced.");
    }
    if (canonicalJson({ ...value, repositoryRoot: undefined })
        !== canonicalJson({ ...rerun, repositoryRoot: undefined })) {
        throw new TaskLifecycleTransitionError("review-mismatch", "The supplied complete readiness review does not match the current task bytes, canonical path, reviewer, review time, protocol, or findings.");
    }
    const staleAuthorizationOnly = options.allowStaleAuthorizationReplacement === true
        && isStaleAuthorizationReview(rerun);
    if (!staleAuthorizationOnly
        && (rerun.readinessStatus !== "ready to implement" || rerun.blockingIssues.length !== 0)) {
        throw new TaskLifecycleTransitionError("review-invalid", "Readiness authorization requires a ready review with no blocking findings.");
    }
    return rerun;
}
function isStaleAuthorizationReplacementReview(value) {
    return isStaleAuthorizationReview(value)
        && value.currentAttemptState.state === "absent";
}
function isStaleAuthorizationReview(value) {
    return value.authoredReadiness === "ready_to_implement"
        && value.authorizationState === "stale"
        && value.blockingIssues.filter((issue) => !issue.startsWith("Stored readiness authorization is stale after a task contract revision")).length === 0;
}
function verifyImplementationReviewReceipt(value, repositoryRoot, canonicalTaskPath, taskText, summaryText, authorization) {
    if (value === undefined || value === null || typeof value !== "object" || Array.isArray(value)) {
        throw new TaskLifecycleTransitionError("review-invalid", `Task ${authorization} requires a complete implementation-summary review receipt.`);
    }
    const canonicalSummaryPath = node_path_1.default.posix.join(node_path_1.default.posix.dirname(canonicalTaskPath), "implementation-summary.md");
    const validShape = value.schemaVersion === implementation_summary_reconciliation_1.IMPLEMENTATION_SUMMARY_REVIEW_RECEIPT_PROTOCOL_VERSION
        && /^implementation-summary-review-[a-f0-9]{24}$/.test(value.reviewReceiptId)
        && value.taskDefinitionPath === canonicalTaskPath
        && value.implementationSummaryPath === canonicalSummaryPath
        && /^[a-f0-9]{64}$/.test(value.taskRevision)
        && /^[a-f0-9]{64}$/.test(value.implementationSummaryRevision)
        && /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/.test(value.attemptId)
        && value.implementationStatus === "completed"
        && value.blockerCount === 0
        && Array.isArray(value.authorizes)
        && value.authorizes.includes(authorization);
    if (!validShape) {
        throw new TaskLifecycleTransitionError("review-invalid", `The implementation-summary review receipt is incomplete, blocking, unsupported, or does not authorize ${authorization}.`);
    }
    const receiptSubject = {
        schemaVersion: value.schemaVersion,
        taskDefinitionPath: value.taskDefinitionPath,
        implementationSummaryPath: value.implementationSummaryPath,
        taskRevision: value.taskRevision,
        implementationSummaryRevision: value.implementationSummaryRevision,
        attemptId: value.attemptId,
        implementationStatus: value.implementationStatus,
        reviewDisposition: value.reviewDisposition,
        blockerCount: value.blockerCount,
        authorizes: [...value.authorizes]
    };
    const expectedId = `implementation-summary-review-${sha256(canonicalJson(receiptSubject)).slice(0, 24)}`;
    if (value.reviewReceiptId !== expectedId) {
        throw new TaskLifecycleTransitionError("review-mismatch", "The implementation-summary review receipt identity does not match its immutable evidence.");
    }
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: taskText,
        implementationSummary: summaryText
    });
    const current = projection.summary?.currentAttempt ?? null;
    if (current?.attemptId !== value.attemptId
        || current.implementationStatus !== value.implementationStatus
        || current.reviewDisposition !== value.reviewDisposition) {
        throw new TaskLifecycleTransitionError("review-mismatch", "The implementation-summary review receipt is superseded or does not match the current attempt.");
    }
    const exactRevisionMatch = sha256(taskText) === value.taskRevision
        && sha256(summaryText) === value.implementationSummaryRevision;
    let reconstructedRevisionMatch = false;
    if (!exactRevisionMatch && authorization === "close") {
        const reconstructedTask = projection.task.status === "completed"
            && projection.task.readiness === "completed"
            ? updateTopLevelFields(taskText, {
                status: "draft",
                readiness: "ready_to_implement"
            })
            : taskText;
        const reconstructedSummary = projection.summary?.status === "completed"
            ? removeTopLevelFields(summaryText, ["status", "completed_at"])
            : summaryText;
        reconstructedRevisionMatch = sha256(reconstructedTask) === value.taskRevision
            && sha256(reconstructedSummary) === value.implementationSummaryRevision;
    }
    if (!exactRevisionMatch && !reconstructedRevisionMatch) {
        throw new TaskLifecycleTransitionError("review-mismatch", "The implementation-summary review receipt is stale or pair-mismatched for the current task and summary bytes.");
    }
    if (exactRevisionMatch) {
        let rerun;
        try {
            rerun = (0, implementation_summary_reconciliation_1.reviewImplementationSummary)(repositoryRoot, {
                taskDefinitionPath: canonicalTaskPath
            });
        }
        catch (error) {
            throw new TaskLifecycleTransitionError("review-mismatch", error instanceof Error ? error.message : "The implementation-summary review could not be reproduced.");
        }
        if (rerun.reviewReceipt === null
            || canonicalJson(rerun.reviewReceipt) !== canonicalJson(value)) {
            throw new TaskLifecycleTransitionError("review-mismatch", "The supplied implementation-summary review receipt does not reproduce from the current zero-blocker pair.");
        }
    }
    return value;
}
function canonicalJson(value) {
    if (Array.isArray(value))
        return `[${value.map((item) => item === undefined ? "null" : canonicalJson(item)).join(",")}]`;
    if (value !== null && typeof value === "object") {
        const record = value;
        return `{${Object.keys(record).filter((key) => record[key] !== undefined).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
    }
    return value === undefined ? "null" : JSON.stringify(value);
}
function validateProposedPair(transition, taskText, summaryText) {
    const initial = (0, task_lifecycle_1.projectTaskLifecyclePair)({ taskDefinition: taskText, implementationSummary: summaryText });
    const current = initial.summary?.currentAttempt ?? null;
    const reviewWaiverApplicable = current?.reviewDisposition === "waived"
        && current.reviewWaiverId !== null
        && (0, task_waivers_1.findApplicableTaskWaiver)("implementation-summary.md", summaryText, {
            waiverType: "review",
            subjectId: initial.task.taskId ?? "missing-task-identity",
            attemptId: current.attemptId,
            evaluatedAt: new Date().toISOString(),
            authorityRequired: true,
            waiverId: current.reviewWaiverId
        })?.applicable === true;
    const proposed = reviewWaiverApplicable
        ? (0, task_lifecycle_1.projectTaskLifecyclePair)({ taskDefinition: taskText, implementationSummary: summaryText, reviewWaiverApplicable: true })
        : initial;
    const blockers = proposed.blockingFindings;
    if (blockers.length > 0) {
        throw new TaskLifecycleTransitionError("transition-invalid", `The proposed lifecycle state violates pair invariants: ${blockers.map((finding) => finding.message).join(" ")}`);
    }
}
function assertMutationCompatible(projection, transition) {
    const blockers = transition === "retry-authorize"
        ? projection.blockingFindings.filter((finding) => finding.code !== "implementation-without-readiness"
            && finding.code !== "stale-attempt-readiness-reference"
            && finding.code !== "stale-readiness-authorization")
        : transition === "readiness-authorize"
            ? projection.blockingFindings.filter((finding) => finding.code !== "stale-readiness-authorization")
            : projection.blockingFindings;
    if (blockers.length > 0) {
        throw new TaskLifecycleTransitionError("pair-invalid", `Task lifecycle pair is invalid: ${blockers.map((finding) => finding.message).join(" ")}`);
    }
    const unsafeCompatibility = projection.compatibilityEvidence.filter((evidence) => evidence.code === "legacy-task-status"
        || evidence.code === "legacy-readiness-token"
        || evidence.code === "legacy-terminal-readiness"
        || evidence.code === "unmaterialized-legacy-attempt"
        || evidence.code === "legacy-attempt-lifecycle-token"
        || evidence.code === "legacy-summary-lifecycle-field"
        || evidence.code === "legacy-summary-current-projection");
    if (unsafeCompatibility.length > 0) {
        throw new TaskLifecycleTransitionError("legacy-mutation-unsupported", "This pair uses read-only legacy lifecycle evidence. Preserve it and run an explicit preservation-first migration before lifecycle mutation.");
    }
}
function resolvePair(repositoryRoot, taskDefinitionPath) {
    const root = node_fs_1.default.realpathSync(node_path_1.default.resolve(requireNonEmpty(repositoryRoot, "Repository root")));
    let parsed;
    try {
        parsed = (0, paths_1.parseRepositoryRelativePath)(taskDefinitionPath);
    }
    catch (error) {
        throw new TaskLifecycleTransitionError("invalid-input", error instanceof Error ? error.message : "Invalid task path.");
    }
    if (parsed.segments.at(-1) !== "task-definition.md") {
        throw new TaskLifecycleTransitionError("invalid-input", "Task lifecycle paths must end in task-definition.md.");
    }
    const task = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(root, parsed.normalizedPath);
    const summaryPath = [...parsed.segments.slice(0, -1), "implementation-summary.md"].join("/");
    const summary = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(root, summaryPath);
    if (!task.exists || !summary.exists) {
        throw new TaskLifecycleTransitionError("artifact-unreadable", "Both task-definition.md and implementation-summary.md must exist.");
    }
    return { repositoryRoot: root, task, summary };
}
function readArtifact(runtimePath, repositoryRelativePath) {
    const stats = node_fs_1.default.statSync(runtimePath, { throwIfNoEntry: false });
    if (stats === undefined || !stats.isFile()) {
        throw new TaskLifecycleTransitionError("artifact-unreadable", `Lifecycle artifact '${repositoryRelativePath}' must be a readable 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 TaskLifecycleTransitionError("artifact-unreadable", `Lifecycle artifact '${repositoryRelativePath}' must contain valid UTF-8: ${error instanceof Error ? error.message : String(error)}`);
    }
}
function updateTopLevelFields(markdown, updates) {
    const newline = markdown.includes("\r\n") ? "\r\n" : "\n";
    const hadFinalNewline = markdown.endsWith("\n");
    const lines = markdown.split(/\r?\n/);
    const closing = lines.findIndex((line, index) => index > 0 && line === "---");
    if (lines[0] !== "---" || closing < 0) {
        throw new TaskLifecycleTransitionError("artifact-unreadable", "Lifecycle artifact has malformed YAML front matter.");
    }
    let insertion = closing;
    const upstream = lines.findIndex((line, index) => index > 0 && index < closing && line === "upstream:");
    if (upstream >= 0)
        insertion = upstream;
    for (const [field, value] of Object.entries(updates)) {
        const index = lines.findIndex((line, lineIndex) => lineIndex > 0 && lineIndex < closing && new RegExp(`^${field}:`).test(line));
        if (index >= 0) {
            lines[index] = replaceExistingScalarLine(lines[index], value);
        }
        else {
            const rendered = `${field}: ${yamlValue(value)}`;
            lines.splice(insertion, 0, rendered);
            insertion += 1;
        }
    }
    const joined = lines.join(newline);
    return hadFinalNewline || joined.endsWith(newline) ? joined : joined;
}
function removeTopLevelFields(markdown, fields) {
    const newline = markdown.includes("\r\n") ? "\r\n" : "\n";
    const lines = markdown.split(/\r?\n/);
    const closing = lines.findIndex((line, index) => index > 0 && line === "---");
    if (lines[0] !== "---" || closing < 0) {
        throw new TaskLifecycleTransitionError("artifact-unreadable", "Lifecycle artifact has malformed YAML front matter.");
    }
    const selected = new Set(fields);
    const retained = lines.filter((line, index) => {
        if (index <= 0 || index >= closing)
            return true;
        const match = line.match(/^([A-Za-z0-9_]+):/);
        return match === null || !selected.has(match[1]);
    });
    return retained.join(newline);
}
function updateCurrentAttempt(markdown, attemptId, updates) {
    const newline = markdown.includes("\r\n") ? "\r\n" : "\n";
    const lines = markdown.split(/\r?\n/);
    const attemptsIndex = lines.findIndex((line) => /^attempts:\s*$/.test(line));
    if (attemptsIndex < 0) {
        throw new TaskLifecycleTransitionError("unmaterialized-attempt", "The current attempt collection is not materialized.");
    }
    let start = -1;
    let end = -1;
    for (let index = attemptsIndex + 1; index < lines.length; index += 1) {
        if (/^[^\s]/.test(lines[index]))
            break;
        const item = lines[index].match(/^\s{2}-\s+([A-Za-z0-9_]+):\s*(.*)$/);
        if (item !== null) {
            let itemEnd = index + 1;
            while (itemEnd < lines.length
                && !/^[^\s]/.test(lines[itemEnd])
                && !/^\s{2}-\s+/.test(lines[itemEnd])) {
                itemEnd += 1;
            }
            const nestedIdentity = lines.slice(index + 1, itemEnd)
                .map((line) => line.match(/^\s{4}attempt_id:\s*(.*)$/))
                .find((match) => match !== null);
            const itemIdentity = item[1] === "attempt_id"
                ? parseYamlScalar(item[2])
                : nestedIdentity === undefined || nestedIdentity === null
                    ? null
                    : parseYamlScalar(nestedIdentity[1]);
            if (itemIdentity === attemptId) {
                start = index;
                end = itemEnd;
                break;
            }
            index = itemEnd - 1;
        }
    }
    if (start < 0) {
        throw new TaskLifecycleTransitionError("unmaterialized-attempt", `Current attempt '${attemptId}' is not materialized.`);
    }
    let insertion = end;
    for (const [field, value] of Object.entries(updates)) {
        const leadingField = lines[start].match(/^\s{2}-\s+([A-Za-z0-9_]+):/);
        const index = leadingField?.[1] === field
            ? start
            : lines.findIndex((line, lineIndex) => lineIndex > start && lineIndex < end && new RegExp(`^\\s{4}${field}:`).test(line));
        if (index >= 0) {
            if (index === start) {
                const match = lines[index].match(/^(\s{2}-\s+[A-Za-z0-9_]+:\s*)(.*?)(\s*)$/);
                lines[index] = match === null
                    ? lines[index]
                    : `${match[1]}${replacementScalar(match[2], value)}${match[3]}`;
            }
            else {
                lines[index] = `    ${field}: ${yamlValue(value)}`;
            }
        }
        else {
            const rendered = `    ${field}: ${yamlValue(value)}`;
            lines.splice(insertion, 0, rendered);
            insertion += 1;
            end += 1;
        }
    }
    return lines.join(newline);
}
function yamlValue(value) {
    return value === "null" ? "null" : JSON.stringify(value);
}
function replacementScalar(existingRaw, value) {
    const trimmed = existingRaw.trim();
    if (trimmed.startsWith('"') && trimmed.endsWith('"'))
        return JSON.stringify(value);
    if (trimmed.startsWith("'") && trimmed.endsWith("'"))
        return `'${value.replace(/'/g, "''")}'`;
    return value;
}
function replaceExistingScalarLine(line, value) {
    const match = line.match(/^([A-Za-z0-9_]+:\s*)(.*?)(\s*)$/);
    if (match === null)
        return line;
    return `${match[1]}${replacementScalar(match[2], value)}${match[3]}`;
}
function parseYamlScalar(value) {
    const trimmed = value.trim();
    if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
        return trimmed.slice(1, -1);
    }
    return trimmed;
}
function requireTaskId(projection) {
    if (projection.task.taskId === null) {
        throw new TaskLifecycleTransitionError("pair-invalid", "Task identity is missing.");
    }
    return projection.task.taskId;
}
function requireNonEmpty(value, label) {
    if (typeof value !== "string" || value.trim() === "") {
        throw new TaskLifecycleTransitionError("invalid-input", `${label} must be non-empty.`);
    }
    return value.trim();
}
function optionalNonEmpty(value, label) {
    return value === undefined ? undefined : requireNonEmpty(value, label);
}
function requireTimestamp(value, label) {
    const normalized = requireNonEmpty(value, label);
    if (!(0, task_lifecycle_1.isTaskLifecycleTimestamp)(normalized)) {
        throw new TaskLifecycleTransitionError("invalid-input", `${label} must be an ISO-8601 timestamp.`);
    }
    return (0, task_lifecycle_timestamps_1.requireObservedLifecycleTimestamp)({
        value: normalized,
        field: transitionTimestampField(label),
        label
    });
}
function transitionTimestamps(request) {
    const readinessReviewTimestamp = 'reviewResult' in request
        && typeof request.reviewResult?.reviewedAt === 'string'
        ? [{
                value: request.reviewResult.reviewedAt,
                field: "readiness_reviewed_at",
                label: "Readiness review time"
            }]
        : [];
    if (request.kind === "retry-authorize") {
        return [
            { value: request.openedAt, field: "opened_at", label: "Retry attempt open time" },
            ...readinessReviewTimestamp
        ];
    }
    if (request.kind === "review-repair") {
        return [
            { value: request.openedAt, field: "opened_at", label: "Repair attempt open time" },
            ...readinessReviewTimestamp
        ];
    }
    if (request.kind === "disposition") {
        return [{ value: request.recordedAt, field: "disposition_at", label: "Disposition time" }];
    }
    if (request.kind === "block") {
        return [{ value: request.recordedAt, field: "blocked_at", label: "Blocker time" }];
    }
    if (request.kind === "cancel") {
        return [{ value: request.recordedAt, field: "cancelled_at", label: "Cancellation time" }];
    }
    if (request.kind === "readiness-authorize") {
        return readinessReviewTimestamp;
    }
    return [];
}
function transitionTimestampField(label) {
    if (label.includes("open time"))
        return "opened_at";
    if (label === "Disposition time")
        return "disposition_at";
    if (label === "Blocker time")
        return "blocked_at";
    if (label === "Cancellation time")
        return "cancelled_at";
    return "recorded_at";
}
function sha256(contents) {
    return (0, node_crypto_1.createHash)("sha256").update(Buffer.from(contents)).digest("hex");
}
function toExpectedCondition(precondition) {
    return {
        path: precondition.repositoryRelativePath,
        kind: precondition.kind,
        expectedState: precondition.kind === "content-revision"
            ? precondition.expectedRevision
            : precondition.expectedPresence
    };
}
