fix: unify release runtime coherence verification

This commit is contained in:
DongHyeonka
2026-08-02 03:38:49 +09:00
parent 184bd98d92
commit 990603e24a
5 changed files with 406 additions and 98 deletions
+201
View File
@@ -1,10 +1,103 @@
import { describe, expect, it } from "vitest";
import { verifyRollbackReleaseCoherence } from "../../scripts/drill-runbook.ts";
import { verifyReleaseRuntimeCoherence } from "../../scripts/lib/release-runtime-coherence.ts";
import type { InstalledContractPackageIdentity } from "../../src/contracts/external-contract-runtime.ts";
import { computeContractSetDigest } from "../../src/contracts/contract-set-canonical.ts";
import type {
ReleaseArtifact,
RuntimeConfigArtifact,
} from "../../src/contracts/release-artifacts.ts";
import {
compareReleaseToRuntime,
RELEASE_TOKEN_REGISTRY,
} from "../../src/contracts/release-tokens.ts";
const contractPackages = [
{
packageId: "@example/accounts",
version: "1.2.3",
digest: `sha256:${"1".repeat(64)}`,
runtimeProtocolVersion: 1,
sourceRevision: "a".repeat(40),
},
{
packageId: "@example/billing",
version: "2.3.4",
digest: `sha256:${"2".repeat(64)}`,
runtimeProtocolVersion: 1,
sourceRevision: "b".repeat(40),
},
] as const satisfies readonly InstalledContractPackageIdentity[];
const releaseV1 = {
schemaVersion: 1,
appVersion: "1.0.0",
buildId: "build-a",
commitSha: "abc1234",
configSchemaVersion: "1",
apiContractVersion: "1.4.0",
assetManifestHash: "assets-a",
releaseId: "release-a",
builtAt: "2026-08-01T00:00:00.000Z",
routeChunks: {},
} as const satisfies ReleaseArtifact;
const runtimeV1 = {
APP_ENV: "local",
API_BASE_URL: "http://localhost:8080/",
REQUEST_TIMEOUT_MS: 10_000,
MAX_RETRY_ATTEMPTS: 2,
TELEMETRY_ENABLED: false,
AUTH_MODE: "external",
CONFIG_SCHEMA_VERSION: "1",
API_CONTRACT_VERSION: "1.6.0",
RELEASE_MANIFEST_URL: "/release-manifest.json",
BUILD_ID: "build-a",
RELEASE_ID: "release-a",
} as const satisfies RuntimeConfigArtifact;
const runtimeV2 = {
APP_ENV: "local",
API_BASE_URL: "http://localhost:8080/",
REQUEST_TIMEOUT_MS: 10_000,
MAX_RETRY_ATTEMPTS: 2,
TELEMETRY_ENABLED: false,
AUTH_MODE: "external",
CONFIG_SCHEMA_VERSION: "2.0",
RELEASE_MANIFEST_URL: "/release-manifest.json",
BUILD_ID: "build-a",
RELEASE_ID: "release-a",
CAPABILITY_OVERRIDES: {
REALTIME: "DEFAULT",
WEB_WORKER: "DEFAULT",
SERVICE_WORKER: "DEFAULT",
OFFLINE_COMMANDS: "DEFAULT",
},
} as const satisfies RuntimeConfigArtifact;
async function releaseV2With(
packages: readonly InstalledContractPackageIdentity[],
setDigest?: `sha256:${string}`,
): Promise<ReleaseArtifact> {
return {
schemaVersion: 2,
appVersion: "1.0.0",
buildId: "build-a",
commitSha: "abc1234",
configSchemaVersion: "2.0",
assetManifestHash: "assets-a",
releaseId: "release-a",
builtAt: "2026-08-01T00:00:00.000Z",
routeChunks: {},
contractSet: {
setAlgorithm: "CA_CONTRACT_SET_V1",
setDigest: setDigest ?? (await computeContractSetDigest(packages)),
packages: [...packages],
},
};
}
describe("release coherence", () => {
it("owns all nine release tokens and keeps builtAt diagnostic-only", () => {
// §5.2 adds contractSetDigest beside the legacy apiContractVersion scalar.
@@ -55,4 +148,112 @@ describe("release coherence", () => {
mismatches: ["buildId", "configSchemaVersion", "apiContractVersion"],
});
});
it("gives release verification and rollback drills the same V1/V2 tamper verdicts", async () => {
const packageAdded = [
...contractPackages,
{
packageId: "@example/notifications",
version: "3.0.0",
digest: `sha256:${"3".repeat(64)}`,
runtimeProtocolVersion: 1,
sourceRevision: "c".repeat(40),
},
] as const satisfies readonly InstalledContractPackageIdentity[];
const packageRemoved = contractPackages.slice(0, 1);
const versionChanged = [
{ ...contractPackages[0], version: "1.2.4" },
contractPackages[1],
] as const satisfies readonly InstalledContractPackageIdentity[];
const packageDigestChanged = [
{
...contractPackages[0],
digest: `sha256:${"f".repeat(64)}`,
},
contractPackages[1],
] as const satisfies readonly InstalledContractPackageIdentity[];
const exactV2 = await releaseV2With(contractPackages);
const matrix = [
{
name: "V1 scalar success",
release: releaseV1,
runtime: runtimeV1,
expectedCompatible: true,
},
{
name: "V1 scalar mismatch",
release: releaseV1,
runtime: { ...runtimeV1, API_CONTRACT_VERSION: "2.0.0" },
expectedCompatible: false,
},
{
name: "V2 exact package set",
release: exactV2,
runtime: runtimeV2,
expectedCompatible: true,
},
{
name: "V2 exact package set in non-canonical manifest order",
release: await releaseV2With([...contractPackages].reverse()),
runtime: runtimeV2,
expectedCompatible: true,
},
{
name: "V2 package added",
release: await releaseV2With(packageAdded),
runtime: runtimeV2,
expectedCompatible: false,
},
{
name: "V2 package removed",
release: await releaseV2With(packageRemoved),
runtime: runtimeV2,
expectedCompatible: false,
},
{
name: "V2 package version tampered",
release: await releaseV2With(versionChanged),
runtime: runtimeV2,
expectedCompatible: false,
},
{
name: "V2 package digest tampered",
release: await releaseV2With(packageDigestChanged),
runtime: runtimeV2,
expectedCompatible: false,
},
{
name: "V2 set digest tampered",
release: await releaseV2With(
contractPackages,
`sha256:${"0".repeat(64)}`,
),
runtime: runtimeV2,
expectedCompatible: false,
},
];
for (const fixture of matrix) {
const input = {
release: fixture.release,
runtime: fixture.runtime,
contractPackages,
};
const verifierVerdict = (
await verifyReleaseRuntimeCoherence(input)
).compatible;
const rollbackDrillVerdict = (
await verifyRollbackReleaseCoherence(input)
).compatible;
expect(verifierVerdict, `${fixture.name}: verifier`).toBe(
fixture.expectedCompatible,
);
expect(rollbackDrillVerdict, `${fixture.name}: rollback drill`).toBe(
fixture.expectedCompatible,
);
expect(rollbackDrillVerdict, `${fixture.name}: identical verdict`).toBe(
verifierVerdict,
);
}
});
});