"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TASK_CONTRACT_IMPACT_SCHEMA_VERSION = exports.TASK_CONTRACT_IMPACT_PATH = void 0;
exports.inspectTaskContractImpact = inspectTaskContractImpact;
exports.parseTaskContractImpact = parseTaskContractImpact;
exports.extractValidationCommands = extractValidationCommands;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const paths_1 = require("../paths");
exports.TASK_CONTRACT_IMPACT_PATH = '.day-shift/contract-impact.json';
exports.TASK_CONTRACT_IMPACT_SCHEMA_VERSION = 2;
const RELATIONSHIP_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const CONSUMER_KINDS = new Set([
    'mirror',
    'generated-output',
    'ledger',
    'test-partition',
    'contract',
]);
const POSTURES = new Set([
    'writable-target',
    'generated-output',
    'read-only-evidence',
    'frozen-dependency',
]);
const REQUIREMENTS = new Set(['required', 'advisory']);
const UNKNOWN_POLICIES = new Set(['advisory', 'blocking']);
function inspectTaskContractImpact(options) {
    const contractPath = exports.TASK_CONTRACT_IMPACT_PATH;
    const absolutePath = node_path_1.default.join(options.repositoryRoot, contractPath);
    const targetPaths = unique(options.targetPaths.map(canonicalRepositoryPath)).sort();
    const validationCommands = new Set(extractValidationCommands(options.validationCommandsText));
    if (!node_fs_1.default.existsSync(absolutePath)) {
        return buildInspection({
            repositoryRoot: options.repositoryRoot,
            contractState: 'absent',
            schemaVersion: null,
            contract: null,
            targetPaths,
            validationCommands,
            issues: [],
        });
    }
    let raw;
    try {
        raw = JSON.parse(node_fs_1.default.readFileSync(absolutePath, 'utf8'));
    }
    catch (error) {
        return buildInspection({
            repositoryRoot: options.repositoryRoot,
            contractState: 'invalid',
            schemaVersion: null,
            contract: null,
            targetPaths,
            validationCommands,
            issues: [`${contractPath} is not valid JSON: ${errorMessage(error)}`],
        });
    }
    const parsed = parseTaskContractImpact(raw);
    return buildInspection({
        repositoryRoot: options.repositoryRoot,
        contractState: parsed.contract === null ? 'invalid' : 'valid',
        schemaVersion: isObject(raw) && Number.isInteger(raw.schemaVersion) ? Number(raw.schemaVersion) : null,
        contract: parsed.contract,
        targetPaths,
        validationCommands,
        issues: parsed.issues,
    });
}
function parseTaskContractImpact(raw) {
    const issues = [];
    if (!isObject(raw)) {
        return { contract: null, issues: ['Contract root must be a JSON object.'] };
    }
    const schemaVersion = raw.schemaVersion;
    if (schemaVersion !== 1 && schemaVersion !== exports.TASK_CONTRACT_IMPACT_SCHEMA_VERSION) {
        issues.push(`schemaVersion must be 1 or ${exports.TASK_CONTRACT_IMPACT_SCHEMA_VERSION}.`);
    }
    const unknownPolicy = schemaVersion === 1 ? 'advisory' : parseUnknownPolicy(raw.unknownPolicy, 'unknownPolicy', issues);
    if (!Array.isArray(raw.relationships)) {
        issues.push('relationships must be an array.');
        return { contract: null, issues };
    }
    const relationships = [];
    const relationshipIds = new Set();
    const producerPaths = new Set();
    raw.relationships.forEach((entry, index) => {
        const location = `relationships[${index}]`;
        if (!isObject(entry)) {
            issues.push(`${location} must be an object.`);
            return;
        }
        const id = typeof entry.id === 'string' ? entry.id.trim() : '';
        if (!RELATIONSHIP_ID_PATTERN.test(id)) {
            issues.push(`${location}.id must be a canonical lowercase kebab identity.`);
        }
        else if (relationshipIds.has(id)) {
            issues.push(`${location}.id duplicates '${id}'.`);
        }
        else {
            relationshipIds.add(id);
        }
        const producerPath = parseContractPath(entry.producerPath, `${location}.producerPath`, issues);
        if (producerPath !== null) {
            if (producerPaths.has(producerPath)) {
                issues.push(`${location}.producerPath duplicates '${producerPath}'.`);
            }
            else {
                producerPaths.add(producerPath);
            }
        }
        const producerPosture = schemaVersion === 1
            ? 'writable-target'
            : parsePosture(entry.producerPosture, `${location}.producerPosture`, issues);
        const producerEnforcedBy = schemaVersion === 1
            ? null
            : parseEnforcer(entry.producerEnforcedBy, producerPosture, `${location}.producerEnforcedBy`, issues);
        const consumers = parseConsumers(entry.consumers, location, schemaVersion === 1, issues);
        const validationCommands = parseValidationObligations(entry.validationCommands, location, issues);
        if (consumers.length === 0 && validationCommands.length === 0) {
            issues.push(`${location} must declare at least one consumer or validation command.`);
        }
        if (id !== '' && producerPath !== null && producerPosture !== null) {
            relationships.push({
                id,
                producerPath,
                producerPosture,
                producerEnforcedBy,
                consumers,
                validationCommands,
            });
        }
    });
    if (schemaVersion === 2) {
        const producersByPath = new Map(relationships.map((relationship) => [relationship.producerPath, relationship]));
        for (const relationship of relationships) {
            for (const consumer of relationship.consumers) {
                const downstream = producersByPath.get(consumer.path);
                if (downstream !== undefined &&
                    !posturesAreCompatibleAcrossRelationship(consumer.posture, downstream.producerPosture)) {
                    issues.push(`Relationship '${relationship.id}' consumer '${consumer.path}' posture '${consumer.posture}' conflicts with downstream producer posture '${downstream.producerPosture}'.`);
                }
            }
        }
    }
    if (issues.length > 0 || (schemaVersion !== 1 && schemaVersion !== 2) || unknownPolicy === null) {
        return { contract: null, issues };
    }
    return {
        contract: {
            schemaVersion,
            unknownPolicy,
            relationships,
        },
        issues: [],
    };
}
function extractValidationCommands(markdownText) {
    const commands = [];
    const fenced = /(?:^|\n)(?:```|~~~)(?:sh|bash|shell)?\s*\n([\s\S]*?)\n(?:```|~~~)(?=\n|$)/g;
    for (const match of markdownText.matchAll(fenced)) {
        for (const line of match[1].split(/\r?\n/)) {
            const command = normalizeCommand(line);
            if (command !== null)
                commands.push(command);
        }
    }
    if (commands.length === 0) {
        for (const line of markdownText.split(/\r?\n/)) {
            const command = normalizeCommand(line.replace(/^\s*[-*]\s+/, ''));
            if (command !== null)
                commands.push(command);
        }
    }
    return unique(commands);
}
function buildInspection(options) {
    const complete = [];
    const writablePaths = [];
    const generatedOutputs = [];
    const readOnlyDependencies = [];
    const frozenDependencies = [];
    const validationCommands = [];
    const enforcedBy = [];
    const immutableTargets = [];
    const knownProducers = new Set();
    const activeRelationships = [];
    const targetPathSet = new Set(options.targetPaths);
    const relationshipByProducer = new Map();
    if (options.contract !== null) {
        for (const relationship of options.contract.relationships) {
            knownProducers.add(relationship.producerPath);
            relationshipByProducer.set(relationship.producerPath, relationship);
        }
        const queue = [...options.targetPaths].sort();
        const visited = new Set();
        while (queue.length > 0) {
            const producerPath = queue.shift();
            if (visited.has(producerPath))
                continue;
            visited.add(producerPath);
            const relationship = relationshipByProducer.get(producerPath);
            if (relationship === undefined)
                continue;
            activeRelationships.push(relationship);
            for (const consumer of relationship.consumers) {
                if (consumer.requirement === 'required' &&
                    (consumer.posture === 'writable-target' || consumer.posture === 'generated-output') &&
                    relationshipByProducer.has(consumer.path) &&
                    !visited.has(consumer.path)) {
                    queue.push(consumer.path);
                    queue.sort();
                }
            }
        }
    }
    for (const relationship of activeRelationships.sort(compareRelationships)) {
        const obligations = [];
        if (targetPathSet.has(relationship.producerPath)) {
            const producerObligation = makePathObligation({
                repositoryRoot: options.repositoryRoot,
                relationship,
                path: relationship.producerPath,
                posture: relationship.producerPosture,
                enforcedBy: relationship.producerEnforcedBy,
                obligationKind: 'producer-target',
                consumerKind: null,
                requirement: 'required',
                targetPathSet,
            });
            obligations.push(producerObligation);
            pushPostureLane(producerObligation, {
                writablePaths,
                generatedOutputs,
                readOnlyDependencies,
                frozenDependencies,
            });
            if (producerObligation.posture === 'read-only-evidence' || producerObligation.posture === 'frozen-dependency')
                immutableTargets.push({ ...producerObligation, satisfied: false });
            if (relationship.producerEnforcedBy !== null) {
                enforcedBy.push(makeEnforcement(options.repositoryRoot, relationship, relationship.producerPath, relationship.producerEnforcedBy));
            }
        }
        for (const consumer of relationship.consumers) {
            const obligation = makePathObligation({
                repositoryRoot: options.repositoryRoot,
                relationship,
                path: consumer.path,
                posture: consumer.posture,
                enforcedBy: consumer.enforcedBy,
                obligationKind: 'consumer',
                consumerKind: consumer.kind,
                requirement: consumer.requirement,
                targetPathSet,
            });
            obligations.push(obligation);
            pushPostureLane(obligation, {
                writablePaths,
                generatedOutputs,
                readOnlyDependencies,
                frozenDependencies,
            });
            if (consumer.enforcedBy !== null) {
                enforcedBy.push(makeEnforcement(options.repositoryRoot, relationship, consumer.path, consumer.enforcedBy));
            }
        }
        for (const validation of relationship.validationCommands) {
            const obligation = {
                relationshipId: relationship.id,
                producerPath: relationship.producerPath,
                obligationKind: 'validation-command',
                consumerKind: null,
                posture: null,
                path: null,
                command: validation.command,
                requirement: validation.requirement,
                enforcedBy: null,
                satisfied: commandIsCovered(options.validationCommands, validation.command),
            };
            obligations.push(obligation);
            validationCommands.push(obligation);
        }
        const effectiveObligations = obligations.map((obligation) => immutableTargets.some((entry) => sameObligation(entry, obligation))
            ? { ...obligation, satisfied: false }
            : obligation);
        if (effectiveObligations.every((entry) => entry.requirement !== 'required' || entry.satisfied)) {
            complete.push({
                relationshipId: relationship.id,
                producerPath: relationship.producerPath,
                requiredObligationCount: effectiveObligations.filter((entry) => entry.requirement === 'required').length,
                advisoryObligationCount: effectiveObligations.filter((entry) => entry.requirement === 'advisory').length,
            });
        }
    }
    sortObligations(writablePaths);
    sortObligations(generatedOutputs);
    sortObligations(readOnlyDependencies);
    sortObligations(frozenDependencies);
    sortObligations(validationCommands);
    sortObligations(immutableTargets);
    enforcedBy.sort((left, right) => `${left.relationshipId}:${left.path}:${left.enforcedBy}`.localeCompare(`${right.relationshipId}:${right.path}:${right.enforcedBy}`));
    const allObligations = [
        ...writablePaths,
        ...generatedOutputs,
        ...readOnlyDependencies,
        ...frozenDependencies,
        ...validationCommands,
    ];
    const unsatisfiedRequired = allObligations.filter((entry) => entry.requirement === 'required' && !entry.satisfied);
    const missingRequired = uniqueObligations([...unsatisfiedRequired, ...immutableTargets]);
    const missingWritable = missingRequired.filter((entry) => entry.posture === 'writable-target');
    const missingGenerated = missingRequired.filter((entry) => entry.posture === 'generated-output');
    const advisory = allObligations.filter((entry) => entry.requirement === 'advisory');
    const unknown = options.targetPaths.filter((targetPath) => !knownProducers.has(targetPath)).sort();
    const unknownPolicy = options.contract?.unknownPolicy ?? 'advisory';
    const invalidMessages = options.issues.map((issue) => `Contract-impact contract is invalid: ${issue}`);
    const missingMessages = missingRequired.map(formatMissingObligation);
    const unknownMessages = options.contractState === 'valid' && unknownPolicy === 'blocking' && unknown.length > 0
        ? [
            `${unknown.length} declared target path(s) have no exact producer relationship in ${exports.TASK_CONTRACT_IMPACT_PATH}; strict contract-impact evidence is incomplete.`,
        ]
        : [];
    const blockingIssues = [...invalidMessages, ...missingMessages, ...unknownMessages];
    const findings = [
        ...invalidMessages.map((message) => ({
            code: 'TASK_CONTRACT_IMPACT_CONTRACT_INVALID',
            message,
            severity: 'error',
            path: exports.TASK_CONTRACT_IMPACT_PATH,
            recovery: 'Correct the repository-local contract with exact canonical paths and rerun task readiness-review; the review performs no repair writes.',
        })),
        ...missingRequired.map((obligation) => ({
            code: findingCodeForMissing(obligation, options.schemaVersion),
            message: formatMissingObligation(obligation),
            severity: 'error',
            path: obligation.path ?? obligation.producerPath,
            affectedPaths: unique([
                obligation.producerPath,
                ...(obligation.path === null ? [] : [obligation.path]),
                ...(obligation.enforcedBy === null ? [] : [obligation.enforcedBy]),
            ]).map((affectedPath) => ({
                role: 'declared-target',
                path: affectedPath,
                description: affectedPath === obligation.producerPath
                    ? `Producer for contract-impact relationship '${obligation.relationshipId}'.`
                    : `Contract-impact evidence for relationship '${obligation.relationshipId}'.`,
            })),
            recovery: recoveryForMissing(obligation),
        })),
        ...(advisory.some((entry) => !entry.satisfied)
            ? [
                {
                    code: 'TASK_CONTRACT_IMPACT_ADVISORY',
                    message: `${advisory.filter((entry) => !entry.satisfied).length} advisory contract-impact obligation(s) are not satisfied; no coverage is inferred.`,
                    severity: 'warning',
                    path: exports.TASK_CONTRACT_IMPACT_PATH,
                },
            ]
            : []),
        ...(options.contractState === 'valid' && unknown.length > 0
            ? [
                {
                    code: unknownPolicy === 'blocking' ? 'TASK_CONTRACT_IMPACT_UNKNOWN_BLOCKED' : 'TASK_CONTRACT_IMPACT_UNKNOWN',
                    message: unknownPolicy === 'blocking'
                        ? unknownMessages[0]
                        : `${unknown.length} declared target path(s) have no exact producer relationship in ${exports.TASK_CONTRACT_IMPACT_PATH}; impact remains unknown rather than inferred.`,
                    severity: unknownPolicy === 'blocking' ? 'error' : 'warning',
                    path: exports.TASK_CONTRACT_IMPACT_PATH,
                    affectedPaths: unknown.map((unknownPath) => ({
                        role: 'declared-target',
                        path: unknownPath,
                        description: 'No exact repository-declared contract-impact relationship applies.',
                    })),
                    recovery: unknownPolicy === 'blocking'
                        ? `Add exact producer relationships for every listed path to ${exports.TASK_CONTRACT_IMPACT_PATH}, then rerun the same task readiness-review command. The review performs no repair writes.`
                        : undefined,
                },
            ]
            : []),
    ];
    const fingerprint = hashCanonical({
        contractState: options.contractState,
        schemaVersion: options.schemaVersion,
        unknownPolicy,
        contract: options.contract,
        targetPaths: [...options.targetPaths].sort(),
        validationCommands: [...options.validationCommands].sort(),
        complete,
        missingRequired,
        writablePaths,
        generatedOutputs,
        readOnlyDependencies,
        frozenDependencies,
        enforcedBy,
        advisory,
        unknown,
        issues: options.issues,
    });
    return {
        contractPath: exports.TASK_CONTRACT_IMPACT_PATH,
        contractState: options.contractState,
        schemaVersion: options.schemaVersion,
        unknownPolicy,
        fingerprint,
        activeRelationshipCount: activeRelationships.length,
        complete,
        missingRequired,
        missingWritable,
        missingGenerated,
        writablePaths,
        generatedOutputs,
        readOnlyDependencies,
        frozenDependencies,
        validationCommands,
        enforcedBy,
        immutableTargets,
        advisory,
        unknown,
        issues: options.issues,
        blockingIssues,
        findings,
        requiredComplete: blockingIssues.length === 0,
        readOnly: true,
    };
}
function parseConsumers(raw, location, legacy, issues) {
    if (!Array.isArray(raw)) {
        issues.push(`${location}.consumers must be an array.`);
        return [];
    }
    const consumers = [];
    const identities = new Set();
    raw.forEach((entry, index) => {
        const entryLocation = `${location}.consumers[${index}]`;
        if (!isObject(entry)) {
            issues.push(`${entryLocation} must be an object.`);
            return;
        }
        const consumerPath = parseContractPath(entry.path, `${entryLocation}.path`, issues);
        const kind = typeof entry.kind === 'string' && CONSUMER_KINDS.has(entry.kind)
            ? entry.kind
            : null;
        if (kind === null)
            issues.push(`${entryLocation}.kind is unsupported.`);
        const posture = legacy && kind !== null ? legacyPosture(kind) : parsePosture(entry.posture, `${entryLocation}.posture`, issues);
        if (kind === 'generated-output' && posture !== null && posture !== 'generated-output') {
            issues.push(`${entryLocation}.posture must be 'generated-output' when kind is 'generated-output'.`);
        }
        if (kind !== null && kind !== 'generated-output' && posture === 'generated-output') {
            issues.push(`${entryLocation}.posture 'generated-output' requires kind 'generated-output'.`);
        }
        const requirement = parseRequirement(entry.requirement, `${entryLocation}.requirement`, issues);
        const enforcedBy = legacy ? null : parseEnforcer(entry.enforcedBy, posture, `${entryLocation}.enforcedBy`, issues);
        if (consumerPath !== null && kind !== null && posture !== null && requirement !== null) {
            const identity = `${kind}:${consumerPath}`;
            if (identities.has(identity))
                issues.push(`${entryLocation} duplicates '${identity}'.`);
            identities.add(identity);
            consumers.push({ path: consumerPath, kind, posture, requirement, enforcedBy });
        }
    });
    return consumers;
}
function parseValidationObligations(raw, location, issues) {
    if (!Array.isArray(raw)) {
        issues.push(`${location}.validationCommands must be an array.`);
        return [];
    }
    const validations = [];
    const commands = new Set();
    raw.forEach((entry, index) => {
        const entryLocation = `${location}.validationCommands[${index}]`;
        if (!isObject(entry)) {
            issues.push(`${entryLocation} must be an object.`);
            return;
        }
        const command = typeof entry.command === 'string' ? normalizeCommandValue(entry.command) : '';
        if (command === '' || command.includes('\n')) {
            issues.push(`${entryLocation}.command must be one exact non-empty command line.`);
        }
        const requirement = parseRequirement(entry.requirement, `${entryLocation}.requirement`, issues);
        if (command !== '' && requirement !== null) {
            if (commands.has(command))
                issues.push(`${entryLocation}.command duplicates '${command}'.`);
            commands.add(command);
            validations.push({ command, requirement });
        }
    });
    return validations;
}
function parseRequirement(raw, location, issues) {
    if (typeof raw !== 'string' || !REQUIREMENTS.has(raw)) {
        issues.push(`${location} must be 'required' or 'advisory'.`);
        return null;
    }
    return raw;
}
function parsePosture(raw, location, issues) {
    if (typeof raw !== 'string' || !POSTURES.has(raw)) {
        issues.push(`${location} must be 'writable-target', 'generated-output', 'read-only-evidence', or 'frozen-dependency'.`);
        return null;
    }
    return raw;
}
function parseUnknownPolicy(raw, location, issues) {
    if (typeof raw !== 'string' || !UNKNOWN_POLICIES.has(raw)) {
        issues.push(`${location} must be 'advisory' or 'blocking'.`);
        return null;
    }
    return raw;
}
function parseEnforcer(raw, posture, location, issues) {
    if (posture !== 'frozen-dependency' && posture !== 'writable-target') {
        if (raw !== undefined && raw !== null && raw !== '') {
            issues.push(`${location} is only valid for posture 'frozen-dependency' or 'writable-target'.`);
        }
        return null;
    }
    if (posture === 'writable-target' && (raw === undefined || raw === null || raw === '')) {
        return null;
    }
    return parseContractPath(raw, location, issues);
}
function posturesAreCompatibleAcrossRelationship(consumerPosture, downstreamProducerPosture) {
    return (consumerPosture === downstreamProducerPosture ||
        (consumerPosture === 'frozen-dependency' && downstreamProducerPosture === 'writable-target'));
}
function parseContractPath(raw, location, issues) {
    if (typeof raw !== 'string' || raw.trim() === '') {
        issues.push(`${location} must be a non-empty repository-relative path.`);
        return null;
    }
    try {
        return canonicalRepositoryPath(raw);
    }
    catch (error) {
        issues.push(`${location} is invalid: ${errorMessage(error)}`);
        return null;
    }
}
function makePathObligation(options) {
    const present = node_fs_1.default.existsSync(node_path_1.default.join(options.repositoryRoot, options.path));
    const enforcerPresent = options.enforcedBy === null || node_fs_1.default.existsSync(node_path_1.default.join(options.repositoryRoot, options.enforcedBy));
    const satisfied = options.obligationKind === 'producer-target'
        ? options.posture === 'writable-target' || options.posture === 'generated-output'
        : options.posture === 'writable-target' || options.posture === 'generated-output'
            ? options.targetPathSet.has(options.path)
            : present && enforcerPresent;
    return {
        relationshipId: options.relationship.id,
        producerPath: options.relationship.producerPath,
        obligationKind: options.obligationKind,
        consumerKind: options.consumerKind,
        posture: options.posture,
        path: options.path,
        command: null,
        requirement: options.requirement,
        enforcedBy: options.enforcedBy,
        satisfied,
    };
}
function makeEnforcement(repositoryRoot, relationship, dependencyPath, enforcedBy) {
    return {
        relationshipId: relationship.id,
        producerPath: relationship.producerPath,
        path: dependencyPath,
        enforcedBy,
        dependencyPresent: node_fs_1.default.existsSync(node_path_1.default.join(repositoryRoot, dependencyPath)),
        enforcerPresent: node_fs_1.default.existsSync(node_path_1.default.join(repositoryRoot, enforcedBy)),
    };
}
function pushPostureLane(obligation, lanes) {
    if (obligation.posture === 'writable-target')
        lanes.writablePaths.push(obligation);
    if (obligation.posture === 'generated-output')
        lanes.generatedOutputs.push(obligation);
    if (obligation.posture === 'read-only-evidence')
        lanes.readOnlyDependencies.push(obligation);
    if (obligation.posture === 'frozen-dependency')
        lanes.frozenDependencies.push(obligation);
}
function findingCodeForMissing(obligation, schemaVersion) {
    if (schemaVersion === 1)
        return 'TASK_CONTRACT_IMPACT_REQUIRED_MISSING';
    if (obligation.obligationKind === 'validation-command') {
        return 'TASK_CONTRACT_IMPACT_VALIDATION_REQUIRED_MISSING';
    }
    if (obligation.obligationKind === 'producer-target') {
        return 'TASK_CONTRACT_IMPACT_IMMUTABLE_TARGET';
    }
    if (obligation.posture === 'writable-target') {
        return 'TASK_CONTRACT_IMPACT_WRITABLE_REQUIRED_MISSING';
    }
    if (obligation.posture === 'generated-output') {
        return 'TASK_CONTRACT_IMPACT_GENERATED_REQUIRED_MISSING';
    }
    return 'TASK_CONTRACT_IMPACT_IMMUTABLE_DEPENDENCY_MISSING';
}
function recoveryForMissing(obligation) {
    if (obligation.obligationKind === 'validation-command') {
        return `Declare the exact validation command '${obligation.command}' in the task contract, then obtain fresh readiness evidence.`;
    }
    if (obligation.obligationKind === 'producer-target') {
        return `Remove immutable path '${obligation.path}' from Implementation Target Paths, or change its posture only through an explicit ${exports.TASK_CONTRACT_IMPACT_PATH} contract revision, then obtain fresh readiness evidence.`;
    }
    if (obligation.posture === 'read-only-evidence' || obligation.posture === 'frozen-dependency') {
        return obligation.enforcedBy === null
            ? `Restore immutable dependency '${obligation.path}' or explicitly revise ${exports.TASK_CONTRACT_IMPACT_PATH}; do not add it as writable task scope.`
            : `Restore immutable dependency '${obligation.path}' and enforcing contract '${obligation.enforcedBy}', or explicitly revise ${exports.TASK_CONTRACT_IMPACT_PATH}; do not add either path as writable task scope.`;
    }
    return `Revise the task contract to declare '${obligation.path}', then obtain fresh readiness evidence before implementation.`;
}
function formatMissingObligation(obligation) {
    if (obligation.obligationKind === 'validation-command') {
        return `Contract-impact relationship '${obligation.relationshipId}' requires validation command '${obligation.command}' for declared producer '${obligation.producerPath}'.`;
    }
    if (obligation.obligationKind === 'producer-target') {
        return `Contract-impact relationship '${obligation.relationshipId}' classifies declared target '${obligation.path}' as '${obligation.posture}' and therefore not writable implementation scope.`;
    }
    if (obligation.posture === 'read-only-evidence' || obligation.posture === 'frozen-dependency') {
        return `Contract-impact relationship '${obligation.relationshipId}' requires present ${obligation.posture} '${obligation.path}'${obligation.enforcedBy === null ? '' : ` enforced by '${obligation.enforcedBy}'`} for declared producer '${obligation.producerPath}'.`;
    }
    return `Contract-impact relationship '${obligation.relationshipId}' requires consumer '${obligation.path}' for declared producer '${obligation.producerPath}'.`;
}
function legacyPosture(kind) {
    return kind === 'generated-output' ? 'generated-output' : 'writable-target';
}
function compareRelationships(left, right) {
    return `${left.producerPath}:${left.id}`.localeCompare(`${right.producerPath}:${right.id}`);
}
function sortObligations(obligations) {
    obligations.sort((left, right) => obligationIdentity(left).localeCompare(obligationIdentity(right)));
}
function uniqueObligations(obligations) {
    const seen = new Set();
    const result = [];
    for (const obligation of obligations) {
        const identity = obligationIdentity(obligation);
        if (seen.has(identity))
            continue;
        seen.add(identity);
        result.push(obligation);
    }
    sortObligations(result);
    return result;
}
function sameObligation(left, right) {
    return obligationIdentity(left) === obligationIdentity(right);
}
function obligationIdentity(obligation) {
    return [
        obligation.relationshipId,
        obligation.producerPath,
        obligation.obligationKind,
        obligation.posture ?? '',
        obligation.path ?? '',
        obligation.command ?? '',
    ].join(':');
}
function canonicalRepositoryPath(value) {
    return (0, paths_1.parseRepositoryRelativePath)(value).normalizedPath;
}
function normalizeCommand(line) {
    const trimmed = line.trim().replace(/^\$\s+/, '');
    if (trimmed === '' || trimmed.startsWith('#'))
        return null;
    return normalizeCommandValue(trimmed);
}
function normalizeCommandValue(value) {
    return value.trim().replace(/\s+/g, ' ');
}
function commandIsCovered(candidates, requiredCommand) {
    const required = normalizeCommandValue(requiredCommand);
    if (candidates.has(required))
        return true;
    const requiredTokens = required.split(' ');
    return [...candidates].some((candidate) => {
        const candidateTokens = new Set(candidate.split(' '));
        return requiredTokens.every((token) => candidateTokens.has(token));
    });
}
function hashCanonical(value) {
    return `sha256:${(0, node_crypto_1.createHash)('sha256')
        .update(JSON.stringify(sortCanonical(value)))
        .digest('hex')}`;
}
function sortCanonical(value) {
    if (Array.isArray(value))
        return value.map(sortCanonical);
    if (!isObject(value))
        return value;
    return Object.fromEntries(Object.keys(value)
        .sort()
        .map((key) => [key, sortCanonical(value[key])]));
}
function unique(values) {
    return [...new Set(values)];
}
function isObject(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function errorMessage(error) {
    return error instanceof Error ? error.message : String(error);
}
