Files
quixos-protocol/src/execution-world/kernel.ts
T

372 lines
16 KiB
TypeScript
Raw Normal View History

import { portableReflectionType, portableReflectionValue } from "./reflection.js";
import type {
ValueType,
InitializationField,
PersistentAttachment,
DependencyBinding,
BoundDependency,
PackageExport,
} from "../capability-model/types.js";
import { type ExecutionPlan, ExecutionPlanError, hexDigest } from "./plan.js";
import { portableValueSchema } from "../bindings/portable.js";
import { subscriptionContract } from "../capability-model/journals.js";
type PreparedStep = string | null | { case: string; id: string };
function preparedInputPaths(type: ValueType, path: PreparedStep[] = []): PreparedStep[][] {
if (type.kind === "change") return [path];
if (type.kind === "optional") return preparedInputPaths(type.value, path);
if (type.kind === "list") return preparedInputPaths(type.value, [...path, null]);
if (type.kind === "variant")
return Object.entries(type.cases).flatMap(([label, entry]) =>
preparedInputPaths(entry.value, [...path, { case: label, id: entry.id }]),
);
if (type.kind === "record")
return Object.entries(type.fields).flatMap(([key, value]) => preparedInputPaths(value, [...path, key]));
return [];
}
/** Small data-only interpreter input; no compiler, parser, Node module or SQL dependency reaches the browser. */
export function compileKernelProgram(plan: ExecutionPlan): unknown {
if (hexDigest(plan.definition) !== plan.digest)
throw new ExecutionPlanError("INVALID_PLAN", "kernel", "Plan digest mismatch");
const workspace = plan.definition.workspace;
if (workspace.sharedAttachments.some((attachment) => attachment.kind !== "edge"))
throw new ExecutionPlanError(
"UNSUPPORTED_SHARED_STORAGE",
"kernel",
"Shared scalar storage is not implemented; use conformance-owned state",
);
const schema = (type: ValueType): unknown | null => {
try {
return portableValueSchema(type);
} catch {
return null;
}
};
const ownerBySlot = new Map<string, string>();
const edges: { id: string; owner: string | null; a: unknown; b: unknown }[] = [];
const lowerEdge = (storage: Extract<PersistentAttachment, { kind: "edge" }>, owner: string | null) => {
const endpoints = storage.endpoints.map((endpoint) => {
if (endpoint.ordered || (endpoint.keyType && endpoint.keyType !== "string") || endpoint.retainOther)
throw new ExecutionPlanError(
"UNSUPPORTED_GRAPH_FEATURE",
storage.id,
"Ordered relations, non-string keys and automatic retention are not implemented",
);
return {
projection: endpoint.projectionId,
constraint: {
kind: endpoint.constraint.kind,
id:
endpoint.constraint.kind === "atom" ? endpoint.constraint.atomId : endpoint.constraint.interfaceRevisionId,
},
cardinality: endpoint.cardinality,
...(endpoint.keyType ? { keyType: endpoint.keyType } : {}),
...(endpoint.onDelete ? { onDelete: endpoint.onDelete } : {}),
};
});
edges.push({ id: storage.id, owner, a: endpoints[0], b: endpoints[1] });
};
for (const storage of workspace.sharedAttachments) if (storage.kind === "edge") lowerEdge(storage, null);
const owners = plan.definition.owners.map((owner) => {
const slots: unknown[] = [];
for (const storage of owner.storage as PersistentAttachment[]) {
if (storage.kind === "state") {
const shape = schema(storage.valueType);
if (!shape)
throw new ExecutionPlanError("UNSUPPORTED_STORAGE_CODEC", storage.id, "Unsupported canonical value type");
ownerBySlot.set(storage.id, owner.id);
const pinsFor = (payload: ValueType) => {
const expectations = new Map<string, { kind: "atom" | "interface"; id: string }>();
const walk = (type: ValueType): void => {
if (type.kind === "object-ref") {
const expectation = {
kind: type.expectation.kind,
id: type.expectation.kind === "atom" ? type.expectation.atomId : type.expectation.interfaceRevisionId,
};
expectations.set(JSON.stringify(expectation), expectation);
} else if (type.kind === "list" || type.kind === "optional") walk(type.value);
else if (type.kind === "record") Object.values(type.fields).forEach(walk);
else if (type.kind === "variant") Object.values(type.cases).forEach((entry) => walk(entry.value));
};
walk(payload);
const pins = [...expectations.values()].map((expectation) => {
const id = `journal-pin:${hexDigest([storage.id, expectation])}`;
const projection = `${id}:source`;
edges.push({
id,
owner: owner.id,
a: {
projection,
constraint: { kind: "atom", id: owner.atom },
cardinality: "many",
keyType: "string",
onDelete: "detach",
},
b: { projection: `${id}:target`, constraint: expectation, cardinality: "many", onDelete: "restrict" },
});
return { edge: id, projection };
});
return pins;
};
let journal: unknown, subscription: unknown;
if (storage.storagePolicy.kind === "journal") {
journal = { limits: storage.storagePolicy.limits, pins: pinsFor(storage.valueType) };
}
if (storage.storagePolicy.kind === "subscription") {
const bindings = plan.definition.operations.filter(
(operation) =>
operation.atomId === owner.atom &&
operation.binding.kind === "subscription" &&
operation.binding.slotId === storage.id &&
operation.binding.primitive === "attach",
);
if (bindings.length !== 1)
throw new ExecutionPlanError(
"SUBSCRIPTION_ATTACHMENT_REQUIRED",
storage.id,
"Native subscription storage requires one checked attachment operation",
);
const binding = bindings[0]!;
const operation = workspace.interfaceImports
.find((iface) => iface.revisionId === binding.interfaceRevisionId)!
.members.flatMap((member) => member.operations)
.find((operation) => operation.id === binding.operationId)!;
const contract = subscriptionContract(
storage.storagePolicy.payload,
storage.valueType,
operation,
workspace.interfaceImports,
);
const expectation = (value: typeof contract.source) => ({
kind: value.kind,
id: value.kind === "atom" ? value.atomId : value.interfaceRevisionId,
});
const source = expectation(contract.source),
handler = expectation(contract.handler);
const configuration: ValueType = {
kind: "record",
fields: {
source: { kind: "object-ref", expectation: contract.source },
handler: { kind: "object-ref", expectation: contract.handler },
parameters: storage.valueType,
},
};
const edge = `subscription-active:${hexDigest(storage.id)}`,
projection = `${edge}:subscription`;
edges.push({
id: edge,
owner: owner.id,
a: { projection, constraint: { kind: "atom", id: owner.atom }, cardinality: "many", onDelete: "restrict" },
b: { projection: `${edge}:stream`, constraint: source, cardinality: "many", onDelete: "restrict" },
});
subscription = {
payload: schema(storage.storagePolicy.payload),
source,
handler,
operation: contract.operation,
pins: pinsFor(configuration),
active: { edge, projection },
};
}
slots.push({
id: storage.id,
schema: shape,
policy: storage.storagePolicy.kind === "crdt-document" ? "crdt" : storage.storagePolicy.kind,
...(journal ? { journal } : {}),
...(subscription ? { subscription } : {}),
...(Object.hasOwn(storage, "defaultValue") ? { defaultLiteral: storage.defaultValue } : {}),
});
} else lowerEdge(storage, owner.id);
}
return {
id: owner.id,
atom: owner.atom,
interface: owner.interface,
slots,
initialize: (owner.initialization as InitializationField[]).map((field) => ({
input: field.input,
binding:
field.binding.kind === "state"
? { kind: "state", slot: field.binding.slotId }
: { kind: "edge", edge: field.binding.edgeTypeId, projection: field.binding.projectionId },
})),
};
});
const slotOwner = (slot: string) => {
const owner = ownerBySlot.get(slot);
if (!owner) throw new ExecutionPlanError("MISSING_STORAGE_OWNER", slot, "No checked owner");
return owner;
};
const via = (binding: DependencyBinding) =>
"via" in binding && binding.via
? { via: { edge: binding.via.edgeTypeId, projection: binding.via.projectionId } }
: {};
const operations = plan.definition.operations.map((operation) => {
const binding = operation.binding;
let target: unknown;
let unsupported: string | null = null;
if (binding.kind === "lifecycle") target = { kind: "lifecycle", primitive: binding.primitive };
else if (binding.kind === "journal" || binding.kind === "subscription")
target = {
kind: binding.kind,
owner: slotOwner(binding.slotId),
slot: binding.slotId,
primitive: binding.primitive,
};
else if (binding.kind === "state")
target = { kind: "state", owner: slotOwner(binding.slotId), slot: binding.slotId, primitive: binding.primitive };
else if (binding.kind === "edge")
target = {
kind: "edge",
edge: binding.edgeTypeId,
projection: binding.projectionId,
primitive: binding.primitive,
};
else {
const pkg = workspace.packageImports.find((p) => p.revisionId === binding.packageRevisionId)!;
const entry = pkg.exports.find((e) => e.id === binding.exportId)!;
const compilePorts = (entry: PackageExport, dependencies: BoundDependency[]) =>
dependencies.map((dependency) => {
const requirement = entry.dependencyPorts.find((p) => p.id === dependency.portId)!.requirement;
const bound = dependency.binding;
const common = { id: dependency.portId, ...via(bound) };
if (bound.kind === "reflection" && requirement.kind === "reflection")
return {
...common,
kind: "reflection",
values: Object.fromEntries(
bound.selectors.map((selector) => [
selector,
{
schema: portableValueSchema(portableReflectionType(selector)),
value: portableReflectionValue(workspace, selector),
},
]),
),
};
if (bound.kind === "state" && requirement.kind === "state")
return {
...common,
kind: "state",
owner: slotOwner(bound.slotId),
slot: bound.slotId,
primitives: requirement.primitives,
};
if (bound.kind === "edge" && requirement.kind === "edge")
return {
...common,
kind: "edge",
edge: bound.edgeTypeId,
projection: bound.projectionId,
primitives: requirement.primitives,
};
if (bound.kind === "interface" && requirement.kind === "interface") {
const iface = workspace.interfaceImports.find((i) => i.revisionId === bound.interfaceRevisionId)!;
return {
...common,
kind: "interface",
interface: bound.interfaceRevisionId,
...(!bound.receiver &&
!bound.via &&
(requirement.operations ?? iface.members.flatMap((m) => m.operations.map((o) => o.id))).every(
(id) => iface.members.flatMap((m) => m.operations).find((o) => o.id === id)?.scope === "class",
)
? { staticAtom: operation.atomId }
: {}),
...(bound.receiver ? { receiver: bound.receiver } : {}),
operations: requirement.operations ?? iface.members.flatMap((m) => m.operations.map((o) => o.id)),
};
}
if (bound.kind === "creation" && requirement.kind === "creation")
return {
...common,
kind: "creation",
atom: bound.atomId,
schema: portableValueSchema(requirement.inputType),
recipe: hexDigest({
codec: "camino-creation-recipe-v1",
versions: plan.definition.versions,
operation: operation.key,
export: entry.id,
port: dependency.portId,
requirement,
binding: bound,
owners: owners.filter((owner) => owner.atom === bound.atomId),
edges: edges.filter((edge) =>
owners.some((owner) => owner.atom === bound.atomId && owner.id === edge.owner),
),
}),
owners: bound.owners.map((seed) => {
const owner = owners.find((o) => o.atom === bound.atomId && o.interface === seed.interfaceRevisionId);
if (!owner)
throw new ExecutionPlanError(
"MISSING_STORAGE_OWNER",
dependency.portId,
"Creation has no initialization owner",
);
return { owner: owner.id, inputPath: seed.inputPath };
}),
};
unsupported = `UNSUPPORTED_EXECUTION_PORT:${bound.kind}`;
return { ...common, kind: "unavailable" };
});
const ports = compilePorts(entry, binding.dependencies);
const check = entry.acceptanceExportId
? pkg.exports.find((candidate) => candidate.id === entry.acceptanceExportId)!
: undefined;
target = {
kind: "package",
package: binding.packageRevisionId,
export: binding.exportId,
ports,
...(entry.preparation ? { preparation: entry.preparation } : {}),
...(check
? { acceptance: { export: check.id, ports: compilePorts(check, binding.acceptanceDependencies ?? []) } }
: {}),
};
}
const inputChanges = preparedInputPaths(operation.input);
const input = schema(operation.input),
output = schema(operation.output);
return {
key: operation.key,
atom: operation.atomId,
interface: operation.interfaceRevisionId,
operation: operation.operationId,
scope: operation.scope,
effect: operation.execution.kind,
targets: operation.availableTargets,
input,
...(inputChanges.length ? { inputChanges } : {}),
output,
unsupported: unsupported ?? (input && output ? null : "UNSUPPORTED_OPERATION_CODEC"),
binding: target,
};
});
const packages = workspace.packageImports.map((pkg) => ({
revision: pkg.revisionId,
exports: pkg.exports.map((entry) => ({
id: entry.id,
...(entry.preparation ? { preparation: entry.preparation } : {}),
...(entry.acceptanceExportId ? { acceptance: entry.acceptanceExportId } : {}),
effect: entry.execution?.kind ?? "remote",
targets: entry.execution && entry.execution.kind !== "remote" ? entry.execution.targets : [],
receiver:
entry.kind === "function"
? null
: entry.kind === "operation" && entry.receiverRequirement.kind === "exact-atom"
? { kind: "atom", id: entry.receiverRequirement.atomId }
: "object",
input: schema(entry.inputType),
output: schema(entry.outputType),
})),
}));
const body = {
codec: "camino-kernel-program-v1",
model: { workspace: workspace.workspaceId, atoms: workspace.atoms.map((atom) => atom.id), owners, edges },
operations,
packages,
};
// The interpreter's plan key covers its real input, not source/build evidence.
return { ...body, plan: hexDigest({ versions: plan.definition.versions, program: body }) };
}