From bcc915d597ff7dd587d12cda97bddec41e340190 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 22 Sep 2026 17:05:36 -0700 Subject: [PATCH] Expose bounded public reflection and presentation discovery --- src/execution-world/index.ts | 2 + src/execution-world/public-reflection.ts | 94 ++++++++++++++++++++++++ test/execution-world.test.ts | 32 ++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/execution-world/public-reflection.ts diff --git a/src/execution-world/index.ts b/src/execution-world/index.ts index 84752e7..b60b9f4 100644 --- a/src/execution-world/index.ts +++ b/src/execution-world/index.ts @@ -17,3 +17,5 @@ export { generatePortableClientBindings } from "../bindings/portable-client.js"; export * from "./candidate.js"; export { reviewSemanticMajors } from "../capability-model/evolution.js"; + +export * from "./public-reflection.js"; diff --git a/src/execution-world/public-reflection.ts b/src/execution-world/public-reflection.ts new file mode 100644 index 0000000..f8b5fa2 --- /dev/null +++ b/src/execution-world/public-reflection.ts @@ -0,0 +1,94 @@ +import { compileKernelProgram } from "./kernel.js"; +import { hexDigest, type ExecutionPlan } from "./plan.js"; +import { clientContractCatalog, type ExecutionInstallation } from "./installation.js"; +import { publicOperationContract, publicQueryContract } from "./client-contracts.js"; + +/** Explicit public projection. Never serialize conformance storage or package ports. */ +export function publicDefinitionSnapshot(plan: ExecutionPlan, installation: ExecutionInstallation) { + const workspace = plan.definition.workspace; + const kernel = compileKernelProgram(plan) as { + operations: { + atom: string; + interface: string; + operation: string; + unsupported: string | null; + effect: string; + targets: string[]; + }[]; + }; + const catalog = clientContractCatalog( + plan, + installation, + installation.packages.map((pkg) => pkg.revision), + ); + const rows = { + atoms: workspace.atoms.map((atom) => ({ id: atom.id, name: atom.displayName, documentation: atom.documentation })), + interfaces: workspace.interfaceImports.map((iface) => ({ + id: iface.revisionId, + interface: iface.interfaceId, + name: iface.displayName, + source: iface.source, + template: iface.template, + application: iface.application, + requiredInterfaces: iface.requiredInterfaces ?? [], + argumentRequirements: iface.argumentRequirements ?? [], + members: iface.members.map((member) => ({ + ...member, + operations: member.operations.map((operation) => ({ + ...operation, + ...(operation.mode === "call" && !iface.template + ? { descriptor: publicOperationContract(iface, operation) } + : {}), + })), + })), + })), + conformances: workspace.conformances.map((c) => ({ + id: JSON.stringify([c.atomId, c.interfaceRevisionId]), + atom: c.atomId, + interface: c.interfaceRevisionId, + semanticMajor: c.semanticMajor ?? 0, + operations: ( + workspace.interfaceImports.find((iface) => iface.revisionId === c.interfaceRevisionId)?.members ?? [] + ).flatMap((member) => + member.operations.map((operation) => { + const installed = kernel.operations.find( + (op) => op.atom === c.atomId && op.interface === c.interfaceRevisionId && op.operation === operation.id, + ); + return { + operation: operation.id, + available: !!installed && !installed.unsupported, + unavailable: installed?.unsupported ?? (installed ? null : "NOT_CALLABLE"), + execution: installed ? { kind: installed.effect, targets: installed.targets } : null, + }; + }), + ), + modules: catalog.moduleBindings.filter( + (binding) => binding.atom === c.atomId && binding.interface === c.interfaceRevisionId, + ), + })), + queries: workspace.packageImports.flatMap((pkg) => + (pkg.checkedQueries ?? []).map((query) => ({ + id: JSON.stringify([pkg.revisionId, query.declaration.id]), + name: query.declaration.displayName, + package: pkg.revisionId, + descriptor: publicQueryContract(pkg, query.declaration.id), + fragment: query.declaration.fragment, + limits: query.declaration.budgets, + watch: query.declaration.watch, + })), + ), + presentations: catalog.components.map((component) => ({ + id: JSON.stringify([component.packageRevision, component.id]), + component, + registrations: catalog.presentations.filter( + (p) => p.packageRevision === component.packageRevision && p.component === component.id, + ), + })), + }; + const publicRows = JSON.parse(JSON.stringify(rows)) as Record< + keyof typeof rows, + { id: string; [key: string]: unknown }[] + >; + for (const entries of Object.values(publicRows)) entries.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)); + return { codec: "camino-definitions-v1" as const, id: hexDigest(publicRows), rows: publicRows }; +} diff --git a/test/execution-world.test.ts b/test/execution-world.test.ts index 963d99f..9fe4eaf 100644 --- a/test/execution-world.test.ts +++ b/test/execution-world.test.ts @@ -308,3 +308,35 @@ test("a count-only local query still requires complete relationship admission", assert.throws(() => compileExecutionPlan(workspace), /REPLICA_TRAVERSAL_MISSING|REPLICA_RELATION_INCOMPLETE/); } }); + +test("public reflection contains nominal contracts and callable descriptors without private bindings", async () => { + const { publicDefinitionSnapshot } = await import("../src/execution-world/public-reflection.js"); + const plan = compileExecutionPlan(await loadReaderWorkspace()); + const installation = { + codec: "camino-execution-installation-v1" as const, + plan: plan.digest, + world: "test", + packages: plan.definition.workspace.packageImports.map((pkg) => ({ + revision: pkg.revisionId, + root: "private-installation-root", + })), + components: [], + routes: {}, + }; + const snapshot = publicDefinitionSnapshot(plan, installation); + assert.ok(snapshot.rows.interfaces.length); + assert.ok(snapshot.rows.queries.length); + assert.ok(snapshot.rows.conformances.length); + const publicText = JSON.stringify(snapshot); + for (const privateKey of [ + "privateAttachments", + "operationBindings", + "relationshipMaterializations", + "storagePolicy", + "ports", + ]) + assert.ok(!publicText.includes('"' + privateKey + '"'), privateKey); + assert.ok(publicText.includes('"descriptor"')); + assert.ok(publicText.includes('"scope":"class"')); + assert.equal(snapshot.id, publicDefinitionSnapshot(plan, installation).id); +});