"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskImplementationCompletionError = void 0;
exports.previewTaskImplementationCompletionSync = previewTaskImplementationCompletionSync;
exports.applyTaskImplementationCompletionSync = applyTaskImplementationCompletionSync;
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_lifecycle_1 = require("./task-lifecycle");
const task_lifecycle_timestamps_1 = require("./task-lifecycle-timestamps");
const ATTEMPT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/;
const TASK_REVISION_PATTERN = /^[a-f0-9]{64}$/;
class TaskImplementationCompletionError extends Error {
    code;
    constructor(code, message) {
        super(message);
        this.name = "TaskImplementationCompletionError";
        this.code = code;
    }
}
exports.TaskImplementationCompletionError = TaskImplementationCompletionError;
const plans = new WeakMap();
/**
 * Projects the exact current-attempt implementation completion transition.
 *
 * The returned object is read-only and is the sole apply token accepted by the
 * atomic completion layer.
 */
function previewTaskImplementationCompletionSync(request) {
    if (request === null || typeof request !== "object") {
        throw new TaskImplementationCompletionError("invalid-input", "An implementation-completion request is required.");
    }
    const attemptId = requireAttemptId(request.attemptId);
    const completedBy = requireText(request.completedBy, "Completion actor");
    const completedAt = requireTimestamp(request.completedAt);
    const taskRevision = requireTaskRevision(request.taskRevision);
    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 observedTaskRevision = sha256(taskText);
    const implementationSummaryRevision = sha256(summaryText);
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: taskText,
        implementationSummary: summaryText,
        evaluatedAt: completedAt
    });
    const current = projection.summary?.currentAttempt ?? null;
    const findings = [];
    let idempotent = false;
    for (const finding of projection.blockingFindings) {
        findings.push({
            code: `lifecycle-${finding.code}`,
            severity: "error",
            artifact: finding.artifact,
            field: finding.field,
            message: finding.message
        });
    }
    if (taskRevision !== observedTaskRevision) {
        findings.push({
            code: "stale-task-revision",
            severity: "error",
            artifact: "request",
            field: "task_revision",
            message: `Completion is bound to task revision '${taskRevision}', but current bytes are '${observedTaskRevision}'.`
        });
    }
    const completionEvidence = (0, implementation_summary_reconciliation_1.reviewImplementationCompletionEvidence)(pair.repositoryRoot, {
        taskDefinitionPath: pair.task.repositoryRelativePath
    });
    for (const finding of completionEvidence.findings) {
        findings.push({
            code: finding.code,
            severity: "error",
            artifact: finding.artifact,
            field: finding.field,
            message: finding.message
        });
    }
    if (projection.task.taskId === null) {
        findings.push({
            code: "missing-task-identity",
            severity: "error",
            artifact: "task_definition",
            field: "id",
            message: "Implementation completion requires a canonical task identity."
        });
    }
    if (!projection.implementationAuthorized) {
        findings.push({
            code: "implementation-not-authorized",
            severity: "error",
            artifact: "pair",
            field: "readiness",
            message: "The selected task pair is not currently authorized for implementation."
        });
    }
    if (current === null || current.attemptId === null) {
        findings.push({
            code: "missing-current-attempt",
            severity: "error",
            artifact: "implementation_summary",
            field: "current_attempt_id",
            message: "Implementation completion requires one materialized current attempt."
        });
    }
    else {
        if (current.attemptId !== attemptId) {
            findings.push({
                code: "wrong-current-attempt",
                severity: "error",
                artifact: "request",
                field: "attempt_id",
                message: `Completion selected attempt '${attemptId}', but the current attempt is '${current.attemptId}'.`
            });
        }
        if (current.closedAt !== null) {
            findings.push({
                code: "attempt-already-closed",
                severity: "error",
                artifact: "implementation_summary",
                field: "attempts.closed_at",
                message: "Implementation completion cannot change a closed attempt."
            });
        }
        if (current.implementationStatus === "completed") {
            idempotent = current.attemptId === attemptId && current.closedAt === null;
        }
        else if (current.implementationStatus !== "in_progress"
            && current.implementationStatus !== "blocked") {
            findings.push({
                code: "implementation-state-not-applicable",
                severity: "error",
                artifact: "implementation_summary",
                field: "attempts.implementation_status",
                message: `Implementation completion requires 'in_progress' or a fully evidenced 'blocked' current attempt, but current status is '${current.implementationStatus ?? "(missing)"}'.`
            });
        }
        if (current.openedAt !== null && Date.parse(completedAt) < Date.parse(current.openedAt)) {
            findings.push({
                code: "completion-before-attempt-open",
                severity: "error",
                artifact: "request",
                field: "completed_at",
                message: "Completion time cannot precede the current attempt opened_at timestamp."
            });
        }
    }
    const blockingFindings = findings.filter((finding) => finding.severity === "error");
    if (blockingFindings.length === 0) {
        findings.push({
            code: idempotent
                ? "transition-already-applied"
                : current?.implementationStatus === "blocked"
                    ? "blocked-attempt-recertification-applicable"
                    : "transition-applicable",
            severity: "info",
            artifact: "pair",
            field: "attempts.implementation_status",
            message: idempotent
                ? "The exact current attempt already records completed implementation; replay requires no write."
                : current?.implementationStatus === "blocked"
                    ? "Fresh task revision and completion evidence recertify the exact blocked current attempt for evidence-preserving completion."
                    : "The exact current attempt is eligible for implementation-completion evidence evaluation."
        });
    }
    findings.push({
        code: "dry-run-no-write",
        severity: "info",
        artifact: "request",
        field: "dry_run",
        message: "Dry-run projected the transition without changing the task pair, attempt, reconciliation, or related state."
    });
    const applicable = blockingFindings.length === 0;
    const recoveryActions = applicable
        ? []
        : buildRejectedPreviewRecoveryActions({
            taskDefinitionPath: pair.task.repositoryRelativePath,
            requestedAttemptId: attemptId,
            currentAttemptId: current?.attemptId ?? null,
            completedBy,
            completedAt,
            observedTaskRevision,
            blockingFindings
        });
    const preconditions = [pair.task, pair.summary].flatMap((target) => [
        (0, mutation_preconditions_1.captureDestinationPresencePreconditionSync)(target),
        (0, mutation_preconditions_1.captureContentRevisionPreconditionSync)(target)
    ]);
    const result = {
        schemaVersion: 1,
        taskId: projection.task.taskId,
        taskDefinitionPath: pair.task.repositoryRelativePath,
        implementationSummaryPath: pair.summary.repositoryRelativePath,
        attemptId,
        completedBy,
        completedAt,
        boundTaskRevision: taskRevision,
        taskRevision: observedTaskRevision,
        implementationSummaryRevision,
        intendedTransition: {
            artifact: "implementation_summary",
            field: "attempts.implementation_status",
            attemptId,
            from: current?.implementationStatus ?? null,
            to: "completed",
            isolatedFrom: [
                "attempts.review_disposition",
                "attempts.closed_at",
                "task_definition.status",
                "reconciliation"
            ]
        },
        applicable,
        idempotent,
        findings,
        intendedPaths: applicable && !idempotent ? [pair.summary.repositoryRelativePath] : [],
        changedPaths: [],
        expected: preconditions.map((entry) => ({
            path: entry.repositoryRelativePath,
            kind: entry.kind,
            expectedState: entry.kind === "content-revision"
                ? entry.expectedRevision
                : entry.expectedPresence
        })),
        writeOutcome: "previewed",
        transactionId: null,
        conflicts: [],
        recoveryPath: null,
        failure: null,
        recoveryActions,
        noWriteBoundary: "Preview reads the canonical task pair and performs no filesystem or lifecycle mutation."
    };
    if (applicable && !idempotent) {
        const nextSummaryText = updateAttemptImplementationStatus(summaryText, attemptId, "completed");
        const nextProjection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
            taskDefinition: taskText,
            implementationSummary: nextSummaryText,
            evaluatedAt: completedAt
        });
        if (nextProjection.blockingFindings.length > 0
            || nextProjection.summary?.currentAttempt?.implementationStatus !== "completed"
            || nextProjection.summary?.currentAttempt?.reviewDisposition
                !== current?.reviewDisposition
            || nextProjection.summary?.currentAttempt?.closedAt !== current?.closedAt
            || nextProjection.task.status !== projection.task.status) {
            throw new TaskImplementationCompletionError("artifact-unreadable", "The isolated implementation-completion mutation would violate task-pair invariants.");
        }
        plans.set(result, {
            repositoryRoot: pair.repositoryRoot,
            nextSummaryText,
            preconditions,
            snapshot: cloneResult(result),
            completedAt
        });
    }
    return result;
}
/** Applies only an original in-process preview through the repository transaction. */
function applyTaskImplementationCompletionSync(preview, options = {}) {
    if (preview.writeOutcome !== "previewed") {
        throw new TaskImplementationCompletionError("stale-preview", "Implementation-completion apply requires an original preview result.");
    }
    const informationalFindings = preview.findings.filter((finding) => finding.code !== "dry-run-no-write");
    if (!preview.applicable) {
        return {
            ...cloneResult(preview),
            findings: [
                ...informationalFindings,
                {
                    code: "apply-rejected-no-write",
                    severity: "info",
                    artifact: "request",
                    field: "apply",
                    message: "Apply was rejected by typed completion findings before any transaction write."
                }
            ],
            writeOutcome: "no-write",
            noWriteBoundary: "Rejected apply requests do not enter the repository transaction."
        };
    }
    if (preview.idempotent) {
        return {
            ...cloneResult(preview),
            findings: [
                ...informationalFindings,
                {
                    code: "idempotent-replay-no-write",
                    severity: "info",
                    artifact: "pair",
                    field: "attempts.implementation_status",
                    message: "The identical completion state is already present; replay made no write."
                }
            ],
            writeOutcome: "no-write",
            noWriteBoundary: "Idempotent replay observes the completed current attempt and performs no filesystem mutation."
        };
    }
    const plan = plans.get(preview);
    if (plan === undefined) {
        throw new TaskImplementationCompletionError("stale-preview", "Implementation-completion apply requires its original in-process preview. Refresh the preview.");
    }
    (0, task_lifecycle_timestamps_1.revalidateObservedLifecycleTimestamp)(plan.completedAt, "completed_at", "Completion timestamp");
    const transaction = (0, repository_transaction_1.executeRepositoryTransactionSync)(plan.repositoryRoot, {
        writes: [{
                path: plan.snapshot.implementationSummaryPath,
                contents: plan.nextSummaryText
            }],
        preconditions: plan.preconditions
    }, options.transaction);
    const successful = transaction.outcome === "committed";
    return {
        ...cloneResult(plan.snapshot),
        applicable: successful,
        findings: [
            ...informationalFindings,
            {
                code: successful
                    ? "transition-applied"
                    : transaction.outcome === "conflict"
                        ? "transaction-precondition-conflict"
                        : "transaction-apply-failed",
                severity: successful ? "info" : "error",
                artifact: "pair",
                field: "attempts.implementation_status",
                message: successful
                    ? "Implementation completion committed through the shared repository transaction."
                    : transaction.outcome === "conflict"
                        ? "The task pair changed before apply; refresh the completion preview."
                        : "The completion transaction did not commit; retained recovery evidence is reported when applicable."
            }
        ],
        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),
        recoveryActions: successful
            ? []
            : transaction.outcome === "no-write"
                ? [createCallableCompletionRecoveryAction({
                        taskDefinitionPath: plan.snapshot.taskDefinitionPath,
                        attemptId: plan.snapshot.attemptId,
                        taskRevision: plan.snapshot.taskRevision,
                        completedBy: plan.snapshot.completedBy,
                        completedAt: plan.snapshot.completedAt,
                        explanation: "Retry the exact revision- and attempt-bound implementation completion request after the no-write transaction failure is resolved."
                    })]
                : [createNonCallableCompletionRecoveryAction(transaction.outcome === "conflict"
                        ? "The task pair changed after preview, so a safe recovery command requires a newly observed task revision and a fresh preview."
                        : `The repository transaction may retain partial state${transaction.transactionPath === null ? "" : ` at '${transaction.transactionPath}'`}; inspect and recover that transaction before issuing another completion mutation.`)],
        noWriteBoundary: successful
            ? "Apply changed only the current attempt implementation-completion field."
            : "Failed or conflicting apply preserves typed transaction evidence and reports every observed canonical write."
    };
}
function updateAttemptImplementationStatus(markdown, attemptId, status) {
    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 === "---");
    const attemptsIndex = lines.findIndex((line, index) => index > 0 && index < closing && /^attempts:\s*$/.test(line));
    if (lines[0] !== "---" || closing < 0 || attemptsIndex < 0) {
        throw new TaskImplementationCompletionError("artifact-unreadable", "Implementation summary has no supported materialized attempts collection.");
    }
    let matchCount = 0;
    for (let index = attemptsIndex + 1; index < closing; index += 1) {
        const identity = lines[index].match(/^ {2}- attempt_id:\s*(.*)$/);
        if (identity === null || parseYamlScalar(identity[1]) !== attemptId)
            continue;
        let end = index + 1;
        while (end < closing
            && !/^ {2}-\s+/.test(lines[end])
            && (/^\s/.test(lines[end]) || lines[end] === "")) {
            end += 1;
        }
        const statusIndex = lines.findIndex((line, lineIndex) => lineIndex > index
            && lineIndex < end
            && /^ {4}implementation_status:\s*/.test(line));
        if (statusIndex < 0) {
            throw new TaskImplementationCompletionError("artifact-unreadable", `Current attempt '${attemptId}' has no implementation_status field.`);
        }
        const scalar = lines[statusIndex].match(/^( {4}implementation_status:\s*)(.*?)(\s*)$/);
        if (scalar === null) {
            throw new TaskImplementationCompletionError("artifact-unreadable", `Current attempt '${attemptId}' has an unsupported implementation_status field.`);
        }
        lines[statusIndex] = `${scalar[1]}${replaceScalar(scalar[2], status)}${scalar[3]}`;
        matchCount += 1;
        index = end - 1;
    }
    if (matchCount !== 1) {
        throw new TaskImplementationCompletionError("artifact-unreadable", `Implementation completion requires exactly one materialized attempt '${attemptId}'.`);
    }
    const rendered = lines.join(newline);
    return hadFinalNewline && !rendered.endsWith(newline)
        ? `${rendered}${newline}`
        : rendered;
}
function parseYamlScalar(value) {
    const trimmed = value.trim();
    if ((trimmed.startsWith("\"") && trimmed.endsWith("\""))
        || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
        return trimmed.slice(1, -1);
    }
    return trimmed;
}
function replaceScalar(existing, value) {
    const trimmed = existing.trim();
    if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) {
        return JSON.stringify(value);
    }
    if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
        return `'${value.replace(/'/g, "''")}'`;
    }
    return value;
}
function cloneResult(result) {
    return {
        ...result,
        intendedTransition: {
            ...result.intendedTransition,
            isolatedFrom: [...result.intendedTransition.isolatedFrom]
        },
        findings: result.findings.map((finding) => ({ ...finding })),
        intendedPaths: [...result.intendedPaths],
        changedPaths: [...result.changedPaths],
        expected: result.expected.map((entry) => ({ ...entry })),
        conflicts: result.conflicts.map((entry) => ({ ...entry })),
        recoveryActions: result.recoveryActions.map(cloneRecoveryAction)
    };
}
function buildRejectedPreviewRecoveryActions(options) {
    const bindingFindingCodes = new Set([
        "stale-task-revision",
        "wrong-current-attempt"
    ]);
    const onlyCorrectableBindings = options.blockingFindings.every((finding) => bindingFindingCodes.has(finding.code));
    if (onlyCorrectableBindings && options.currentAttemptId !== null) {
        return [createCallableCompletionRecoveryAction({
                taskDefinitionPath: options.taskDefinitionPath,
                attemptId: options.currentAttemptId,
                taskRevision: options.observedTaskRevision,
                completedBy: options.completedBy,
                completedAt: options.completedAt,
                explanation: [
                    "Retry implementation completion with the observed task revision and exact current attempt identity.",
                    `The rejected request selected '${options.requestedAttemptId}'.`
                ].join(" ")
            })];
    }
    const codes = [...new Set(options.blockingFindings.map((finding) => finding.code))];
    return [createNonCallableCompletionRecoveryAction(`No complete callable recovery can be inferred for blocking finding${codes.length === 1 ? "" : "s"} ${codes.join(", ")}; the missing, stale, or invalid lifecycle evidence must be corrected with values that are not present in the rejected request.`)];
}
function createCallableCompletionRecoveryAction(options) {
    return {
        id: "recovery.task.implementation-complete",
        callable: true,
        explanation: options.explanation,
        command: "task implementation-complete",
        argv: [
            "day-shift",
            "task",
            "implementation-complete",
            "--task-definition",
            options.taskDefinitionPath,
            "--attempt-id",
            options.attemptId,
            "--task-revision",
            options.taskRevision,
            "--completed-by",
            options.completedBy,
            "--completed-at",
            options.completedAt
        ],
        writeCapable: true,
        effects: {
            filesystem: "write",
            scope: "selected canonical task pair",
            network: "none",
            hostState: "none",
            destructive: false,
            interactive: false
        },
        authorization: "workspace-write",
        confirmation: "explicit-command",
        selectionRequired: true,
        idempotency: "idempotent",
        preconditions: [{
                id: "selected-task-pair",
                description: "The canonical task definition and paired implementation summary remain selected.",
                status: "satisfied",
                expected: options.taskDefinitionPath,
                observed: options.taskDefinitionPath
            }, {
                id: "current-attempt",
                description: "The recovery targets the exact current open attempt.",
                status: "satisfied",
                expected: options.attemptId,
                observed: options.attemptId
            }, {
                id: "task-revision",
                description: "The recovery binds to the currently observed task-definition bytes.",
                status: "satisfied",
                expected: options.taskRevision,
                observed: options.taskRevision
            }],
        transition: {
            id: "task-current-attempt-implementation-complete",
            kind: "state-transition",
            expectedPostconditions: [{
                    id: "implementation-status-completed",
                    description: "Only the exact current attempt implementation_status becomes completed."
                }, {
                    id: "separate-lifecycle-dimensions-preserved",
                    description: "Review disposition, closure, task status, reconciliation, and prior attempt evidence remain unchanged."
                }]
        }
    };
}
function createNonCallableCompletionRecoveryAction(reason) {
    return {
        id: "recovery.task.implementation-complete.unavailable",
        callable: false,
        explanation: "Correct the reported implementation-completion blockers before requesting a fresh revision-bound preview.",
        command: "task implementation-complete",
        argv: null,
        writeCapable: null,
        effects: null,
        authorization: null,
        confirmation: null,
        selectionRequired: null,
        idempotency: null,
        preconditions: [{
                id: "complete-recovery-inputs",
                description: "Every value required for a safe recovery invocation is available from current evidence.",
                status: "blocked",
                expected: "exact current attempt, current task revision, and complete lifecycle and validation evidence",
                observed: null
            }],
        transition: {
            id: "recovery.task.implementation-complete.unavailable:state-observed",
            kind: "state-observed",
            expectedPostconditions: [{
                    id: "guidance-preserved",
                    description: "Typed guidance is reported without claiming that recovery occurred."
                }]
        },
        nonCallableReason: reason
    };
}
function cloneRecoveryAction(action) {
    return {
        ...action,
        argv: action.argv === null ? null : [...action.argv],
        effects: action.effects === null ? null : { ...action.effects },
        preconditions: action.preconditions.map((entry) => ({ ...entry })),
        transition: {
            ...action.transition,
            expectedPostconditions: action.transition.expectedPostconditions
                .map((entry) => ({ ...entry }))
        }
    };
}
function resolvePair(repositoryRoot, taskDefinitionPath) {
    const rootText = requireText(repositoryRoot, "Repository root");
    let root;
    try {
        root = node_fs_1.default.realpathSync(node_path_1.default.resolve(rootText));
    }
    catch (error) {
        throw new TaskImplementationCompletionError("artifact-unreadable", `Repository root is unreadable: ${error instanceof Error ? error.message : String(error)}`);
    }
    let parsed;
    try {
        parsed = (0, paths_1.parseRepositoryRelativePath)(taskDefinitionPath);
    }
    catch (error) {
        throw new TaskImplementationCompletionError("invalid-input", error instanceof Error ? error.message : "Invalid task path.");
    }
    if (parsed.segments.at(-1) !== "task-definition.md") {
        throw new TaskImplementationCompletionError("invalid-input", "Implementation-complete paths must end in task-definition.md.");
    }
    const task = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(root, parsed.normalizedPath);
    const summary = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(root, [...parsed.segments.slice(0, -1), "implementation-summary.md"].join("/"));
    if (!task.exists || !summary.exists) {
        throw new TaskImplementationCompletionError("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 TaskImplementationCompletionError("artifact-unreadable", `Completion artifact '${repositoryRelativePath}' 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 TaskImplementationCompletionError("artifact-unreadable", `Completion artifact '${repositoryRelativePath}' must contain valid UTF-8: ${error instanceof Error ? error.message : String(error)}`);
    }
}
function requireAttemptId(value) {
    const attemptId = requireText(value, "Attempt identity");
    if (!ATTEMPT_ID_PATTERN.test(attemptId)) {
        throw new TaskImplementationCompletionError("invalid-input", "Attempt identity must be 3-128 machine-safe characters.");
    }
    return attemptId;
}
function requireTaskRevision(value) {
    const revision = requireText(value, "Task revision");
    if (!TASK_REVISION_PATTERN.test(revision)) {
        throw new TaskImplementationCompletionError("invalid-input", "Task revision must be an exact lowercase 64-character SHA-256 revision.");
    }
    return revision;
}
function requireTimestamp(value) {
    const timestamp = requireText(value, "Completion timestamp");
    if (!(0, task_lifecycle_1.isTaskLifecycleTimestamp)(timestamp)) {
        throw new TaskImplementationCompletionError("invalid-input", "Completion timestamp must be ISO-8601 with an explicit timezone.");
    }
    return (0, task_lifecycle_timestamps_1.requireObservedLifecycleTimestamp)({
        value: timestamp,
        field: "completed_at",
        label: "Completion timestamp"
    });
}
function requireText(value, label) {
    if (typeof value !== "string" || value.trim() === "") {
        throw new TaskImplementationCompletionError("invalid-input", `${label} must be non-empty.`);
    }
    return value.trim();
}
function sha256(contents) {
    return (0, node_crypto_1.createHash)("sha256").update(contents).digest("hex");
}
