Lower checked shared edges into the portable graph model

This commit is contained in:
Timothy J. Aveni
2026-09-25 12:34:13 -07:00
parent c482a99aca
commit 58a75da83f
6 changed files with 105 additions and 42 deletions
+22 -11
View File
@@ -213,17 +213,28 @@ export function generateComponentClientBindings(plan: ExecutionPlan, packageRevi
`ExtraModuleProps<${JSON.stringify(JSON.stringify([iface, member]))}>`;
// Preserve closed type arguments for the artifact consumer. Interpretation of
// a particular module contract belongs to that consumer, not this compiler.
const moduleArguments = (iface: InterfaceRevision) => `[${(iface.application?.arguments ?? []).map(argument => {
if (argument.kind === "value") return tsType(argument.type);
const target = argument.target;
const reference = target.kind === "atom" ? `ManagedReference<"atom", ${JSON.stringify(target.atomId)}>` : `ManagedReference<"interface", ${JSON.stringify(target.interfaceRevisionId)}>`;
const fields = target.kind === "interface" ? find(target.interfaceRevisionId).members.flatMap(member => {
if ((member.kind !== "value" && member.kind !== "relationship") || !member.queryRead) return [];
const getter = member.operations.find(operation => operation.displayName === (member.kind === "value" ? "get" : "resolve"));
return getter ? [`readonly ${JSON.stringify(member.displayName)}: ${tsType(getter.outputType)}`] : [];
}) : [];
return `{readonly reference: ${reference}; readonly values: {${fields.join(";")}}}`;
}).join(",")}]`;
const moduleArguments = (iface: InterfaceRevision) =>
`[${(iface.application?.arguments ?? [])
.map((argument) => {
if (argument.kind === "value") return tsType(argument.type);
const target = argument.target;
const reference =
target.kind === "atom"
? `ManagedReference<"atom", ${JSON.stringify(target.atomId)}>`
: `ManagedReference<"interface", ${JSON.stringify(target.interfaceRevisionId)}>`;
const fields =
target.kind === "interface"
? find(target.interfaceRevisionId).members.flatMap((member) => {
if ((member.kind !== "value" && member.kind !== "relationship") || !member.queryRead) return [];
const getter = member.operations.find(
(operation) => operation.displayName === (member.kind === "value" ? "get" : "resolve"),
);
return getter ? [`readonly ${JSON.stringify(member.displayName)}: ${tsType(getter.outputType)}`] : [];
})
: [];
return `{readonly reference: ${reference}; readonly values: {${fields.join(";")}}}`;
})
.join(",")}]`;
const interfaces = generateInterfaceClientBindings(imports, "@quixos/camino-react", props);
const subjectBindings = generateInterfaceClientBindings(subjects, "@quixos/camino-react", props)
.split("\n")
+18 -2
View File
@@ -18,7 +18,15 @@ const meta = () => selection("_camino", { selection: [selection("ref")] });
export const inspectionPackage = "org.quixos.inspection/1";
export interface InspectionTemplate {
interface: string;
members: { id: string; name: string; key: string; flag: string; lane: "local" | "server"; many: boolean; relationship: boolean }[];
members: {
id: string;
name: string;
key: string;
flag: string;
lane: "local" | "server";
many: boolean;
relationship: boolean;
}[];
inputs?: PublicQueryContract;
view?: PublicQueryContract;
server?: PublicQueryContract;
@@ -81,7 +89,15 @@ export function compileInspectionTemplates(plan: ExecutionPlan) {
// Only native edges promise the relationship traversal used by the interpreter.
if (member.kind === "relationship" && providers.some((op) => op!.binding.kind !== "edge")) continue;
if (!complete && (lane === "local" || !portable))
template.members.push({ id: member.id, name: member.displayName, key, flag, lane, many, relationship: member.kind === "relationship" });
template.members.push({
id: member.id,
name: member.displayName,
key,
flag,
lane,
many,
relationship: member.kind === "relationship",
});
inputFields[flag] = scalar("bool");
const field = selection(member.displayName, {
key,
+27 -27
View File
@@ -22,11 +22,11 @@ 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.length)
if (workspace.sharedAttachments.some((attachment) => attachment.kind !== "edge"))
throw new ExecutionPlanError(
"UNSUPPORTED_SHARED_STORAGE",
"kernel",
"Stage 0 requires explicit conformance-owned storage",
"Shared scalar storage is not implemented; use conformance-owned state",
);
const schema = (type: ValueType): unknown | null => {
try {
@@ -36,7 +36,30 @@ export function compileKernelProgram(plan: ExecutionPlan): unknown {
}
};
const ownerBySlot = new Map<string, string>();
const edges: { id: string; owner: string; a: unknown; b: unknown }[] = [];
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[]) {
@@ -51,30 +74,7 @@ export function compileKernelProgram(plan: ExecutionPlan): unknown {
policy: storage.storagePolicy.kind === "register" ? "register" : "crdt",
...(Object.hasOwn(storage, "defaultValue") ? { defaultLiteral: storage.defaultValue } : {}),
});
} else {
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: owner.id, a: endpoints[0], b: endpoints[1] });
}
} else lowerEdge(storage, owner.id);
}
return {
id: owner.id,
+3 -1
View File
@@ -82,7 +82,9 @@ function componentDeclarations(workspace: WorkspaceRevision, pkg: WorkspaceRevis
...(pkg.modules ?? []).flatMap((module) => {
const contract = moduleContract(workspace.interfaceImports, module)!;
if (
!["org.quixos.react.component/1", "org.quixos.react.component/2", "org.quixos.react.subject-only/1"].includes(contract.contract) ||
!["org.quixos.react.component/1", "org.quixos.react.component/2", "org.quixos.react.subject-only/1"].includes(
contract.contract,
) ||
contract.subject?.kind !== "interface"
)
return [];