Separate graph execution identity from installation provenance
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { specializePackageExport } from "../src/capability-model/generic-packages.js";
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import fs from "node:fs/promises";
|
||||
@@ -297,3 +298,25 @@ test("typed presentation and factory consumers preserve distinct closed applicat
|
||||
await fs.rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("specialized dispatch keys retain nominal arguments, not build provenance", () => {
|
||||
const pkg = definition();
|
||||
const generic = pkg.genericExports![0]!;
|
||||
const environment = {
|
||||
arguments: new Map(),
|
||||
applyInterface: () => {
|
||||
throw Error("unused");
|
||||
},
|
||||
implementsInterface: () => false,
|
||||
};
|
||||
const arguments_ = [{ kind: "value" as const, type: { kind: "scalar" as const, name: "string" as const } }];
|
||||
const original = specializePackageExport(pkg, generic, arguments_, environment);
|
||||
const rebuilt = structuredClone(pkg);
|
||||
rebuilt.source.commit = "f".repeat(40);
|
||||
assert.deepEqual(specializePackageExport(rebuilt, generic, arguments_, environment), original);
|
||||
assert.notEqual(
|
||||
specializePackageExport(rebuilt, generic, [{ kind: "value", type: { kind: "scalar", name: "bool" } }], environment)
|
||||
.id,
|
||||
original.id,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -165,7 +165,7 @@ test("bounds require evidence and storable constraints inspect nested values", (
|
||||
);
|
||||
});
|
||||
|
||||
test("closed applications canonicalize records without erasing nominal identity or provenance", () => {
|
||||
test("closed applications preserve nominal identity while separating source provenance", () => {
|
||||
const a: ClosedTypeArgument = {
|
||||
kind: "value",
|
||||
type: { kind: "record", fields: { b: valueType.int64, a: valueType.string } },
|
||||
@@ -183,6 +183,10 @@ test("closed applications canonicalize records without erasing nominal identity
|
||||
assert.equal(appliedInterfaceId(application), appliedInterfaceId({ ...application, arguments: [b] }));
|
||||
assert.notEqual(appliedInterfaceId(application), appliedInterfaceId({ ...application, self: note }));
|
||||
assert.notEqual(
|
||||
appliedInterfaceId(application),
|
||||
appliedInterfaceId({ ...application, definitionId: capabilityId.interfaceRevision("other@1") }),
|
||||
);
|
||||
assert.equal(
|
||||
appliedInterfaceId(application),
|
||||
appliedInterfaceId({ ...application, source: { ...application.source, commit: "b".repeat(40) } }),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { loadReaderWorkspace } from "./reader-fixture.js";
|
||||
import { buildArtifactFixture } from "./artifact-fixture.js";
|
||||
import {
|
||||
generatePortableClientBindings,
|
||||
compileExecutionPlan,
|
||||
compileKernelProgram,
|
||||
compileQueryProgram,
|
||||
graphExecutionRepresentation,
|
||||
hexDigest,
|
||||
type ExecutionPlan,
|
||||
type ExecutionInstallation,
|
||||
} from "../src/execution-world/index.js";
|
||||
|
||||
function prepare(plan: ExecutionPlan) {
|
||||
const installation: ExecutionInstallation = {
|
||||
codec: "camino-execution-installation-v1",
|
||||
plan: plan.digest,
|
||||
world: "0".repeat(64),
|
||||
routes: {},
|
||||
components: [],
|
||||
packages: plan.definition.packages.map((pkg) => {
|
||||
const { manifest } = buildArtifactFixture(plan, pkg.revision);
|
||||
return {
|
||||
revision: pkg.revision,
|
||||
root: "/build/provenance",
|
||||
service: manifest.implementations[0]!,
|
||||
browserImplementation: manifest.implementations[2]!,
|
||||
};
|
||||
}),
|
||||
};
|
||||
const kernel = compileKernelProgram(plan) as any,
|
||||
queries = compileQueryProgram(plan) as any;
|
||||
return {
|
||||
installation,
|
||||
kernel,
|
||||
queries,
|
||||
graph: () => graphExecutionRepresentation(plan, installation, kernel, queries),
|
||||
};
|
||||
}
|
||||
|
||||
test("actual interpreter inputs and creation recipes exclude source provenance", async () => {
|
||||
const workspace = await loadReaderWorkspace();
|
||||
const originalPlan = compileExecutionPlan(workspace);
|
||||
const before = prepare(originalPlan);
|
||||
workspace.sourceRootCommit = "f".repeat(40);
|
||||
for (const pkg of workspace.packageImports) pkg.source.commit = "e".repeat(40);
|
||||
const nextPlan = compileExecutionPlan(workspace);
|
||||
const after = prepare(nextPlan);
|
||||
assert.equal(
|
||||
generatePortableClientBindings(originalPlan, { target: "browser" }),
|
||||
generatePortableClientBindings(nextPlan, { target: "browser" }),
|
||||
);
|
||||
assert.notEqual(before.installation.plan, after.installation.plan);
|
||||
assert.deepEqual(before.kernel, after.kernel);
|
||||
assert.deepEqual(before.queries, after.queries);
|
||||
assert.deepEqual(before.graph(), after.graph());
|
||||
assert.ok(JSON.stringify(before.kernel).includes('"recipe"'));
|
||||
});
|
||||
|
||||
test("graph identity covers executable closures, query semantics and real interpreter input", async () => {
|
||||
const plan = compileExecutionPlan(await loadReaderWorkspace());
|
||||
const fixture = prepare(plan),
|
||||
baseline = hexDigest(fixture.graph());
|
||||
fixture.installation.packages[0]!.service!.files[0]!.digest = "c".repeat(64);
|
||||
assert.notEqual(hexDigest(fixture.graph()), baseline);
|
||||
const query = prepare(plan);
|
||||
query.queries.queries[0].budgets.rows++;
|
||||
assert.notEqual(hexDigest(query.graph()), baseline);
|
||||
query.kernel.operations[0].output = { kind: "scalar", name: "string" };
|
||||
assert.throws(() => query.graph(), /GRAPH_PROGRAM_MISMATCH/);
|
||||
const shared = prepare(plan);
|
||||
(shared.installation as any).engineURL = "/changed-engine.mjs";
|
||||
assert.notEqual(hexDigest(shared.graph()), baseline);
|
||||
const future = prepare(plan);
|
||||
future.kernel.codec = "future-kernel";
|
||||
assert.throws(() => future.graph(), /GRAPH_PROGRAM_MISMATCH/);
|
||||
});
|
||||
|
||||
test("initialization, storage, nominal contracts and bound ports affect executed kernel identity", async () => {
|
||||
const workspace = await loadReaderWorkspace(),
|
||||
plan = compileExecutionPlan(workspace);
|
||||
const original = prepare(plan);
|
||||
for (const change of [
|
||||
(p: ExecutionPlan) => {
|
||||
(p.definition.owners[0]!.initialization as any[]).push({
|
||||
input: "extra",
|
||||
binding: { kind: "state", slotId: "extra" },
|
||||
});
|
||||
},
|
||||
(p: ExecutionPlan) => {
|
||||
p.definition.owners[0]!.id += ":different";
|
||||
},
|
||||
(p: ExecutionPlan) => {
|
||||
p.definition.operations[0]!.operationId += ":different";
|
||||
},
|
||||
(p: ExecutionPlan) => {
|
||||
p.definition.versions.crdt = "future-genesis" as any;
|
||||
},
|
||||
]) {
|
||||
const changed = structuredClone(plan);
|
||||
change(changed);
|
||||
changed.digest = hexDigest(changed.definition);
|
||||
assert.notEqual((compileKernelProgram(changed) as any).plan, original.kernel.plan);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user