Files
tech-log-frontend/scripts/lib/release-runtime-coherence.ts

86 lines
2.7 KiB
TypeScript

import { isVersionCompatible } from "../../src/application/policies/compatibility.ts";
import { verifyContractSet } from "../../src/contracts/contract-set.ts";
import type { InstalledContractPackageIdentity } from "../../src/contracts/external-contract-runtime.ts";
import type { ReleaseArtifact } from "../../src/contracts/release-artifacts.ts";
import { compareReleaseToRuntime } from "../../src/contracts/release-tokens.ts";
export type ReleaseRuntimeCoherenceInput = Readonly<{
release: ReleaseArtifact;
runtime: Readonly<{
BUILD_ID: string;
CONFIG_SCHEMA_VERSION: string;
API_CONTRACT_VERSION?: string;
RELEASE_ID: string;
}>;
contractPackages: readonly InstalledContractPackageIdentity[];
}>;
export type ReleaseRuntimeCoherence = Readonly<{
compatible: boolean;
mismatches: readonly string[];
warnings: readonly string[];
}>;
/**
* Verifies the runtime identity using the release schema's own contract model.
* V1 retains the scalar compatibility policy. V2 has no scalar projection: its
* identity is the exact compiled package tuple set and canonical set digest.
*/
export async function verifyReleaseRuntimeCoherence(
input: ReleaseRuntimeCoherenceInput,
): Promise<ReleaseRuntimeCoherence> {
if (input.release.schemaVersion === 1) {
if (input.runtime.API_CONTRACT_VERSION === undefined) {
return compareWithoutContractScalar(input, ["apiContractVersion"]);
}
return compareReleaseToRuntime(input.release, {
...input.runtime,
API_CONTRACT_VERSION: input.runtime.API_CONTRACT_VERSION,
});
}
const comparison = compareWithoutContractScalar(input);
const contractSet = await verifyContractSet({
expected: input.contractPackages,
manifest: input.release.contractSet,
});
if (contractSet.ok) return comparison;
const mismatches = Object.freeze([
...comparison.mismatches,
contractSet.code,
]);
return Object.freeze({
compatible: false,
mismatches,
warnings: comparison.warnings,
});
}
function compareWithoutContractScalar(
input: Pick<ReleaseRuntimeCoherenceInput, "release" | "runtime">,
initialMismatches: readonly string[] = [],
): ReleaseRuntimeCoherence {
const mismatches = [...initialMismatches];
if (input.release.buildId !== input.runtime.BUILD_ID) {
mismatches.push("buildId");
}
if (
!isVersionCompatible(
input.release.configSchemaVersion,
input.runtime.CONFIG_SCHEMA_VERSION,
)
) {
mismatches.push("configSchemaVersion");
}
const warnings =
input.release.releaseId === input.runtime.RELEASE_ID
? []
: ["releaseId"];
return Object.freeze({
compatible: mismatches.length === 0,
mismatches: Object.freeze(mismatches),
warnings: Object.freeze(warnings),
});
}