"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.projectTaskLifecycle = exports.TASK_LIFECYCLE_FIELD_OWNERS = exports.REVIEW_DISPOSITION_VALUES = exports.CURRENT_IMPLEMENTATION_STATUS_VALUES = exports.TASK_STATUS_VALUES = exports.REVIEW_DISPOSITIONS = exports.IMPLEMENTATION_STATUSES = exports.IMPLEMENTATION_SUMMARY_STATUSES = exports.TASK_READINESS_VALUES = exports.TASK_STATUSES = void 0;
exports.projectTaskLifecyclePair = projectTaskLifecyclePair;
exports.projectImplementationSummaryLifecycle = projectImplementationSummaryLifecycle;
exports.isTaskReadinessAuthorizationRevisionApplicable = isTaskReadinessAuthorizationRevisionApplicable;
exports.taskReadinessAuthorizationSubjectRevision = taskReadinessAuthorizationSubjectRevision;
exports.rebindTaskReadinessAuthorizationRevision = rebindTaskReadinessAuthorizationRevision;
exports.translatePromotedTaskReadinessAuthorization = translatePromotedTaskReadinessAuthorization;
exports.isTaskReviewRepairRevisionApplicable = isTaskReviewRepairRevisionApplicable;
exports.isTaskLifecycleTimestamp = isTaskLifecycleTimestamp;
const node_crypto_1 = require("node:crypto");
const planning_document_utils_1 = require("./planning-document-utils");
const task_waivers_1 = require("./task-waivers");
const attempt_projection_1 = require("./task-lifecycle/attempt-projection");
exports.TASK_STATUSES = ['draft', 'completed', 'cancelled'];
exports.TASK_READINESS_VALUES = ['needs_more_evidence', 'ready_to_implement', 'completed'];
exports.IMPLEMENTATION_SUMMARY_STATUSES = ['completed'];
exports.IMPLEMENTATION_STATUSES = ['pending', 'in_progress', 'completed', 'blocked'];
exports.REVIEW_DISPOSITIONS = [
    'pending',
    'changes_requested',
    'accepted',
    'accepted_with_follow_up',
    'blocked',
    'cancelled',
    'superseded',
    'contract_gap',
    'invalid_chronology',
    'waived',
];
const CONTROLLED_LIFECYCLE_KEYS = new Set([
    'artifact_type',
    'id',
    'task',
    'status',
    'readiness',
    'readiness_review_id',
    'readiness_review_revision',
    'readiness_review_protocol_version',
    'readiness_reviewed_by',
    'readiness_reviewed_at',
    'readiness_waiver_id',
    'task_waivers',
    'cancellation_actor',
    'cancellation_reason',
    'cancelled_at',
    'completed_at',
    'current_attempt_id',
    'attempts',
    'attempt_id',
    'opened_at',
    'closed_at',
    'implementation_status',
    'review_disposition',
    'blocker_reason',
    'blocker_next',
    'disposition_by',
    'disposition_at',
    'disposition_reason',
    'blocked_by',
    'blocked_at',
    'readiness_authorization_kind',
    'review_waiver_id',
    'predecessor_attempt_id',
    'successor_kind',
    'repair_reason',
    'repair_readiness_review_id',
    'repair_task_revision',
    'repair_reviewed_by',
    'repair_reviewed_at',
    'superseded_task_revision',
    'superseded_summary_revision',
    'scope_recovery_id',
    'scope_recovery_mode',
    'scope_recovery_impact_fingerprint',
    'scope_recovery_added_paths',
    'scope_recovery_added_validations',
    'scope_recovery_recorded_by',
    'scope_recovery_recorded_at',
    'scope_recovery_reason',
    'supplemental_runtime_attribution_baseline',
    'scope_recovery_history',
    'contract_revision_recovery_id',
    'predecessor_contract_revision_recovery_id',
    'contract_revision_checkpoint_id',
    'contract_revision_run_id',
    'contract_revision_old_task_revision',
    'contract_revision_new_task_revision',
    'chronology_recovery_id',
    'predecessor_chronology_recovery_id',
    'chronology_recovery_run_id',
    'chronology_recovery_task_revision',
    'chronology_observed_opened_at',
    'chronology_observed_baseline_at',
    'chronology_evidence_entry_ids',
    'chronology_evidence_hashes',
    'chronology_unrecordable_validation_ids',
    'chronology_original_closed_at',
    'chronology_effective_closed_at',
    'chronology_source_entry_id',
    'chronology_source_evidence_hash',
    'chronology_recovery_reason',
    'chronology_recovered_by',
    'chronology_recovered_at',
]);
const ATTEMPT_WRONG_OWNER_FIELDS = new Set([
    'artifact_type',
    'id',
    'task',
    'status',
    'readiness',
    'readiness_review_revision',
    'readiness_review_protocol_version',
    'readiness_reviewed_by',
    'readiness_reviewed_at',
    'cancellation_actor',
    'cancellation_reason',
    'cancelled_at',
    'current_attempt_id',
    'attempts',
]);
exports.TASK_STATUS_VALUES = exports.TASK_STATUSES;
exports.CURRENT_IMPLEMENTATION_STATUS_VALUES = exports.IMPLEMENTATION_STATUSES;
exports.REVIEW_DISPOSITION_VALUES = exports.REVIEW_DISPOSITIONS;
exports.TASK_LIFECYCLE_FIELD_OWNERS = {
    status: 'task_definition',
    readiness: 'task_definition',
    implementation_status: 'implementation_summary.current_attempt',
    review_disposition: 'implementation_summary.current_attempt',
};
/**
 * Projects lifecycle evidence without writing, normalizing, or otherwise mutating
 * either artifact. Findings are emitted in validation-rule order so identical
 * inputs always produce identical output.
 */
function projectTaskLifecyclePair(options) {
    const findings = [];
    const compatibility = [];
    const taskDocument = (0, planning_document_utils_1.parseMarkdownFrontMatter)(options.taskDefinition);
    validateControlledTopLevelKeySyntax(taskDocument.frontMatterLines, 'task_definition', findings);
    validateDocumentType(taskDocument.frontMatterLines, 'task_definition', findings);
    validateDuplicates(taskDocument.frontMatterLines, 'task_definition', [
        'artifact_type',
        'id',
        'task',
        'status',
        'readiness',
        'implementation_status',
        'review_disposition',
        'readiness_review_id',
        'readiness_review_revision',
        'readiness_review_protocol_version',
        'readiness_reviewed_by',
        'readiness_reviewed_at',
        'readiness_waiver_id',
        'cancellation_actor',
        'cancellation_reason',
        'cancelled_at',
    ], findings);
    const taskId = readTaskId(taskDocument.frontMatterLines, 'task_definition', findings, compatibility);
    const rawStatus = scalar(taskDocument.frontMatterLines, 'status');
    const rawReadiness = scalar(taskDocument.frontMatterLines, 'readiness');
    const status = projectTaskStatus(rawStatus, findings, compatibility);
    const readiness = projectTaskReadiness(rawReadiness, findings, compatibility);
    const authorization = {
        kind: null,
        id: scalar(taskDocument.frontMatterLines, 'readiness_review_id'),
        waiverId: scalar(taskDocument.frontMatterLines, 'readiness_waiver_id'),
        taskRevision: scalar(taskDocument.frontMatterLines, 'readiness_review_revision'),
        protocolVersion: scalar(taskDocument.frontMatterLines, 'readiness_review_protocol_version'),
        reviewer: scalar(taskDocument.frontMatterLines, 'readiness_reviewed_by'),
        reviewedAt: scalar(taskDocument.frontMatterLines, 'readiness_reviewed_at'),
        complete: false,
        valid: false,
        applicable: false,
    };
    const reviewAuthorizationComplete = [
        authorization.id,
        authorization.taskRevision,
        authorization.protocolVersion,
        authorization.reviewer,
        authorization.reviewedAt,
    ].every((value) => value !== null);
    const reviewAuthorizationValid = reviewAuthorizationComplete &&
        authorization.id !== null &&
        /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(authorization.id) &&
        authorization.taskRevision !== null &&
        /^[a-f0-9]{64}$/.test(authorization.taskRevision) &&
        authorization.protocolVersion === '1' &&
        authorization.reviewer !== null &&
        authorization.reviewer.trim() !== '' &&
        authorization.reviewedAt !== null &&
        isTaskLifecycleTimestamp(authorization.reviewedAt);
    const reviewAuthorizationApplicable = reviewAuthorizationValid &&
        isTaskReadinessAuthorizationRevisionApplicable(options.taskDefinition, authorization.taskRevision);
    if (reviewAuthorizationComplete)
        authorization.kind = 'review';
    const authorizationFields = [
        'readiness_review_id',
        'readiness_review_revision',
        'readiness_review_protocol_version',
        'readiness_reviewed_by',
        'readiness_reviewed_at',
    ];
    const authorizationFieldCount = authorizationFields.filter((field) => hasTopLevelField(taskDocument.frontMatterLines, field)).length;
    if (authorizationFieldCount > 0 && authorizationFieldCount < authorizationFields.length) {
        findings.push(finding('partial-readiness-authorization-envelope', 'task_definition', 'readiness_review_id', 'Readiness authorization fields must be absent together or present as one complete canonical envelope.'));
    }
    if (readiness === 'needs_more_evidence' && (authorizationFieldCount > 0 || authorization.waiverId !== null)) {
        findings.push(finding('orphaned-readiness-authorization', 'task_definition', 'readiness_review_id', 'needs_more_evidence cannot retain a readiness authorization envelope.'));
    }
    const cancellationAuditFieldPresent = ['cancellation_actor', 'cancellation_reason', 'cancelled_at'].some((field) => hasTopLevelField(taskDocument.frontMatterLines, field));
    if (status !== 'cancelled' && cancellationAuditFieldPresent) {
        findings.push(finding('orphaned-cancellation-audit', 'task_definition', 'cancellation_actor', 'Cancellation audit fields are valid only while task status is cancelled.'));
    }
    for (const wrongOwner of ['implementation_status', 'review_disposition', 'current_attempt_id', 'attempts']) {
        if (hasTopLevelField(taskDocument.frontMatterLines, wrongOwner)) {
            findings.push(finding('wrong-owner-lifecycle-field', 'task_definition', wrongOwner, `Lifecycle field '${wrongOwner}' is not owned by task-definition.md.`));
        }
    }
    for (const wrongOwner of [
        'attempt_id',
        'opened_at',
        'closed_at',
        'blocker_reason',
        'blocker_next',
        'completed_at',
        'disposition_by',
        'disposition_at',
        'disposition_reason',
        'blocked_by',
        'blocked_at',
    ]) {
        if (hasTopLevelField(taskDocument.frontMatterLines, wrongOwner)) {
            findings.push(finding('wrong-owner-lifecycle-field', 'task_definition', wrongOwner, `Lifecycle field '${wrongOwner}' is not owned by task-definition.md.`));
        }
    }
    const task = {
        taskId,
        rawStatus,
        status,
        rawReadiness,
        readiness,
        readinessAuthorization: authorization,
        cancellationActor: scalar(taskDocument.frontMatterLines, 'cancellation_actor'),
        cancellationReason: scalar(taskDocument.frontMatterLines, 'cancellation_reason'),
        cancelledAt: scalar(taskDocument.frontMatterLines, 'cancelled_at'),
    };
    let summary = null;
    if (options.implementationSummary === undefined || options.implementationSummary === null) {
        findings.push(finding('missing-implementation-summary', 'pair', 'implementation_summary', 'The task pair is missing implementation-summary.md evidence.'));
    }
    else {
        const summaryProjection = projectImplementationSummaryLifecycle(options.implementationSummary);
        summary = summaryProjection.value;
        findings.push(...summaryProjection.blockingFindings);
        compatibility.push(...summaryProjection.compatibilityEvidence);
        if (taskId !== null && summary.taskId !== null && taskId !== summary.taskId) {
            findings.push(finding('task-identity-mismatch', 'pair', 'task', `Task definition identity '${taskId}' does not match implementation summary identity '${summary.taskId}'.`));
        }
    }
    let readinessWaiverApplicable = false;
    if (authorization.waiverId !== null &&
        options.implementationSummary !== undefined &&
        options.implementationSummary !== null) {
        try {
            readinessWaiverApplicable =
                (0, task_waivers_1.findApplicableTaskWaiver)('implementation-summary.md', options.implementationSummary, {
                    waiverType: 'readiness',
                    subjectId: taskId ?? 'missing-task-identity',
                    attemptId: null,
                    evaluatedAt: options.evaluatedAt ?? new Date().toISOString(),
                    authorityRequired: true,
                    waiverId: authorization.waiverId,
                })?.applicable === true;
        }
        catch {
            readinessWaiverApplicable = false;
        }
        if (!reviewAuthorizationComplete) {
            authorization.kind = 'waiver';
            authorization.id = authorization.waiverId;
        }
    }
    authorization.complete = reviewAuthorizationComplete || authorization.waiverId !== null;
    authorization.valid =
        reviewAuthorizationValid ||
            (authorization.waiverId !== null && /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/.test(authorization.waiverId));
    authorization.applicable = reviewAuthorizationApplicable || readinessWaiverApplicable;
    validatePairInvariants(task, summary, options.reviewWaiverApplicable === true, findings);
    const currentAttempt = summary?.currentAttempt ?? null;
    if (currentAttempt?.successorKind === 'review_repair' &&
        !isTaskReviewRepairRevisionApplicable(options.taskDefinition, currentAttempt.repairTaskRevision)) {
        findings.push(finding('stale-review-repair-authorization', 'pair', 'attempts.repair_task_revision', 'The review-repair successor authorization is not bound to the exact completed predecessor task bytes.'));
    }
    const implementationAuthorized = findings.length === 0 &&
        summary !== null &&
        status === 'draft' &&
        readiness === 'ready_to_implement' &&
        authorization.complete &&
        authorization.applicable;
    const acceptedCompleted = findings.length === 0 &&
        status === 'completed' &&
        (currentAttempt?.reviewDisposition === 'accepted' ||
            currentAttempt?.reviewDisposition === 'accepted_with_follow_up');
    return {
        task,
        summary,
        implementationStatus: currentAttempt?.implementationStatus ?? null,
        reviewDisposition: currentAttempt?.reviewDisposition ?? null,
        implementationAuthorized,
        acceptedCompleted,
        terminal: status === 'completed' || status === 'cancelled',
        compatibilityEvidence: compatibility,
        blockingFindings: findings,
    };
}
exports.projectTaskLifecycle = projectTaskLifecyclePair;
function projectImplementationSummaryLifecycle(markdownText) {
    const findings = [];
    const compatibility = [];
    const value = projectSummary(markdownText, findings, compatibility);
    return { value, compatibilityEvidence: compatibility, blockingFindings: findings };
}
function projectSummary(markdownText, findings, compatibility) {
    const document = (0, planning_document_utils_1.parseMarkdownFrontMatter)(markdownText);
    const lines = document.frontMatterLines;
    validateControlledTopLevelKeySyntax(lines, 'implementation_summary', findings);
    validateDocumentType(lines, 'implementation_summary', findings);
    validateDuplicates(lines, 'implementation_summary', [
        'artifact_type',
        'id',
        'task',
        'status',
        'completed_at',
        'readiness',
        'implementation_status',
        'review_disposition',
        'current_attempt_id',
        'attempts',
    ], findings);
    const taskId = readTaskId(lines, 'implementation_summary', findings, compatibility);
    const attempts = readAttempts(lines, findings, compatibility);
    const currentAttemptId = nullableScalar(lines, 'current_attempt_id');
    if (!hasTopLevelField(lines, 'current_attempt_id') &&
        attempts.length === 0 &&
        !compatibility.some((entry) => entry.code === 'unmaterialized-legacy-attempt')) {
        compatibility.push(compatibilityEvidence('unmaterialized-legacy-attempt', 'implementation_summary', 'current_attempt_id', null, null));
    }
    let currentAttempt = null;
    if (currentAttemptId !== null) {
        const matching = attempts.filter((attempt) => attempt.attemptId === currentAttemptId);
        if (matching.length !== 1) {
            findings.push(finding('invalid-current-attempt-reference', 'implementation_summary', 'current_attempt_id', `Current attempt '${currentAttemptId}' must reference exactly one materialized attempt.`));
        }
        else {
            currentAttempt = matching[0];
        }
    }
    else if (attempts.length > 0 &&
        !isPendingSuccessorBoundary(attempts.at(-1) ?? null)) {
        findings.push(finding('missing-current-attempt-reference', 'implementation_summary', 'current_attempt_id', 'Materialized attempts require exactly one current_attempt_id reference.'));
    }
    const nonCurrentOpenAttempts = attempts.filter((attempt) => attempt.closedAt === null && attempt.attemptId !== currentAttemptId);
    if (nonCurrentOpenAttempts.length > 0) {
        findings.push(finding('open-attempt-not-current', 'implementation_summary', 'current_attempt_id', 'Every open materialized attempt must be the current_attempt_id.'));
    }
    if (currentAttemptId !== null &&
        attempts.length > 0 &&
        attempts[attempts.length - 1].attemptId !== currentAttemptId) {
        findings.push(finding('current-attempt-not-latest', 'implementation_summary', 'current_attempt_id', 'current_attempt_id must reference the last append-preserved materialized attempt.'));
    }
    for (let index = 0; index < attempts.length - 1; index += 1) {
        const predecessor = attempts[index];
        const successor = attempts[index + 1];
        const ordinaryRetry = predecessor.closedAt !== null && predecessor.reviewDisposition === 'changes_requested';
        const supersededSuccessor = predecessor.closedAt !== null &&
            predecessor.reviewDisposition === 'superseded' &&
            successor.predecessorAttemptId === predecessor.attemptId;
        const scopeRecoverySuccessor = predecessor.closedAt !== null &&
            predecessor.reviewDisposition === 'contract_gap' &&
            successor.predecessorAttemptId === predecessor.attemptId &&
            successor.successorKind === 'scope_recovery' &&
            hasNonEmptyValue(predecessor.scopeRecoveryId) &&
            successor.scopeRecoveryId === predecessor.scopeRecoveryId;
        const contractRevisionSuccessor = predecessor.closedAt !== null &&
            predecessor.reviewDisposition === 'contract_gap' &&
            successor.predecessorAttemptId === predecessor.attemptId &&
            successor.successorKind === 'contract_revision' &&
            hasNonEmptyValue(predecessor.contractRevisionRecoveryId) &&
            contractRevisionPredecessorRecoveryId(successor) === predecessor.contractRevisionRecoveryId;
        const chronologyRepairSuccessor = predecessor.closedAt !== null &&
            (predecessor.reviewDisposition === 'invalid_chronology' ||
                (predecessor.reviewDisposition === 'superseded' && predecessor.chronologyEffectiveClosedAt !== null)) &&
            successor.predecessorAttemptId === predecessor.attemptId &&
            successor.successorKind === 'chronology_repair' &&
            hasNonEmptyValue(predecessor.chronologyRecoveryId) &&
            successor.predecessorChronologyRecoveryId === predecessor.chronologyRecoveryId;
        const reviewRepair = predecessor.closedAt !== null &&
            (predecessor.reviewDisposition === 'accepted' || predecessor.reviewDisposition === 'accepted_with_follow_up') &&
            successor.predecessorAttemptId === predecessor.attemptId &&
            successor.successorKind === 'review_repair' &&
            hasNonEmptyValue(successor.repairReason) &&
            hasNonEmptyValue(successor.repairReadinessReviewId) &&
            /^[a-f0-9]{64}$/.test(successor.repairTaskRevision ?? '') &&
            hasNonEmptyValue(successor.repairReviewedBy) &&
            isValidTimestamp(successor.repairReviewedAt);
        if (!ordinaryRetry &&
            !supersededSuccessor &&
            !scopeRecoverySuccessor &&
            !contractRevisionSuccessor &&
            !chronologyRepairSuccessor &&
            !reviewRepair) {
            findings.push(finding('invalid-attempt-predecessor-lineage', 'implementation_summary', 'attempts.review_disposition', 'Every later attempt requires a closed changes_requested, superseded, contract_gap, or invalid_chronology predecessor with matching successor evidence, or complete review_repair authorization after accepted closeout.'));
        }
        if (timestampPrecedes(successor.openedAt, predecessor.chronologyEffectiveClosedAt ?? predecessor.closedAt)) {
            findings.push(finding('overlapping-attempt-chronology', 'implementation_summary', 'attempts.opened_at', 'A later attempt cannot open before its predecessor closed.'));
        }
    }
    const topLevelStatus = scalar(lines, 'status');
    const summaryStatus = projectImplementationSummaryStatus(topLevelStatus, compatibility);
    const completedAt = scalar(lines, 'completed_at');
    const topLevelReadiness = scalar(lines, 'readiness');
    const topLevelImplementation = scalar(lines, 'implementation_status');
    const topLevelReview = scalar(lines, 'review_disposition');
    const legacyImplementationStatus = legacySummaryToken(topLevelImplementation, exports.IMPLEMENTATION_STATUSES, 'implementation_status', findings);
    const legacyReviewDisposition = legacySummaryToken(topLevelReview, exports.REVIEW_DISPOSITIONS, 'review_disposition', findings);
    for (const wrongOwner of [
        'readiness_review_id',
        'readiness_review_revision',
        'readiness_review_protocol_version',
        'readiness_reviewed_by',
        'readiness_reviewed_at',
        'cancellation_actor',
        'cancellation_reason',
        'cancelled_at',
        'attempt_id',
        'opened_at',
        'closed_at',
        'blocker_reason',
        'blocker_next',
        'disposition_by',
        'disposition_at',
        'disposition_reason',
        'blocked_by',
        'blocked_at',
    ]) {
        if (hasTopLevelField(lines, wrongOwner)) {
            findings.push(finding('wrong-owner-lifecycle-field', 'implementation_summary', wrongOwner, `Lifecycle field '${wrongOwner}' is not owned by implementation-summary.md top-level evidence.`));
        }
    }
    for (const [field, value] of [['readiness', topLevelReadiness]]) {
        if (hasTopLevelField(lines, field)) {
            compatibility.push(compatibilityEvidence('legacy-summary-lifecycle-field', 'implementation_summary', field, value, null));
        }
    }
    for (const [field, value] of [
        ['implementation_status', topLevelImplementation],
        ['review_disposition', topLevelReview],
    ]) {
        if (!hasTopLevelField(lines, field))
            continue;
        compatibility.push(compatibilityEvidence('legacy-summary-current-projection', 'implementation_summary', field, value, field === 'implementation_status' ? legacyImplementationStatus : legacyReviewDisposition));
        if (currentAttempt !== null) {
            findings.push(finding('duplicate-current-lifecycle-field', 'implementation_summary', field, `Top-level '${field}' duplicates lifecycle evidence owned by the current attempt.`));
        }
    }
    if (attempts.length === 0 &&
        (hasTopLevelField(lines, 'implementation_status') || hasTopLevelField(lines, 'review_disposition')) &&
        !compatibility.some((entry) => entry.code === 'unmaterialized-legacy-attempt')) {
        compatibility.push(compatibilityEvidence('unmaterialized-legacy-attempt', 'implementation_summary', 'attempts', null, null));
    }
    return {
        taskId,
        rawStatus: topLevelStatus,
        status: summaryStatus,
        completedAt,
        currentAttemptId,
        currentAttempt,
        attempts,
        legacyImplementationStatus,
        legacyReviewDisposition,
    };
}
function readAttempts(lines, findings, compatibility) {
    return (0, attempt_projection_1.readTaskAttempts)(lines, findings, compatibility, {
        implementationStatuses: exports.IMPLEMENTATION_STATUSES,
        reviewDispositions: exports.REVIEW_DISPOSITIONS,
        wrongOwnerFields: ATTEMPT_WRONG_OWNER_FIELDS,
        canonicalToken,
        nullable,
        compatibilityEvidence,
        finding,
        unsupportedAttemptsShapeFinding,
        validateControlledAttemptKeySyntax,
        projectReadinessAuthorizationKind,
        projectSuccessorKind,
        hasNonEmptyValue,
        isValidTimestamp,
        timestampPrecedes,
    });
}
function validatePairInvariants(task, summary, reviewWaiverApplicable, findings) {
    const current = summary?.currentAttempt ?? null;
    if ((task.readiness === 'ready_to_implement' || task.readiness === 'completed') &&
        !task.readinessAuthorization.complete) {
        findings.push(finding('missing-readiness-authorization', 'task_definition', 'readiness_review_id', 'ready_to_implement requires review identity, task revision, protocol version, reviewer, and review time evidence.'));
    }
    else if ((task.readiness === 'ready_to_implement' || task.readiness === 'completed') &&
        !task.readinessAuthorization.valid) {
        findings.push(finding('invalid-readiness-authorization', 'task_definition', 'readiness_review_id', 'Readiness authorization requires a machine-safe review ID, protocol 1, 64-hex raw task revision, non-empty reviewer, and valid review time.'));
    }
    else if ((task.readiness === 'ready_to_implement' || task.readiness === 'completed') &&
        !task.readinessAuthorization.applicable) {
        findings.push(finding('stale-readiness-authorization', 'task_definition', 'readiness_review_revision', 'The stored readiness review revision does not match the reconstructed pre-authorization task bytes.'));
    }
    if (task.readiness === 'needs_more_evidence' && current !== null) {
        findings.push(finding('implementation-without-readiness', 'pair', 'readiness', 'needs_more_evidence cannot authorize an implementation attempt.'));
    }
    if (current !== null) {
        if (timestampPrecedes(current.openedAt, task.readinessAuthorization.reviewedAt)) {
            findings.push(finding('attempt-before-readiness-authorization', 'pair', 'attempts.opened_at', 'The current attempt cannot open before its task readiness authorization was reviewed.'));
        }
        if (current.readinessReviewId !== null && task.readinessAuthorization.id !== current.readinessReviewId) {
            findings.push(finding('stale-attempt-readiness-reference', 'pair', 'readiness_review_id', 'The current attempt readiness review does not match the task authorization.'));
        }
        if (current.readinessAuthorizationKind === 'waiver' &&
            (task.readinessAuthorization.kind !== 'waiver' ||
                current.readinessWaiverId !== task.readinessAuthorization.waiverId)) {
            findings.push(finding('stale-attempt-readiness-waiver-reference', 'pair', 'readiness_waiver_id', 'The current attempt readiness waiver does not match the task authorization.'));
        }
        const blocked = current.implementationStatus === 'blocked' || current.reviewDisposition === 'blocked';
        if (blocked && (!hasNonEmptyValue(current.blockerReason) || !hasNonEmptyValue(current.blockerNext))) {
            findings.push(finding('incomplete-blocker-evidence', 'implementation_summary', 'attempts.blocker_reason', 'Blocked implementation or review evidence requires a reason and next decision or dependency.'));
        }
        if (blocked && (!hasNonEmptyValue(current.blockedBy) || !isValidTimestamp(current.blockedAt))) {
            findings.push(finding('incomplete-blocker-audit', 'implementation_summary', 'attempts.blocked_by', 'Blocked implementation or review evidence requires a blocker actor and valid time.'));
        }
    }
    if (task.status === 'completed') {
        const accepted = current?.reviewDisposition === 'accepted' ||
            current?.reviewDisposition === 'accepted_with_follow_up' ||
            (current?.reviewDisposition === 'waived' && reviewWaiverApplicable);
        if (task.readiness !== 'completed' ||
            !task.readinessAuthorization.complete ||
            summary?.status !== 'completed' ||
            !isValidTimestamp(summary?.completedAt ?? null) ||
            summary?.completedAt !== current?.closedAt ||
            current?.implementationStatus !== 'completed' ||
            !accepted) {
            findings.push(finding('invalid-completed-task-state', 'pair', 'status', 'Completion requires completed task readiness and summary status, applicable readiness evidence, a completed current attempt, accepted review or an applicable review waiver, and one shared completion timestamp.'));
        }
    }
    if (summary?.status === 'completed' && !isValidTimestamp(summary.completedAt)) {
        findings.push(finding('incomplete-summary-completion-audit', 'implementation_summary', 'completed_at', 'Completed implementation-summary status requires a valid completed_at timestamp.'));
    }
    else if (summary?.status !== 'completed' && summary?.completedAt !== null && summary !== null) {
        findings.push(finding('orphaned-summary-completion-audit', 'implementation_summary', 'completed_at', 'Implementation-summary completed_at evidence requires completed summary status.'));
    }
    if (task.status === 'cancelled') {
        if (current?.reviewDisposition !== 'cancelled' ||
            !hasNonEmptyValue(task.cancellationActor) ||
            !hasNonEmptyValue(task.cancellationReason) ||
            !isValidTimestamp(task.cancelledAt)) {
            findings.push(finding('invalid-cancelled-task-state', 'pair', 'status', 'Cancellation requires matching current-attempt disposition plus actor, reason, and time audit evidence.'));
        }
        if (current !== null &&
            current.reviewDisposition === 'cancelled' &&
            hasNonEmptyValue(task.cancellationActor) &&
            hasNonEmptyValue(task.cancellationReason) &&
            task.cancelledAt !== null &&
            (task.cancellationActor !== current.dispositionBy ||
                task.cancellationReason !== current.dispositionReason ||
                task.cancelledAt !== current.dispositionAt ||
                current.closedAt !== current.dispositionAt)) {
            findings.push(finding('cancellation-audit-mismatch', 'pair', 'cancelled_at', 'Task cancellation and current-attempt cancelled disposition must record one matching actor, reason, and time event.'));
        }
    }
    else if (current?.reviewDisposition === 'cancelled') {
        findings.push(finding('cancelled-disposition-without-task-cancellation', 'pair', 'review_disposition', 'A cancelled review disposition requires an explicit task cancellation transition.'));
    }
}
/**
 * Reconstructs the reviewed task subject without mutating it: authorization and
 * terminal audit fields are removed and lifecycle owner fields return to their
 * pre-authorization draft values before the raw-byte revision is checked.
 */
function isTaskReadinessAuthorizationRevisionApplicable(taskDefinition, expectedRevision) {
    if (expectedRevision === null || !/^[a-f0-9]{64}$/.test(expectedRevision))
        return false;
    return taskReadinessAuthorizationSubjectRevision(taskDefinition) === expectedRevision;
}
/**
 * Returns the exact pre-authorization task revision reconstructed from current
 * canonical lifecycle evidence, or null when the artifact cannot be
 * reconstructed safely.
 */
function taskReadinessAuthorizationSubjectRevision(taskDefinition) {
    const newline = taskDefinition.includes('\r\n') ? '\r\n' : '\n';
    const lines = taskDefinition.split(/\r?\n/);
    const closing = lines.findIndex((line, index) => index > 0 && line === '---');
    if (lines[0] !== '---' || closing < 0)
        return null;
    const frontMatter = lines.slice(1, closing);
    const terminalStatus = normalizeToken(scalar(frontMatter, 'status'));
    const reconstructedLines = [];
    for (let index = 0; index < lines.length; index += 1) {
        const line = lines[index];
        if (index > 0 && index < closing) {
            if (/^readiness_(?:review_id|review_revision|review_protocol_version|reviewed_by|reviewed_at):/.test(line)) {
                continue;
            }
            if (terminalStatus === 'cancelled' && /^(?:cancellation_actor|cancellation_reason|cancelled_at):/.test(line)) {
                continue;
            }
            const readiness = line.match(/^(readiness:\s*)(["']?)(?:ready_to_implement|completed)\2(\s*)$/);
            if (readiness !== null) {
                reconstructedLines.push(`${readiness[1]}${readiness[2]}needs_more_evidence${readiness[2]}${readiness[3]}`);
                continue;
            }
            const status = line.match(/^(status:\s*)(["']?)(?:completed|cancelled)\2(\s*)$/);
            if (status !== null) {
                reconstructedLines.push(`${status[1]}${status[2]}draft${status[2]}${status[3]}`);
                continue;
            }
        }
        reconstructedLines.push(line);
    }
    const reconstructed = reconstructedLines.join(newline);
    return (0, node_crypto_1.createHash)('sha256').update(Buffer.from(reconstructed)).digest('hex');
}
function rebindTaskReadinessAuthorizationRevision(taskDefinition) {
    const revision = taskReadinessAuthorizationSubjectRevision(taskDefinition);
    const match = taskDefinition.match(/^(readiness_review_revision:\s*)(["']?)([a-f0-9]{64})\2(\s*)$/m);
    if (revision === null || match === null) {
        return {
            text: taskDefinition,
            previousRevision: match?.[3] ?? null,
            revision,
            changed: false,
        };
    }
    const text = taskDefinition.replace(match[0], `${match[1]}${match[2]}${revision}${match[2]}${match[4]}`);
    return {
        text,
        previousRevision: match[3],
        revision,
        changed: revision !== match[3],
    };
}
/**
 * Translates one valid source readiness authorization onto rewritten
 * destination task bytes. Invalid, stale, waiver-only, or otherwise
 * untranslatable authorization evidence fails closed instead of being copied.
 */
function translatePromotedTaskReadinessAuthorization(sourceTaskDefinition, sourceImplementationSummary, destinationTaskDefinition) {
    const source = projectTaskLifecyclePair({
        taskDefinition: sourceTaskDefinition,
        implementationSummary: sourceImplementationSummary,
    });
    const authorization = source.task.readinessAuthorization;
    if (source.task.readiness === 'needs_more_evidence') {
        if (authorization.id !== null ||
            authorization.waiverId !== null ||
            authorization.taskRevision !== null ||
            authorization.protocolVersion !== null ||
            authorization.reviewer !== null ||
            authorization.reviewedAt !== null) {
            throw new TypeError('Unready promotion source cannot retain readiness authorization fields.');
        }
        return {
            text: destinationTaskDefinition,
            action: 'preserved-unready',
            readinessReviewId: null,
            sourceRevision: null,
            destinationRevision: null,
            changed: false,
        };
    }
    if ((source.task.readiness !== 'ready_to_implement' && source.task.readiness !== 'completed') ||
        authorization.kind !== 'review' ||
        !authorization.complete ||
        !authorization.valid ||
        !authorization.applicable ||
        authorization.id === null ||
        authorization.taskRevision === null) {
        throw new TypeError('Promotion source readiness authorization is stale, invalid, waived, or not translatable.');
    }
    const rebound = rebindTaskReadinessAuthorizationRevision(destinationTaskDefinition);
    if (rebound.previousRevision !== authorization.taskRevision ||
        rebound.revision === null ||
        !isTaskReadinessAuthorizationRevisionApplicable(rebound.text, rebound.revision)) {
        throw new TypeError('Promotion destination readiness authorization could not be bound to the exact rewritten task bytes.');
    }
    return {
        text: rebound.text,
        action: 'translated-review-revision',
        readinessReviewId: authorization.id,
        sourceRevision: authorization.taskRevision,
        destinationRevision: rebound.revision,
        changed: rebound.changed,
    };
}
function isTaskReviewRepairRevisionApplicable(taskDefinition, expectedRevision) {
    if (expectedRevision === null || !/^[a-f0-9]{64}$/.test(expectedRevision))
        return false;
    const newline = taskDefinition.includes('\r\n') ? '\r\n' : '\n';
    const lines = taskDefinition.split(/\r?\n/);
    const closing = lines.findIndex((line, index) => index > 0 && line === '---');
    if (lines[0] !== '---' || closing < 0)
        return false;
    let replaced = false;
    let alreadyCompleted = false;
    const reconstructed = lines
        .map((line, index) => {
        if (index <= 0 || index >= closing)
            return line;
        const status = line.match(/^(status:\s*)(["']?)draft\2(\s*)$/);
        if (status !== null) {
            replaced = true;
            return `${status[1]}${status[2]}completed${status[2]}${status[3]}`;
        }
        if (/^status:\s*(["']?)completed\1\s*$/.test(line)) {
            alreadyCompleted = true;
        }
        const readiness = line.match(/^(readiness:\s*)(["']?)ready_to_implement\2(\s*)$/);
        if (readiness !== null) {
            return `${readiness[1]}${readiness[2]}completed${readiness[2]}${readiness[3]}`;
        }
        return line;
    })
        .join(newline);
    return ((replaced || alreadyCompleted) &&
        (0, node_crypto_1.createHash)('sha256').update(Buffer.from(reconstructed)).digest('hex') === expectedRevision);
}
function projectTaskStatus(raw, findings, compatibility) {
    const normalized = normalizeToken(raw);
    if (normalized === null) {
        findings.push(finding('missing-lifecycle-field', 'task_definition', 'status', 'Task status is missing.'));
        return null;
    }
    if (isOneOf(normalized, exports.TASK_STATUSES)) {
        if (raw !== normalized) {
            compatibility.push(compatibilityEvidence('legacy-task-status', 'task_definition', 'status', raw, normalized));
        }
        return normalized;
    }
    if (normalized === 'active' || normalized === 'blocked') {
        compatibility.push(compatibilityEvidence('legacy-task-status', 'task_definition', 'status', raw, 'draft'));
        return 'draft';
    }
    findings.push(finding('unsupported-lifecycle-token', 'task_definition', 'status', `Unsupported task status '${raw}'.`));
    return null;
}
function projectTaskReadiness(raw, findings, compatibility) {
    const normalized = normalizeToken(raw);
    if (normalized === null) {
        findings.push(finding('missing-lifecycle-field', 'task_definition', 'readiness', 'Task readiness is missing.'));
        return null;
    }
    if (isOneOf(normalized, exports.TASK_READINESS_VALUES)) {
        if (raw !== normalized) {
            compatibility.push(compatibilityEvidence('legacy-readiness-token', 'task_definition', 'readiness', raw, normalized));
        }
        return normalized;
    }
    findings.push(finding('unsupported-lifecycle-token', 'task_definition', 'readiness', `Unsupported task readiness '${raw}'.`));
    return null;
}
function projectImplementationSummaryStatus(raw, compatibility) {
    if (raw === null)
        return null;
    const normalized = normalizeToken(raw);
    if (normalized === 'completed')
        return 'completed';
    compatibility.push(compatibilityEvidence('legacy-summary-lifecycle-field', 'implementation_summary', 'status', raw, null));
    return null;
}
function canonicalToken(raw, allowed, field, findings) {
    const normalized = normalizeToken(nullable(raw));
    if (normalized === null) {
        findings.push(finding('missing-attempt-lifecycle-field', 'implementation_summary', `attempts.${field}`, `Every materialized attempt requires '${field}'.`));
        return null;
    }
    if (isOneOf(normalized, allowed))
        return normalized;
    findings.push(finding('unsupported-lifecycle-token', 'implementation_summary', `attempts.${field}`, `Unsupported ${field} '${raw}'.`));
    return null;
}
function projectReadinessAuthorizationKind(raw) {
    const normalized = normalizeToken(nullable(raw));
    if (normalized === null)
        return 'review';
    return normalized === 'review' || normalized === 'waiver' ? normalized : null;
}
function projectSuccessorKind(raw, findings) {
    const normalized = normalizeToken(nullable(raw));
    if (normalized === null)
        return null;
    if (normalized === 'review_repair' ||
        normalized === 'scope_recovery' ||
        normalized === 'contract_revision' ||
        normalized === 'chronology_repair')
        return normalized;
    findings.push(finding('unsupported-lifecycle-token', 'implementation_summary', 'attempts.successor_kind', `Unsupported successor_kind '${raw}'.`));
    return null;
}
function isPendingSuccessorBoundary(attempt) {
    if (attempt?.closedAt === null)
        return false;
    return attempt?.reviewDisposition === 'superseded' || (attempt?.reviewDisposition === 'invalid_chronology' &&
        hasNonEmptyValue(attempt.chronologyRecoveryId) &&
        hasNonEmptyValue(attempt.chronologyRecoveryRunId)) || (attempt?.reviewDisposition === 'contract_gap' &&
        hasNonEmptyValue(attempt.contractRevisionRecoveryId) &&
        hasNonEmptyValue(attempt.contractRevisionCheckpointId) &&
        hasNonEmptyValue(attempt.contractRevisionRunId) &&
        /^[a-f0-9]{64}$/.test(attempt.contractRevisionOldTaskRevision ?? '') &&
        /^[a-f0-9]{64}$/.test(attempt.contractRevisionNewTaskRevision ?? ''));
}
function contractRevisionPredecessorRecoveryId(attempt) {
    if (hasNonEmptyValue(attempt.predecessorContractRevisionRecoveryId)) {
        return attempt.predecessorContractRevisionRecoveryId;
    }
    if (attempt.successorKind === 'contract_revision' &&
        attempt.contractRevisionCheckpointId === null &&
        attempt.contractRevisionRunId === null &&
        attempt.contractRevisionOldTaskRevision === null &&
        attempt.contractRevisionNewTaskRevision === null) {
        return attempt.contractRevisionRecoveryId;
    }
    return null;
}
function legacySummaryToken(raw, allowed, field, findings) {
    const normalized = normalizeToken(raw);
    if (normalized === null)
        return null;
    if (isOneOf(normalized, allowed))
        return normalized;
    findings.push(finding('unsupported-lifecycle-token', 'implementation_summary', field, `Unsupported legacy ${field} '${raw}'.`));
    return null;
}
function validateControlledTopLevelKeySyntax(lines, artifact, findings) {
    for (const line of lines) {
        if (line === '' || /^\s/.test(line) || /^#/.test(line))
            continue;
        const explicit = line.match(/^\?\s+(.+?)\s*$/);
        if (explicit !== null) {
            const rawKey = explicit[1].trim();
            const key = decodeYamlKeySpelling(rawKey);
            findings.push(unsupportedKeySyntaxFinding(artifact, key ?? rawKey));
            continue;
        }
        const mapping = line.match(/^([^:]+):(.*)$/);
        if (mapping === null)
            continue;
        const rawKey = mapping[1];
        const key = decodeYamlKeySpelling(rawKey);
        if (isAlternativeYamlKeySyntax(rawKey)) {
            findings.push(unsupportedKeySyntaxFinding(artifact, key ?? rawKey.trim()));
        }
        else if (key !== null && CONTROLLED_LIFECYCLE_KEYS.has(key) && rawKey !== key) {
            findings.push(unsupportedKeySyntaxFinding(artifact, key));
        }
        if (key !== null &&
            CONTROLLED_LIFECYCLE_KEYS.has(key) &&
            key !== 'attempts' &&
            !isSupportedControlledScalarSyntax(mapping[2])) {
            findings.push(unsupportedScalarSyntaxFinding(artifact, key));
        }
    }
}
function validateControlledAttemptKeySyntax(line, findings) {
    const explicit = line.match(/^\s{2}(?:-\s+)?\?\s+(.+?)\s*$/) ?? line.match(/^\s{4}\?\s+(.+?)\s*$/);
    if (explicit !== null) {
        const rawKey = explicit[1].trim();
        const key = decodeYamlKeySpelling(rawKey);
        findings.push(unsupportedKeySyntaxFinding('implementation_summary', `attempts.${key ?? rawKey}`));
        return;
    }
    const item = line.match(/^\s{2}-\s+([^:]+):(.*)$/);
    const nested = line.match(/^\s{4}([^:]+):(.*)$/);
    const rawKey = item?.[1] ?? nested?.[1] ?? null;
    if (rawKey === null)
        return;
    const key = decodeYamlKeySpelling(rawKey);
    if (isAlternativeYamlKeySyntax(rawKey)) {
        findings.push(unsupportedKeySyntaxFinding('implementation_summary', `attempts.${key ?? rawKey.trim()}`));
    }
    else if (key !== null && CONTROLLED_LIFECYCLE_KEYS.has(key) && rawKey !== key) {
        findings.push(unsupportedKeySyntaxFinding('implementation_summary', `attempts.${key}`));
    }
    const rawValue = item?.[2] ?? nested?.[2];
    if (key !== null &&
        CONTROLLED_LIFECYCLE_KEYS.has(key) &&
        rawValue !== undefined &&
        !isSupportedControlledScalarSyntax(rawValue)) {
        findings.push(unsupportedScalarSyntaxFinding('implementation_summary', `attempts.${key}`));
    }
}
function isAlternativeYamlKeySyntax(rawKey) {
    const trimmed = rawKey.trim();
    return rawKey !== trimmed || containsControlCharacters(trimmed) || trimmed === '<<' || /^["'!*&?]/.test(trimmed);
}
function isSupportedControlledScalarSyntax(rawValue) {
    const trimmed = rawValue.trim();
    if (containsControlCharacters(trimmed))
        return false;
    if (trimmed === '' || trimmed === 'null' || trimmed === '~')
        return true;
    if (trimmed.startsWith('"')) {
        try {
            return typeof JSON.parse(trimmed) === 'string';
        }
        catch {
            return false;
        }
    }
    if (trimmed.startsWith("'"))
        return /^'(?:[^']|'')*'$/.test(trimmed);
    return (!/[[\]#,{}|>!&*]/.test(trimmed) &&
        !/^(?:[-?:])(?:\s|$)/.test(trimmed) &&
        !/^[%@`]/.test(trimmed) &&
        !/^(?:---|\.\.\.)(?:\s|$)/.test(trimmed) &&
        !/:(?:\s|$)/.test(trimmed));
}
function containsControlCharacters(value) {
    for (const character of value) {
        const code = character.charCodeAt(0);
        if (code <= 0x1f || code === 0x7f) {
            return true;
        }
    }
    return false;
}
function decodeYamlKeySpelling(rawKey) {
    let value = rawKey.trim();
    while (/^(?:!!\S+|!<[^>]+>|!\S+|&\S+)\s+/.test(value)) {
        value = value.replace(/^(?:!!\S+|!<[^>]+>|!\S+|&\S+)\s+/, '').trim();
    }
    if (value.startsWith('*'))
        return null;
    if (value.startsWith('"') && value.endsWith('"')) {
        try {
            const parsed = JSON.parse(value);
            return typeof parsed === 'string' ? parsed : null;
        }
        catch {
            return null;
        }
    }
    if (value.startsWith("'") && value.endsWith("'")) {
        return value.slice(1, -1).replace(/''/g, "'");
    }
    return value;
}
function unsupportedKeySyntaxFinding(artifact, field) {
    return finding('unsupported-lifecycle-key-syntax', artifact, field, `Controlled lifecycle key '${field}' must use canonical plain YAML mapping syntax.`);
}
function unsupportedScalarSyntaxFinding(artifact, field) {
    return finding('unsupported-lifecycle-scalar-syntax', artifact, field, `Controlled lifecycle field '${field}' must use one complete canonical plain or quoted scalar without YAML collection, alias, tag, block, or comment syntax.`);
}
function unsupportedAttemptsShapeFinding() {
    return finding('unsupported-attempts-shape', 'implementation_summary', 'attempts', "The attempts collection must be exact 'attempts: []' or a canonical block sequence of attempt mappings.");
}
function validateDocumentType(lines, expected, findings) {
    const actual = scalar(lines, 'artifact_type');
    if (actual !== expected) {
        findings.push(finding('artifact-type-mismatch', expected, 'artifact_type', `Expected artifact_type '${expected}', received '${actual ?? '(missing)'}'.`));
    }
}
function validateDuplicates(lines, artifact, fields, findings) {
    for (const field of fields) {
        if (topLevelValues(lines, field).length > 1) {
            findings.push(finding('duplicate-lifecycle-field', artifact, field, `Front matter field '${field}' is duplicated.`));
        }
    }
}
function readTaskId(lines, artifact, findings, compatibility) {
    const id = scalar(lines, 'id');
    const task = scalar(lines, 'task');
    if (id !== null && task !== null && id !== task) {
        findings.push(finding('conflicting-task-identity', artifact, 'id', `Artifact identity '${id}' conflicts with task slug '${task}'.`));
    }
    if (id === null && task !== null) {
        compatibility.push(compatibilityEvidence('legacy-task-identity', artifact, 'id', null, task));
    }
    const resolved = id ?? task;
    if (!hasNonEmptyValue(resolved)) {
        findings.push(finding('missing-task-identity', artifact, 'task', 'Task identity is missing.'));
    }
    return resolved;
}
function scalar(lines, field) {
    return nullable(topLevelRawValue(lines, field));
}
function nullableScalar(lines, field) {
    return nullable(topLevelRawValue(lines, field));
}
function nullable(value) {
    if (value === undefined || value === null)
        return null;
    const trimmed = value.trim();
    if (trimmed === '' || trimmed === 'null' || trimmed === '~')
        return null;
    return decodeLifecycleScalar(trimmed);
}
function decodeLifecycleScalar(rawValue) {
    if (rawValue.startsWith('"')) {
        try {
            const parsed = JSON.parse(rawValue);
            return typeof parsed === 'string' ? parsed : rawValue;
        }
        catch {
            return rawValue;
        }
    }
    if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
        return rawValue.slice(1, -1).replace(/''/g, "'");
    }
    return rawValue;
}
function topLevelRawValue(lines, field) {
    for (const line of lines) {
        if (/^\s/.test(line))
            continue;
        const match = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
        if (match?.[1] === field)
            return match[2];
    }
    return undefined;
}
function isTaskLifecycleTimestamp(value) {
    if (value === null)
        return false;
    const match = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-](\d{2}):(\d{2}))$/);
    if (match === null)
        return false;
    const year = Number(match[1]);
    const month = Number(match[2]);
    const day = Number(match[3]);
    const hour = Number(match[4]);
    const minute = Number(match[5]);
    const second = Number(match[6]);
    const offsetHour = match[8] === undefined ? 0 : Number(match[8]);
    const offsetMinute = match[9] === undefined ? 0 : Number(match[9]);
    if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59)
        return false;
    const daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate();
    return day >= 1 && day <= daysInMonth && Number.isFinite(Date.parse(value));
}
function isValidTimestamp(value) {
    return isTaskLifecycleTimestamp(value);
}
function hasNonEmptyValue(value) {
    return value !== null && value.trim() !== '';
}
function timestampPrecedes(value, baseline) {
    return (isValidTimestamp(value) &&
        isValidTimestamp(baseline) &&
        Date.parse(value) < Date.parse(baseline));
}
function normalizeToken(value) {
    return value === null
        ? null
        : value
            .trim()
            .toLowerCase()
            .replace(/[\s-]+/g, '_');
}
function isOneOf(value, allowed) {
    return allowed.includes(value);
}
function hasTopLevelField(lines, field) {
    return topLevelValues(lines, field).length > 0;
}
function topLevelValues(lines, field) {
    const values = [];
    for (const line of lines) {
        if (/^\s/.test(line))
            continue;
        const match = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
        if (match?.[1] === field)
            values.push((0, planning_document_utils_1.parseScalar)(match[2]));
    }
    return values;
}
function finding(code, artifact, field, message) {
    return { code, artifact, field, message };
}
function compatibilityEvidence(code, artifact, field, sourceValue, projectedValue) {
    return { code, artifact, field, sourceValue, projectedValue };
}
