"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LIFECYCLE_RUN_ANALYSIS_SCHEMA_VERSION = void 0;
exports.explainLifecycleRun = explainLifecycleRun;
exports.reportLifecycleRunFriction = reportLifecycleRunFriction;
exports.compareLifecycleRuns = compareLifecycleRuns;
const node_crypto_1 = require("node:crypto");
const agent_trajectory_budget_1 = require("../agent-trajectory-budget");
const decision_trace_1 = require("../evidence-freshness/decision-trace");
const lifecycle_run_context_1 = require("../lifecycle-run-context");
const evidence_store_1 = require("../lifecycle-run-context/evidence-store");
exports.LIFECYCLE_RUN_ANALYSIS_SCHEMA_VERSION = 1;
function explainLifecycleRun(options) {
    const { repositoryRoot, inspection, selector } = options;
    const observations = inspectEvidence(repositoryRoot, inspection);
    const selected = observations.filter((observation) => matchesSelector(observation, selector));
    const divergence = selected.find(isDivergence) ?? null;
    const causalEdges = collectCausalEdges(inspection, observations);
    const priorPrerequisite = divergence === null
        ? unsupportedConclusion('No supported divergence was selected.')
        : explicitPrerequisite(divergence, observations, inspection.manifestPath);
    const invalidated = divergence === null
        ? []
        : causalEdges.filter((edge) => {
            const target = inspection.manifest.entries.find((entry) => entry.entryId === edge.toEntryId);
            return (edge.toEntryId === divergence.entry.entryId ||
                (target !== undefined && target.sequence > divergence.entry.sequence));
        });
    const status = (0, lifecycle_run_context_1.projectLifecycleRunStatus)(repositoryRoot, inspection);
    const evidenceReferences = unique([
        inspection.manifestPath,
        ...selected.map((observation) => observation.entry.evidencePath),
        ...causalEdges.flatMap((edge) => edge.evidenceReferences),
    ]);
    const requestId = stableId('explain', inspection.manifest.runId, selector.kind, selector.value);
    return {
        schemaVersion: exports.LIFECYCLE_RUN_ANALYSIS_SCHEMA_VERSION,
        analysis: 'explain',
        readOnly: true,
        writeCapable: false,
        state: inspection.state,
        runId: inspection.manifest.runId,
        runDirectory: inspection.manifest.runDirectory,
        manifestPath: inspection.manifestPath,
        manifestHash: inspection.manifestHash,
        manifestComplete: inspection.manifestComplete,
        terminalState: inspection.terminalState,
        selector,
        selection: {
            match: selected.length === 0 ? 'not-found' : selected.length === 1 ? 'unique' : 'multiple',
            matchedEntryIds: selected.map((observation) => observation.entry.entryId),
            ambiguity: selected.length > 1
                ? 'The selector matched multiple immutable entries; the earliest supported divergence by manifest sequence is reported.'
                : null,
        },
        firstSupportedDivergence: divergence === null
            ? unsupportedConclusion('No selected entry contains an indexed CLI error or failed nested validation outcome.')
            : {
                supported: true,
                entryId: divergence.entry.entryId,
                sequence: divergence.entry.sequence,
                gate: divergence.entry.gate,
                commandIdentity: divergence.entry.commandIdentity,
                evidencePath: divergence.entry.evidencePath,
                evidenceReferences: [inspection.manifestPath, divergence.entry.evidencePath],
                basis: divergence.entry.status === 'error' ? 'cli-envelope-error' : 'nested-validation-failure',
            },
        violatedInvariant: divergence === null
            ? unsupportedConclusion('A violated invariant cannot be identified without a supported divergence.')
            : violatedInvariant(divergence),
        priorSuccessfulPrerequisite: priorPrerequisite,
        invalidatedDownstreamEvidence: invalidated.length === 0
            ? {
                supported: false,
                entries: [],
                reason: 'No manifest supersession, contract-revision invalidation, or validation-supersession edge proves downstream invalidation for the selected divergence.',
            }
            : { supported: true, entries: invalidated },
        smallestAuthoritativeRecovery: divergence === null
            ? unsupportedConclusion('No selected divergence exposes authoritative recovery evidence.')
            : authoritativeRecovery(divergence),
        validationHistory: validationHistory(inspection, observations),
        causalEdges,
        unsupportedCausalityPolicy: 'Sequence and timestamps are never treated as causal proof; unsupported or ambiguous relationships remain explicit.',
        decisionTrace: (0, decision_trace_1.createLifecycleRunAnalysisTrace)({
            commandId: 'resume.run.explain',
            requestId,
            workspaceId: inspection.manifest.runId,
            evidenceReferences,
            ruleIds: ['manifest-link-only-causality', 'nested-validation-distinct', 'read-only-history'],
            transitionIds: causalEdges.map((edge) => edge.id),
            inputCount: observations.length,
            resultCount: selected.length,
        }),
        blockers: status.blockers,
        blockerCount: status.blockerCount,
        recovery: analysisRecovery(inspection.manifest.runDirectory, 'explain', 'causalEdges', selector),
        authorityBoundary: inspection.authorityBoundary,
        noWriteBoundary: 'Explain reads verified manifest and indexed evidence bytes only; it never executes recovery or treats history as current write authority.',
    };
}
function reportLifecycleRunFriction(options) {
    const { repositoryRoot, inspection } = options;
    const observations = inspectEvidence(repositoryRoot, inspection);
    const inactiveEntryIds = collectInactiveEntryIds(inspection);
    const validations = observations
        .map((observation) => observation.validation)
        .filter((value) => value !== null);
    const resultValidations = validations.filter((validation) => validation.readable && !validation.preview);
    const supersededValidationIds = new Set(resultValidations.flatMap((validation) => validation.supersedesValidationId === null ? [] : [validation.supersedesValidationId]));
    const currentValidations = resultValidations.filter((validation) => !inactiveEntryIds.has(validation.entryId) && !supersededValidationIds.has(validation.validationId));
    const clusters = retryClusters(observations);
    const cliErrors = observations.filter((observation) => observation.entry.status === 'error');
    const nestedHistorical = countStatuses(resultValidations);
    const nestedCurrent = countStatuses(currentValidations);
    const nonCallableRecoveryCount = observations.reduce((total, observation) => total + observation.recoveryActions.filter((action) => action.callable !== true).length, 0);
    const outputBudgetPressure = observations.filter((observation) => observation.budgetStrategy !== null && observation.budgetStrategy !== 'within-budget');
    const manifestSupersessionCount = inspection.manifest.entries.filter((entry) => entry.supersedesEntryId !== null).length;
    const validationSupersessionCount = resultValidations.filter((validation) => validation.supersedesValidationId !== null).length;
    const evidenceStorage = (0, evidence_store_1.projectLifecycleEvidenceStorageMetrics)(inspection.manifest.entries);
    const evidenceBytes = evidenceStorage.logicalEntryBytes;
    const gateAttempts = [...group(observations, (observation) => observation.entry.gate).entries()]
        .map(([gate, values]) => ({
        gate,
        attempts: values.length,
        cliErrors: values.filter((value) => value.entry.status === 'error').length,
        entryIds: values.map((value) => value.entry.entryId),
        evidenceReferences: [inspection.manifestPath, ...values.map((value) => value.entry.evidencePath)],
    }))
        .sort((left, right) => left.gate.localeCompare(right.gate));
    const metricDetails = [
        metric('cli-envelope-errors', cliErrors.length, cliErrors.map((value) => value.entry.evidencePath)),
        metric('nested-validations-historical', resultValidations.length, resultValidations.map((value) => value.evidencePath)),
        metric('nested-validations-current-effective', currentValidations.length, currentValidations.map((value) => value.evidencePath)),
        metric('retry-clusters', clusters.length, clusters.flatMap((value) => value.evidenceReferences)),
        metric('gate-attempts', observations.length, [inspection.manifestPath]),
        metric('contract-revision-churn', inspection.manifest.contractRevisions.length, [inspection.manifestPath]),
        metric('manifest-supersessions', manifestSupersessionCount, [inspection.manifestPath]),
        metric('validation-supersessions', validationSupersessionCount, resultValidations.map((value) => value.evidencePath)),
        metric('non-callable-recovery-actions', nonCallableRecoveryCount, cliErrors.map((value) => value.entry.evidencePath)),
        metric('evidence-bytes', evidenceBytes, [inspection.manifestPath]),
        metric('output-budget-pressure', outputBudgetPressure.length, outputBudgetPressure.map((value) => value.entry.evidencePath)),
    ].slice(0, agent_trajectory_budget_1.LIFECYCLE_RUN_ANALYSIS_BUDGET.maxMetricDetails);
    const status = (0, lifecycle_run_context_1.projectLifecycleRunStatus)(repositoryRoot, inspection);
    const requestId = stableId('friction', inspection.manifest.runId);
    return {
        schemaVersion: exports.LIFECYCLE_RUN_ANALYSIS_SCHEMA_VERSION,
        analysis: 'friction-report',
        readOnly: true,
        writeCapable: false,
        state: inspection.state,
        runId: inspection.manifest.runId,
        runDirectory: inspection.manifest.runDirectory,
        manifestPath: inspection.manifestPath,
        manifestHash: inspection.manifestHash,
        manifestComplete: inspection.manifestComplete,
        terminalState: inspection.terminalState,
        cliEnvelopeOutcomes: {
            ok: observations.length - cliErrors.length,
            error: cliErrors.length,
            total: observations.length,
            errorEntryIds: cliErrors.map((value) => value.entry.entryId),
        },
        nestedValidationOutcomes: {
            previews: validations.filter((validation) => validation.preview).length,
            unreadable: validations.filter((validation) => !validation.readable).length,
            historical: { total: resultValidations.length, ...nestedHistorical },
            superseded: resultValidations.filter((validation) => supersededValidationIds.has(validation.validationId)).length,
            currentEffective: { total: currentValidations.length, ...nestedCurrent },
        },
        retryClusters: clusters,
        gateAttempts,
        timeToRepair: clusters.map((cluster) => ({
            clusterId: cluster.clusterId,
            observedElapsedMs: cluster.observedElapsedMs,
            supported: cluster.observedElapsedMs !== null,
            interpretation: 'Elapsed manifest observation only; never causal proof.',
            evidenceReferences: cluster.evidenceReferences,
        })),
        revisionChurn: {
            contractRevisionCount: inspection.manifest.contractRevisions.length,
            checkpointIds: inspection.manifest.contractRevisions.map((checkpoint) => checkpoint.checkpointId),
        },
        supersession: {
            manifestEntries: manifestSupersessionCount,
            validationRecords: validationSupersessionCount,
            inactiveEntryCount: inactiveEntryIds.size,
        },
        recoveryPosture: { nonCallableActionCount: nonCallableRecoveryCount },
        evidenceStorage,
        outputBudgetPressure: {
            pressuredEntryCount: outputBudgetPressure.length,
            entries: outputBudgetPressure.map((value) => ({
                entryId: value.entry.entryId,
                strategy: value.budgetStrategy,
                renderedBytes: value.budgetRenderedBytes,
                byteBudget: value.budgetByteLimit,
                evidencePath: value.entry.evidencePath,
            })),
        },
        metricDetails,
        decisionTrace: (0, decision_trace_1.createLifecycleRunAnalysisTrace)({
            commandId: 'resume.run.friction-report',
            requestId,
            workspaceId: inspection.manifest.runId,
            evidenceReferences: [inspection.manifestPath, ...observations.map((value) => value.entry.evidencePath)],
            ruleIds: ['cli-errors-separate', 'validation-current-effective', 'read-only-history'],
            inputCount: observations.length,
            resultCount: metricDetails.length,
        }),
        blockers: status.blockers,
        blockerCount: status.blockerCount,
        recovery: analysisRecovery(inspection.manifest.runDirectory, 'friction-report', 'metricDetails'),
        authorityBoundary: inspection.authorityBoundary,
        noWriteBoundary: 'Friction reporting derives semantic counts from verified immutable evidence and performs no lifecycle, recovery, or repository mutation.',
    };
}
function compareLifecycleRuns(options) {
    const left = normalizedRun(options.repositoryRoot, options.left);
    const right = normalizedRun(options.repositoryRoot, options.right);
    const nonComparable = [];
    compareDimension(nonComparable, 'schema', left.schemaVersion, right.schemaVersion);
    compareDimension(nonComparable, 'profile', left.lifecycleProfile, right.lifecycleProfile);
    compareDimension(nonComparable, 'scope', left.phasePath, right.phasePath);
    compareDimension(nonComparable, 'terminal-state', left.terminalState, right.terminalState);
    const numericFields = [
        'entryCount',
        'cliEnvelopeErrors',
        'nestedValidationFailuresHistorical',
        'nestedValidationFailuresCurrent',
        'contractRevisionCount',
        'manifestSupersessionCount',
        'validationSupersessionCount',
        'nonCallableRecoveryCount',
        'evidenceBytes',
        'outputBudgetPressureCount',
    ];
    const comparisonDetails = numericFields
        .map((field) => ({
        dimension: field,
        comparable: true,
        left: left.metrics[field],
        right: right.metrics[field],
        delta: right.metrics[field] - left.metrics[field],
        evidenceReferences: [left.manifestPath, right.manifestPath],
    }))
        .slice(0, agent_trajectory_budget_1.LIFECYCLE_RUN_ANALYSIS_BUDGET.maxComparisonDetails);
    const requestId = stableId('compare', left.runId, right.runId);
    return {
        schemaVersion: exports.LIFECYCLE_RUN_ANALYSIS_SCHEMA_VERSION,
        analysis: 'compare',
        readOnly: true,
        writeCapable: false,
        left,
        right,
        comparable: nonComparable.length === 0,
        nonComparable,
        stableDeltas: Object.fromEntries(comparisonDetails.map((detail) => [detail.dimension, detail.delta])),
        comparisonDetails,
        validationHistoryBoundary: 'Historical failed and superseded validation metrics remain distinct from current effective failures in both normalized runs.',
        decisionTrace: (0, decision_trace_1.createLifecycleRunAnalysisTrace)({
            commandId: 'resume.run.compare',
            requestId,
            workspaceId: left.runId,
            evidenceReferences: [left.manifestPath, right.manifestPath],
            ruleIds: ['normalized-manifest-comparison', 'non-comparable-explicit', 'read-only-history'],
            inputCount: 2,
            resultCount: comparisonDetails.length + nonComparable.length,
        }),
        recovery: {
            leftFull: `day-shift resume run status --run-directory ${left.runDirectory} --detail full --format json`,
            rightFull: `day-shift resume run status --run-directory ${right.runDirectory} --detail full --format json`,
            comparisonPage: `day-shift resume run compare --left-run-directory ${left.runDirectory} --right-run-directory ${right.runDirectory} --detail full --page-lane comparisonDetails --page-size 25 --format json`,
        },
        authorityBoundary: 'Both manifests are verified independently; comparison never grants current write authority to either history.',
        noWriteBoundary: 'Compare reads two verified lifecycle runs and returns normalized deltas without changing either source run or repository state.',
    };
}
function inspectEvidence(repositoryRoot, inspection) {
    return inspection.manifest.entries.slice(0, agent_trajectory_budget_1.LIFECYCLE_RUN_ANALYSIS_BUDGET.maxManifestEntries).map((entry) => {
        const envelope = readEnvelope(repositoryRoot, inspection.manifest.runDirectory, entry);
        const data = asRecord(envelope?.data);
        const error = asRecord(envelope?.error);
        const output = asRecord(envelope?.output);
        const budget = asRecord(output?.budget);
        const findings = [
            ...(Array.isArray(envelope?.findings) ? envelope.findings : []),
            ...(Array.isArray(error?.findings) ? error.findings : []),
        ]
            .map(asRecord)
            .filter((value) => value !== null);
        return {
            entry,
            errorCode: stringValue(error?.code) ?? entry.errorCode,
            findingCodes: unique([
                ...(stringValue(error?.code) === null ? [] : [stringValue(error?.code)]),
                ...findings.map((finding) => stringValue(finding.code)).filter((value) => value !== null),
            ]),
            recoveryActions: [
                ...(Array.isArray(error?.recoveryActions) ? error.recoveryActions : []),
                ...(Array.isArray(data?.recoveryActions) ? data.recoveryActions : []),
            ]
                .map(asRecord)
                .filter((value) => value !== null),
            validation: readValidation(entry, data),
            budgetStrategy: stringValue(budget?.strategy),
            budgetRenderedBytes: numberValue(budget?.renderedBytes),
            budgetByteLimit: numberValue(budget?.byteBudget),
            explicitPrerequisiteEntryId: stringValue(data?.prerequisiteEntryId),
        };
    });
}
function readEnvelope(repositoryRoot, runDirectory, entry) {
    try {
        const bytes = (0, evidence_store_1.readLifecycleEvidenceBytesSync)({
            repositoryRoot,
            runDirectory,
            entry,
            maximumBytes: 1024 * 1024,
        }).bytes;
        const document = asRecord(JSON.parse(bytes.toString('utf8')));
        return asRecord(document?.result) ?? document;
    }
    catch {
        return null;
    }
}
function readValidation(entry, data) {
    if (entry.commandIdentity !== 'task validation-record')
        return null;
    const validation = asRecord(data?.validation);
    if (validation !== null) {
        const validationId = stringValue(validation.validationId);
        const status = stringValue(validation.status);
        if (validationId !== null && status !== null) {
            return {
                entryId: entry.entryId,
                evidencePath: entry.evidencePath,
                validationId,
                supersedesValidationId: stringValue(validation.supersedesValidationId),
                status,
                preview: false,
                readable: true,
            };
        }
    }
    const preview = data?.executionPlan !== undefined || data?.writeOutcome === 'previewed';
    return {
        entryId: entry.entryId,
        evidencePath: entry.evidencePath,
        validationId: `unreadable:${entry.entryId}`,
        supersedesValidationId: null,
        status: preview ? 'preview' : 'unreadable',
        preview,
        readable: preview,
    };
}
function matchesSelector(observation, selector) {
    if (selector.kind === 'entry')
        return observation.entry.entryId === selector.value;
    if (selector.kind === 'gate')
        return observation.entry.gate === selector.value;
    return observation.findingCodes.includes(selector.value);
}
function isDivergence(observation) {
    return observation.entry.status === 'error' || observation.validation?.status === 'failed';
}
function violatedInvariant(observation) {
    if (observation.errorCode !== null) {
        return {
            supported: true,
            identity: observation.errorCode,
            basis: 'typed-error-code',
            evidenceReferences: [observation.entry.evidencePath],
        };
    }
    if (observation.validation?.status === 'failed') {
        return {
            supported: true,
            identity: `validation:${observation.validation.validationId}:failed`,
            basis: 'nested-validation-status',
            evidenceReferences: [observation.entry.evidencePath],
        };
    }
    return unsupportedConclusion('The selected evidence does not expose a typed error code or failed validation identity.');
}
function explicitPrerequisite(observation, observations, manifestPath) {
    if (observation.explicitPrerequisiteEntryId === null) {
        return unsupportedConclusion('No indexed evidence field explicitly identifies a successful prerequisite; manifest order alone is not causal proof.');
    }
    const prerequisite = observations.find((candidate) => candidate.entry.entryId === observation.explicitPrerequisiteEntryId);
    if (prerequisite === undefined || prerequisite.entry.status !== 'ok') {
        return unsupportedConclusion('The explicit prerequisite identity is absent or does not reference a successful indexed entry.');
    }
    return {
        supported: true,
        entryId: prerequisite.entry.entryId,
        evidenceReferences: [manifestPath, prerequisite.entry.evidencePath],
    };
}
function authoritativeRecovery(observation) {
    const selected = observation.recoveryActions.find((action) => action.callable === true) ?? observation.recoveryActions[0];
    if (selected === undefined) {
        return unsupportedConclusion('The divergence evidence contains no structured recovery action.');
    }
    return {
        supported: true,
        callable: selected.callable === true,
        id: stringValue(selected.id),
        command: stringValue(selected.command),
        argv: Array.isArray(selected.argv) ? selected.argv : null,
        explanation: stringValue(selected.explanation),
        evidenceReferences: [observation.entry.evidencePath],
        executed: false,
    };
}
function collectCausalEdges(inspection, observations) {
    const edges = [];
    for (const entry of inspection.manifest.entries) {
        if (entry.supersedesEntryId !== null) {
            edges.push(edge('manifest-supersession', entry.entryId, entry.supersedesEntryId, [inspection.manifestPath]));
        }
    }
    for (const checkpoint of inspection.manifest.contractRevisions) {
        for (const invalidatedEntryId of checkpoint.invalidatedEntryIds) {
            edges.push(edge('contract-revision-invalidation', checkpoint.reviewEntryId, invalidatedEntryId, [
                inspection.manifestPath,
                checkpoint.reviewEvidencePath,
            ]));
        }
    }
    const validations = observations
        .map((value) => value.validation)
        .filter((value) => value !== null && value.readable && !value.preview);
    for (const validation of validations) {
        if (validation.supersedesValidationId === null)
            continue;
        const predecessor = validations.find((candidate) => candidate.validationId === validation.supersedesValidationId);
        if (predecessor !== undefined) {
            edges.push(edge('validation-supersession', validation.entryId, predecessor.entryId, [
                validation.evidencePath,
                predecessor.evidencePath,
            ]));
        }
    }
    for (const observation of observations) {
        if (observation.explicitPrerequisiteEntryId !== null) {
            edges.push(edge('explicit-prerequisite', observation.entry.entryId, observation.explicitPrerequisiteEntryId, [
                observation.entry.evidencePath,
            ]));
        }
    }
    return edges.slice(0, agent_trajectory_budget_1.LIFECYCLE_RUN_ANALYSIS_BUDGET.maxCausalEdges);
}
function edge(kind, fromEntryId, toEntryId, evidenceReferences) {
    return {
        id: stableId(kind, fromEntryId, toEntryId),
        kind,
        fromEntryId,
        toEntryId,
        supported: true,
        evidenceReferences: unique(evidenceReferences),
        timingUsedAsProof: false,
    };
}
function validationHistory(inspection, observations) {
    const inactive = collectInactiveEntryIds(inspection);
    const records = observations
        .map((value) => value.validation)
        .filter((value) => value !== null && value.readable && !value.preview);
    const superseded = new Set(records.flatMap((record) => (record.supersedesValidationId === null ? [] : [record.supersedesValidationId])));
    const current = records.filter((record) => !inactive.has(record.entryId) && !superseded.has(record.validationId));
    return {
        historical: { total: records.length, ...countStatuses(records) },
        supersededValidationIds: [...superseded].sort(),
        currentEffective: { total: current.length, ...countStatuses(current) },
    };
}
function collectInactiveEntryIds(inspection) {
    return new Set([
        ...inspection.manifest.entries.flatMap((entry) => entry.supersedesEntryId === null ? [] : [entry.supersedesEntryId]),
        ...inspection.manifest.contractRevisions.flatMap((checkpoint) => [
            checkpoint.reviewEntryId,
            ...checkpoint.invalidatedEntryIds,
        ]),
    ]);
}
function retryClusters(observations) {
    const attemptObservations = observations.filter((observation) => !observation.entry.gate.endsWith('-preview'));
    return [
        ...group(attemptObservations, (observation) => `${semanticGate(observation.entry.gate)}\u0000${observation.entry.commandIdentity}`).entries(),
    ]
        .filter(([, values]) => values.length > 1)
        .map(([identity, values]) => {
        const [gate, commandIdentity] = identity.split('\u0000');
        const firstError = values.find((value) => isDivergence(value));
        const laterSuccess = firstError === undefined
            ? undefined
            : values.find((value) => value.entry.sequence > firstError.entry.sequence &&
                value.entry.status === 'ok' &&
                value.validation?.status !== 'failed');
        const elapsed = firstError === undefined || laterSuccess === undefined
            ? null
            : elapsedMs(firstError.entry.recordedAt, laterSuccess.entry.recordedAt);
        return {
            clusterId: stableId('retry', gate, commandIdentity),
            gate,
            commandIdentity,
            attemptCount: values.length,
            cliErrorCount: values.filter((value) => value.entry.status === 'error').length,
            nestedValidationFailureCount: values.filter((value) => value.validation?.status === 'failed').length,
            entryIds: values.map((value) => value.entry.entryId),
            observedElapsedMs: elapsed,
            causalClaim: false,
            evidenceReferences: unique(values.map((value) => value.entry.evidencePath)),
        };
    })
        .sort((left, right) => left.clusterId.localeCompare(right.clusterId));
}
function semanticGate(gate) {
    return gate.replace(/-(?:preview|apply)$/u, '').replace(/-r\d+$/u, '');
}
function normalizedRun(repositoryRoot, inspection) {
    const friction = reportLifecycleRunFriction({ repositoryRoot, inspection });
    const cli = asRecord(friction.cliEnvelopeOutcomes);
    const nested = asRecord(friction.nestedValidationOutcomes);
    const historical = asRecord(nested.historical);
    const current = asRecord(nested.currentEffective);
    const supersession = asRecord(friction.supersession);
    const recovery = asRecord(friction.recoveryPosture);
    const storage = asRecord(friction.evidenceStorage);
    const pressure = asRecord(friction.outputBudgetPressure);
    return {
        runId: inspection.manifest.runId,
        runDirectory: inspection.manifest.runDirectory,
        manifestPath: inspection.manifestPath,
        manifestHash: inspection.manifestHash,
        state: inspection.state,
        schemaVersion: inspection.manifest.schemaVersion,
        lifecycleProfile: inspection.manifest.lifecycleProfile,
        phasePath: inspection.manifest.phasePath,
        terminalState: inspection.terminalState,
        manifestComplete: inspection.manifestComplete,
        metrics: {
            entryCount: inspection.manifest.entries.length,
            cliEnvelopeErrors: numberValue(cli.error) ?? 0,
            nestedValidationFailuresHistorical: numberValue(historical.failed) ?? 0,
            nestedValidationFailuresCurrent: numberValue(current.failed) ?? 0,
            contractRevisionCount: inspection.manifest.contractRevisions.length,
            manifestSupersessionCount: numberValue(supersession.manifestEntries) ?? 0,
            validationSupersessionCount: numberValue(supersession.validationRecords) ?? 0,
            nonCallableRecoveryCount: numberValue(recovery.nonCallableActionCount) ?? 0,
            evidenceBytes: numberValue(storage.logicalEntryBytes) ?? 0,
            outputBudgetPressureCount: numberValue(pressure.pressuredEntryCount) ?? 0,
        },
    };
}
function compareDimension(destination, dimension, left, right) {
    if (left === right)
        return;
    destination.push({
        dimension,
        comparable: false,
        left,
        right,
        reason: `${dimension} differs; no equivalence is inferred.`,
    });
}
function analysisRecovery(runDirectory, command, lane, selector) {
    const selectorArgs = selector === undefined ? '' : ` --${selector.kind} ${selector.value}`;
    return {
        fullDetailCommand: `day-shift resume run ${command} --run-directory ${runDirectory}${selectorArgs} --detail full --format json`,
        pagingCommand: `day-shift resume run ${command} --run-directory ${runDirectory}${selectorArgs} --detail full --page-lane ${lane} --page-size 25 --format json`,
        sourceStatusCommand: `day-shift resume run status --run-directory ${runDirectory} --detail full --format json`,
    };
}
function metric(metricName, value, evidenceReferences) {
    return { metric: metricName, value, evidenceReferences: unique(evidenceReferences) };
}
function countStatuses(values) {
    const passed = values.filter((value) => value.status === 'passed').length;
    const failed = values.filter((value) => value.status === 'failed').length;
    return { passed, failed, other: values.length - passed - failed };
}
function elapsedMs(left, right) {
    const start = Date.parse(left);
    const end = Date.parse(right);
    return Number.isFinite(start) && Number.isFinite(end) && end >= start ? end - start : null;
}
function unsupportedConclusion(reason) {
    return { supported: false, reason, evidenceReferences: [] };
}
function stableId(...values) {
    return `analysis-${(0, node_crypto_1.createHash)('sha256').update(values.join('\u0000'), 'utf8').digest('hex').slice(0, 24)}`;
}
function group(values, key) {
    const result = new Map();
    for (const value of values) {
        const identity = key(value);
        const existing = result.get(identity) ?? [];
        existing.push(value);
        result.set(identity, existing);
    }
    return result;
}
function unique(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function asRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value)
        ? value
        : null;
}
function stringValue(value) {
    return typeof value === 'string' && value.length > 0 ? value : null;
}
function numberValue(value) {
    return typeof value === 'number' && Number.isFinite(value) ? value : null;
}
