import { executionPlanDigestAsync, type ExecutionPlan, hexDigest, hexDigestAsync, ExecutionPlanError } from "./plan.js"; import { portableReflectionValue } from "./reflection.js"; import type { BoundDependency, DependencyBinding, PackageExport, PersistentAttachment, } from "../capability-model/types.js"; import { type ExecutionInstallation, type ExecutionGroupIdentity } from "./installation.js"; export interface ExecutionEnvironment { /** Explicit host placement. Merely including an optional module does not select it. */ readonly placement: "service" | "module"; /** Exact host executable/isolation inputs, computed by the trusted installer. */ readonly hostDigest: string; readonly policyDigest: string; /** Identifier of the secret/config generation; never secret material. */ readonly credentialGeneration: string; } export interface InstalledExecutionGroup extends ExecutionGroupIdentity { readonly packageRevision: string; readonly consumedPackages: readonly string[]; } /** Conservative outbound closure. Runtime graph values, reverse callers, source * provenance and the workspace epoch are deliberately absent from this identity. */ export async function compileExecutionGroups( plan: ExecutionPlan, installation: ExecutionInstallation, environments: Readonly>, ): Promise { if (installation.plan !== plan.digest || (await executionPlanDigestAsync(plan)) !== plan.digest) throw new ExecutionPlanError("GROUP_PLAN_MISMATCH", plan.digest, "Expected one checked installation"); const workspace = plan.definition.workspace; const packages = new Map(workspace.packageImports.map((p) => [p.revisionId as string, p])); const operations = new Map(plan.definition.operations.map((op) => [op.key, op])); const attachments = new Map( [...workspace.sharedAttachments, ...workspace.conformances.flatMap((c) => c.privateAttachments)].map((a) => [ a.id, a, ]), ); const logical = (revision: string) => { const pkg = packages.get(revision); if (!pkg) throw new ExecutionPlanError("GROUP_PACKAGE_MISSING", revision, "Uninstalled consumed package"); return pkg.packageId; }; const environment = (revision: string) => { const value = environments[revision]; if ( !value || !["service", "module"].includes(value.placement) || !/^[0-9a-f]{64}$/.test(value.hostDigest) || !/^[0-9a-f]{64}$/.test(value.policyDigest) || !value.credentialGeneration || value.credentialGeneration.length > 4096 ) throw new ExecutionPlanError( "GROUP_ENVIRONMENT_MISSING", revision, "Expected checked host/policy digests and a credential generation", ); return value; }; const implementations = new Map( installation.packages.map((pkg) => { if (!pkg.service && !pkg.module) return [pkg.revision, undefined] as const; const implementation = pkg[environment(pkg.revision).placement]; if (!implementation) throw new ExecutionPlanError( "GROUP_IMPLEMENTATION_MISSING", pkg.revision, "Host selected an unavailable implementation", ); return [pkg.revision, implementation] as const; }), ); const attachmentContract = (value: PersistentAttachment) => { const { displayName: _name, ...rest } = value; return value.kind === "state" ? rest : { ...rest, endpoints: value.endpoints.map(({ displayName: _name, ...endpoint }) => endpoint) }; }; const bindingContract = (value: DependencyBinding) => value.kind === "query" ? { ...value, packageRevisionId: logical(value.packageRevisionId) } : value; const groups: InstalledExecutionGroup[] = []; for (const root of installation.packages.filter((p) => !!implementations.get(p.revision))) { const consumed = new Map(), visited = new Set(), visitedQueries = new Set(), consumedPackages = new Set(); const remember = (key: string, value: unknown) => consumed.set(key, value); const storage = (id: string) => { const value = attachments.get(id); if (!value) throw new ExecutionPlanError("GROUP_STORAGE_MISSING", id, "Uninstalled consumed storage"); remember(`storage:${id}`, attachmentContract(value)); }; // Deletion may visit related objects and their owned state. Conservatively // include the connected schema component, including possible future edges. const lifecycle = (atom: string) => { const visited = new Set(), pending = [atom]; const atoms = (constraint: Extract["endpoints"][number]["constraint"]) => constraint.kind === "atom" ? [constraint.atomId as string] : workspace.conformances .filter((c) => c.interfaceRevisionId === constraint.interfaceRevisionId) .map((c) => c.atomId as string); while (pending.length) { const current = pending.pop()!; if (visited.has(current)) continue; visited.add(current); for (const owner of workspace.conformances.filter((c) => c.atomId === current)) for (const attachment of owner.privateAttachments) storage(attachment.id); for (const attachment of attachments.values()) if (attachment.kind === "edge" && attachment.endpoints.some((e) => atoms(e.constraint).includes(current))) { storage(attachment.id); pending.push(...attachment.endpoints.flatMap((e) => atoms(e.constraint))); } } }; const targets = (binding: DependencyBinding, atom: string): string[] => { if (binding.kind === "interface" && binding.receiver === "input") return workspace.conformances .filter((c) => c.interfaceRevisionId === binding.interfaceRevisionId) .map((c) => c.atomId); if (!("via" in binding) || !binding.via) return [atom]; storage(binding.via.edgeTypeId); const edge = attachments.get(binding.via.edgeTypeId); if (!edge || edge.kind !== "edge") throw new ExecutionPlanError("GROUP_EDGE_MISSING", atom, "Missing traversal edge"); const endpoint = edge.endpoints.find((e) => e.projectionId !== binding.via!.projectionId)!; const constraint = endpoint.constraint; return constraint.kind === "atom" ? [constraint.atomId] : workspace.conformances .filter((c) => c.interfaceRevisionId === constraint.interfaceRevisionId) .map((c) => c.atomId); }; const query = (revision: string, id: string) => { const key = JSON.stringify([revision, id]); if (visitedQueries.has(key)) return; visitedQueries.add(key); const value = plan.definition.queries.find((q) => q.id === key); if (!value) throw new ExecutionPlanError("GROUP_QUERY_MISSING", key, "Missing consumed query"); remember(`query:${JSON.stringify([logical(revision), id])}`, value.query.checked.definitionDigest); for (const operation of value.operations) visit(operation); }; const dependencies = (entry: PackageExport, bindings: readonly BoundDependency[], atom: string) => { for (const bound of bindings) { const target = bound.binding, port = entry.dependencyPorts.find((p) => p.id === bound.portId)!; if ("via" in target && target.via) storage(target.via.edgeTypeId); if (target.kind === "reflection") { for (const selector of target.selectors) remember("reflection:" + selector, portableReflectionValue(workspace, selector)); } else if (target.kind === "state") storage(target.slotId); else if (target.kind === "edge") storage(target.edgeTypeId); else if (target.kind === "query") query(target.packageRevisionId, target.queryId); else if (target.kind === "interface" && port.requirement.kind === "interface") { const iface = workspace.interfaceImports.find((i) => i.revisionId === target.interfaceRevisionId)!; for (const targetAtom of targets(target, atom)) for (const op of port.requirement.operations ?? iface.members.flatMap((m) => m.operations.map((o) => o.id))) visit(JSON.stringify([targetAtom, target.interfaceRevisionId, op])); } else if (target.kind === "creation") { for (const seed of target.owners) { const owner = workspace.conformances.find( (c) => c.atomId === target.atomId && c.interfaceRevisionId === seed.interfaceRevisionId, )!; remember(`birth:${owner.id}`, { id: owner.id, atom: owner.atomId, interface: owner.interfaceRevisionId, initialization: owner.initialization ?? [], }); for (const attachment of owner.privateAttachments) storage(attachment.id); } } else throw new ExecutionPlanError( "GROUP_DEPENDENCY_UNSUPPORTED", bound.portId, "Cannot establish execution reuse for this dependency", ); } }; const visit = (key: string) => { if (visited.has(key)) return; visited.add(key); const operation = operations.get(key); if (!operation) throw new ExecutionPlanError("GROUP_OPERATION_MISSING", key, "Missing consumed operation"); const binding = operation.binding; if (binding.kind === "package") { const pkg = packages.get(binding.packageRevisionId)!; const implementation = implementations.get(pkg.revisionId); if (!implementation) throw new ExecutionPlanError( "GROUP_IMPLEMENTATION_MISSING", pkg.revisionId, "Consumed package has no selected server implementation", ); consumedPackages.add(pkg.packageId); remember(`implementation:${pkg.packageId}`, { digest: implementation.contentDigest, environment: environment(pkg.revisionId), }); const entry = pkg.exports.find((e) => e.id === binding.exportId)!; const bindings = binding.dependencies.map((d) => ({ portId: d.portId, binding: bindingContract(d.binding) })); const acceptance = (binding.acceptanceDependencies ?? []).map((d) => ({ portId: d.portId, binding: bindingContract(d.binding), })); const contract = { input: operation.input, output: operation.output, execution: operation.execution, scope: operation.scope, binding: { package: pkg.packageId, export: binding.exportId, dependencies: bindings, acceptanceDependencies: acceptance, }, }; // Deduplicate aliases of the same bound export, rather than keying its owner by reverse callers. remember(`binding:${hexDigest(contract)}`, contract); dependencies(entry, binding.dependencies, operation.atomId); if (entry.acceptanceExportId) dependencies( pkg.exports.find((e) => e.id === entry.acceptanceExportId)!, binding.acceptanceDependencies ?? [], operation.atomId, ); } else { remember(`operation:${key}`, operation); if (binding.kind === "state") storage(binding.slotId); if (binding.kind === "edge") storage(binding.edgeTypeId); if (binding.kind === "lifecycle") lifecycle(operation.atomId); } }; for (const operation of plan.definition.operations) if (operation.binding.kind === "package" && operation.binding.packageRevisionId === root.revision) visit(operation.key); const own = implementations.get(root.revision)!; const exports = packages.get(root.revision)!.exports.map(({ displayName: _name, dependencyPorts, ...entry }) => ({ ...entry, dependencyPorts: dependencyPorts.map(({ displayName: _name, ...port }) => port), })); const entries = [...consumed].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)); groups.push({ logicalId: logical(root.revision), packageRevision: root.revision, executionDigest: `sha256:${await hexDigestAsync({ implementation: own.contentDigest, exports, environment: environment(root.revision), versions: plan.definition.versions, consumed: entries })}`, consumedPackages: [...consumedPackages].sort(), }); } return groups; }