Files
quixos-protocol/src/capability-model/validation.ts
T
Timothy J. Aveni 1e72713ae2 Implement durable typed journals, subscriptions, and Reader document connections
Add checked native journal/subscription bindings, managed-reference retention,
bounded history and backpressure, atomic delivery receipts, and fenced hosting.
Preserve source publication prediction and replica reconciliation; subscription
attachment, controls, and delivery remain authoritative in this initial version.

Connect independent manual/Gutenberg document producers to Reader bookshelves
through typed adapters. Add connection inspection, shared provider selection,
authoring documentation, and restart/rejection/fan-out integration coverage.

Verified compiler suite (287 passed, two existing skips), Nix runtime and
PostgreSQL qualification, five example assemblies, headless Reader integration,
Chromium Reader integration, resource pins, formatting, and lint.
No pinned workspace runtime was deployed.
2026-09-28 19:35:51 -07:00

2835 lines
108 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { isStorableType } from "./generics.js";
import {
journalPayloadType,
journalPrimitiveSignature,
subscriptionContract,
subscriptionStatusType,
} from "./journals.js";
import { validateInitialization } from "./initialization.js";
import { validatePresentations } from "./presentations.js";
import { validateModules } from "./modules.js";
import { validateWorkspaceControllers } from "./controllers.js";
import { validateExecutionContracts } from "./execution.js";
import { resolveForwardedBinding, type ConcreteBinding } from "./forwarding.js";
import type {
AtomConstructorBinding,
AtomId,
AttachmentOwner,
Binding,
BoundDependency,
Conformance,
DependencyBinding,
DependencyPort,
EdgeCardinality,
EdgeDefinition,
EdgeEndpoint,
EdgeEndpointConstraint,
EdgePrimitive,
EdgeProjectionId,
EdgeTypeId,
InterfaceMember,
InterfaceOperation,
InterfaceOperationMode,
InterfaceRevision,
InterfaceRevisionId,
OperationId,
OwnedAttachment,
PackageExport,
PackageOperationExport,
PackageFunctionExport,
PackageRevision,
PackageRevisionId,
PersistentAttachment,
SlotId,
SourceRevision,
StatePrimitive,
StateSlotDefinition,
ValueType,
WorkspaceRevision,
} from "./types.js";
import { valueType } from "./types.js";
import { appliedInterfaceId, GenericTypeError } from "./generics.js";
import { specializePackageExport } from "./generic-packages.js";
import { isDeepStrictEqual } from "node:util";
export type CapabilityValidationIssueCode =
| "invalid-controller"
| "invalid-presentation"
| "invalid-module-contract"
| "invalid-initialization"
| "invalid-execution-contract"
| "invalid-query-contract"
| "query-provider-reason-required"
| "invalid-semantic-major"
| "duplicate-conformance-id"
| "required-value"
| "invalid-source"
| "invalid-value-type"
| "invalid-operation"
| "duplicate-atom"
| "duplicate-attachment"
| "duplicate-projection"
| "duplicate-interface-revision"
| "duplicate-interface-member"
| "duplicate-interface-operation"
| "duplicate-package-revision"
| "duplicate-package-export"
| "duplicate-dependency-port"
| "duplicate-conformance"
| "duplicate-operation-binding"
| "duplicate-relationship-materialization"
| "duplicate-constructor"
| "unresolved-reference"
| "invalid-attachment"
| "invalid-state-binding"
| "invalid-edge-binding"
| "invalid-lifecycle-binding"
| "invalid-package-binding"
| "invalid-forwarding-binding"
| "invalid-dependency-binding"
| "private-attachment-access"
| "incompatible-package-receiver"
| "unsatisfied-interface"
| "cyclic-conformance-requirement"
| "missing-operation-binding"
| "unknown-operation-binding"
| "invalid-constructor"
| "invalid-relationship-materialization";
export interface CapabilityValidationIssue {
code: CapabilityValidationIssueCode;
path: string;
message: string;
}
interface InterfaceOperationEntry {
operation: InterfaceOperation;
member: InterfaceMember;
}
interface InterfaceIndexEntry {
revision: InterfaceRevision;
operations: Map<OperationId, InterfaceOperationEntry>;
}
interface PackageIndexEntry {
revision: PackageRevision;
exports: Map<string, PackageExport>;
}
interface AttachmentIndexEntry extends OwnedAttachment {
path: string;
}
interface ValidationIndexes {
atoms: Map<string, WorkspaceRevision["atoms"][number]>;
interfaces: Map<string, InterfaceIndexEntry>;
packages: Map<string, PackageIndexEntry>;
conformances: Map<string, Conformance>;
conformancePaths: Map<string, string>;
attachments: Map<string, AttachmentIndexEntry>;
projections: Map<string, { edge: EdgeDefinition; endpoint: EdgeEndpoint; endpointIndex: 0 | 1 }>;
constructors: Map<AtomId, AtomConstructorBinding>;
}
const hasText = (value: string) => value.trim().length > 0;
const conformanceKey = (atomId: AtomId, interfaceRevisionId: InterfaceRevisionId): string =>
`${atomId}\u0000${interfaceRevisionId}`;
const attachmentKey = (attachment: PersistentAttachment): string => `${attachment.kind}\u0000${attachment.id}`;
const issue = (
issues: CapabilityValidationIssue[],
code: CapabilityValidationIssueCode,
path: string,
message: string,
) => issues.push({ code, path, message });
const requireText = (issues: CapabilityValidationIssue[], value: string, path: string, label: string) => {
if (!hasText(value)) {
issue(issues, "required-value", path, `${label} is required`);
}
};
const typeLabel = (type: ValueType): string => {
switch (type.kind) {
case "variant":
return `variant:${type.id}{${Object.entries(type.cases)
.sort(([a], [b]) => a.localeCompare(b))
.map(([label, entry]) => `${label}:${entry.id}:${typeLabel(entry.value)}`)
.join(";")}}`;
case "builtin":
case "scalar":
return type.name;
case "message":
return `message:${type.descriptorId}`;
case "record":
return `record{${Object.keys(type.fields)
.sort()
.map((key) => `${key}:${typeLabel(type.fields[key])}`)
.join(";")}}`;
case "object-ref":
return type.expectation.kind === "atom"
? `object:atom:${type.expectation.atomId}`
: `object:interface:${type.expectation.interfaceRevisionId}`;
case "change":
return `change<${typeLabel(type.value)}>`;
case "optional":
return `optional<${typeLabel(type.value)}>`;
case "list":
return `list<${typeLabel(type.value)}>`;
}
};
export const valueTypesEqual = (left: ValueType, right: ValueType): boolean => {
if (left.kind !== right.kind) {
return false;
}
switch (left.kind) {
case "variant": {
const other = right as typeof left;
return (
left.id === other.id &&
Object.keys(left.cases).length === Object.keys(other.cases).length &&
Object.entries(left.cases).every(
([label, entry]) =>
Object.hasOwn(other.cases, label) &&
entry.id === other.cases[label].id &&
valueTypesEqual(entry.value, other.cases[label].value),
)
);
}
case "builtin":
case "scalar":
return left.name === (right as typeof left).name;
case "message":
return left.descriptorId === (right as typeof left).descriptorId;
case "record": {
const other = (right as typeof left).fields;
return (
Object.keys(left.fields).length === Object.keys(other).length &&
Object.entries(left.fields).every(
([key, value]) => Object.hasOwn(other, key) && valueTypesEqual(value, other[key]),
)
);
}
case "object-ref": {
const other = (right as typeof left).expectation;
if (left.expectation.kind !== other.kind) {
return false;
}
return left.expectation.kind === "atom"
? left.expectation.atomId === (other as typeof left.expectation).atomId
: left.expectation.interfaceRevisionId === (other as typeof left.expectation).interfaceRevisionId;
}
case "change":
case "optional":
case "list":
return valueTypesEqual(left.value, (right as typeof left).value);
}
};
const constraintsEqual = (left: EdgeEndpointConstraint, right: EdgeEndpointConstraint) =>
left.kind === right.kind &&
(left.kind === "atom"
? left.atomId === (right as typeof left).atomId
: left.interfaceRevisionId === (right as typeof left).interfaceRevisionId);
const sourceRequiredFields: Array<[keyof SourceRevision, string]> = [
["repository", "repository"],
["commit", "commit"],
];
const validateSource = (issues: CapabilityValidationIssue[], source: SourceRevision, path: string) => {
for (const [key, label] of sourceRequiredFields) {
if (!hasText(source[key])) {
issue(issues, "invalid-source", `${path}.${key}`, `Source ${label} is required`);
}
}
};
const validateValueType = (
issues: CapabilityValidationIssue[],
type: ValueType,
path: string,
indexes: Pick<ValidationIndexes, "atoms" | "interfaces">,
) => {
switch (type.kind) {
case "variant": {
requireText(issues, type.id, `${path}.id`, "Variant identity");
const entries = Object.entries(type.cases),
ids = new Set<string>();
if (!entries.length || entries.length > 256) issue(issues, "invalid-value-type", path, "Use 1–256 variant cases");
for (const [label, entry] of entries) {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(label)) issue(issues, "invalid-value-type", path, "Invalid variant label");
requireText(issues, entry.id, `${path}.${label}.id`, "Case identity");
if (ids.has(entry.id)) issue(issues, "invalid-value-type", path, "Duplicate case identity");
ids.add(entry.id);
validateValueType(issues, entry.value, `${path}.${label}`, indexes);
}
return;
}
case "record":
for (const [name, field] of Object.entries(type.fields)) {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
issue(issues, "invalid-value-type", path, "Invalid record field name");
validateValueType(issues, field, `${path}.fields.${name}`, indexes);
}
return;
case "builtin":
if (!["unit", "watch-handle"].includes(type.name))
issue(issues, "invalid-value-type", path, "Unknown builtin value type");
return;
case "scalar":
if (!["bool", "bytes", "double", "int32", "int64", "string", "uint32", "uint64"].includes(type.name))
issue(issues, "invalid-value-type", path, "Unknown scalar value type");
return;
case "message":
requireText(issues, type.descriptorId, `${path}.descriptorId`, "Message descriptor identity");
return;
case "change":
if (!isStorableType(type.value))
issue(
issues,
"invalid-value-type",
path,
"change<T> requires a storable materialized type, not references or nested changes",
);
validateValueType(issues, type.value, path + ".value", indexes);
return;
case "optional":
case "list":
validateValueType(issues, type.value, `${path}.value`, indexes);
return;
case "object-ref":
if (type.expectation.kind === "atom") {
if (!indexes.atoms.has(type.expectation.atomId)) {
issue(
issues,
"unresolved-reference",
`${path}.expectation.atomId`,
`Unknown atom ${type.expectation.atomId}`,
);
}
} else if (!indexes.interfaces.has(type.expectation.interfaceRevisionId)) {
issue(
issues,
"unresolved-reference",
`${path}.expectation.interfaceRevisionId`,
`Unknown interface revision ${type.expectation.interfaceRevisionId}`,
);
}
return;
default:
issue(
issues,
"invalid-value-type",
path,
"Installed contracts require closed value types; unresolved authoring expressions are not executable",
);
}
};
const validateConstraint = (
issues: CapabilityValidationIssue[],
constraint: EdgeEndpointConstraint,
path: string,
indexes: Pick<ValidationIndexes, "atoms" | "interfaces">,
) => {
if (constraint.kind === "atom") {
if (!indexes.atoms.has(constraint.atomId)) {
issue(issues, "unresolved-reference", `${path}.atomId`, `Unknown atom ${constraint.atomId}`);
}
} else if (!indexes.interfaces.has(constraint.interfaceRevisionId)) {
issue(
issues,
"unresolved-reference",
`${path}.interfaceRevisionId`,
`Unknown interface revision ${constraint.interfaceRevisionId}`,
);
}
};
const emitsEvents = (mode: InterfaceOperationMode) => mode === "watch-start" || mode === "subscribe";
const validateOperationEventShape = (
issues: CapabilityValidationIssue[],
operation: Pick<InterfaceOperation, "mode" | "eventType">,
path: string,
) => {
if (emitsEvents(operation.mode) && operation.eventType === undefined) {
issue(issues, "invalid-operation", `${path}.eventType`, `${operation.mode} operations require an event type`);
}
if (!emitsEvents(operation.mode) && operation.eventType !== undefined) {
issue(
issues,
"invalid-operation",
`${path}.eventType`,
`${operation.mode} operations must not declare an event type`,
);
}
};
interface OperationSignature {
mode: InterfaceOperationMode;
inputType: ValueType;
outputType: ValueType;
eventType?: ValueType;
}
const signaturesMatch = (operation: OperationSignature, implementation: OperationSignature) =>
operation.mode === implementation.mode &&
valueTypesEqual(operation.inputType, implementation.inputType) &&
valueTypesEqual(operation.outputType, implementation.outputType) &&
(operation.eventType === undefined
? implementation.eventType === undefined
: implementation.eventType !== undefined && valueTypesEqual(operation.eventType, implementation.eventType));
const describeSignature = (signature: OperationSignature) => {
const event = signature.eventType ? ` emits ${typeLabel(signature.eventType)}` : "";
return `${signature.mode} ${typeLabel(signature.inputType)} -> ${typeLabel(signature.outputType)}${event}`;
};
export const statePrimitiveSignature = (slot: StateSlotDefinition, primitive: StatePrimitive): OperationSignature => {
const writeType = slot.storagePolicy.kind === "crdt-document" ? slot.storagePolicy.updateType : slot.valueType;
switch (primitive) {
case "read":
return {
mode: "call",
inputType: valueType.unit,
outputType: slot.valueType,
};
case "edit":
return { mode: "call", inputType: { kind: "change", value: slot.valueType }, outputType: valueType.unit };
case "write":
return {
mode: "call",
inputType: writeType,
outputType: valueType.unit,
};
case "watch-start":
return {
mode: "watch-start",
inputType: valueType.unit,
outputType: valueType.watchHandle,
eventType: slot.valueType,
};
case "watch-stop":
return {
mode: "watch-stop",
inputType: valueType.watchHandle,
outputType: valueType.unit,
};
}
};
const constraintValueType = (constraint: EdgeEndpointConstraint): ValueType =>
constraint.kind === "atom"
? valueType.atomRef(constraint.atomId)
: valueType.interfaceRef(constraint.interfaceRevisionId);
export const cardinalityValueType = (constraint: EdgeEndpointConstraint, cardinality: EdgeCardinality): ValueType => {
const target = constraintValueType(constraint);
switch (cardinality) {
case "exactly-one":
return target;
case "optional-one":
return valueType.optional(target);
case "many":
case "many-unique":
return valueType.list(target);
}
};
const edgeProjection = (edge: EdgeDefinition, projectionId: EdgeProjectionId) => {
const index = edge.endpoints.findIndex((endpoint) => endpoint.projectionId === projectionId);
if (index !== 0 && index !== 1) {
return undefined;
}
const endpointIndex = index as 0 | 1;
return {
endpoint: edge.endpoints[endpointIndex],
target: edge.endpoints[endpointIndex === 0 ? 1 : 0],
endpointIndex,
};
};
export const edgePrimitiveSignature = (
edge: EdgeDefinition,
projectionId: EdgeProjectionId,
primitive: EdgePrimitive,
): OperationSignature | undefined => {
const projection = edgeProjection(edge, projectionId);
if (!projection) {
return undefined;
}
const targetType = constraintValueType(projection.target.constraint);
const resolvedType = cardinalityValueType(projection.target.constraint, projection.endpoint.cardinality);
switch (primitive) {
case "resolve":
return {
mode: "call",
inputType: valueType.unit,
outputType: resolvedType,
};
case "connect":
case "disconnect":
return {
mode: "call",
inputType:
primitive === "connect" && projection.endpoint.keyType
? {
kind: "record",
fields: {
mapKey: {
kind: "scalar",
name: projection.endpoint.keyType === "boolean" ? "bool" : projection.endpoint.keyType,
},
target: targetType,
},
}
: targetType,
outputType: valueType.unit,
};
case "watch-start":
return {
mode: "watch-start",
inputType: valueType.unit,
outputType: valueType.watchHandle,
eventType: resolvedType,
};
case "watch-stop":
return {
mode: "watch-stop",
inputType: valueType.watchHandle,
outputType: valueType.unit,
};
}
};
const atomSatisfiesConstraint = (
atomId: AtomId,
constraint: EdgeEndpointConstraint,
conformances: ReadonlyMap<string, Conformance>,
) =>
constraint.kind === "atom"
? constraint.atomId === atomId
: conformances.has(conformanceKey(atomId, constraint.interfaceRevisionId));
const interfaceImplies = (
indexes: ValidationIndexes,
actual: InterfaceRevisionId,
required: InterfaceRevisionId,
visited = new Set<string>(),
): boolean => {
if (actual === required) return true;
if (visited.has(actual)) return false;
visited.add(actual);
return (indexes.interfaces.get(actual)?.revision.requiredInterfaces ?? []).some((entry) =>
interfaceImplies(indexes, entry, required, visited),
);
};
const constraintSatisfiesConstraint = (
actual: EdgeEndpointConstraint,
required: EdgeEndpointConstraint,
indexes: ValidationIndexes,
) =>
constraintsEqual(actual, required) ||
(actual.kind === "atom" &&
required.kind === "interface" &&
atomSatisfiesConstraint(actual.atomId, required, indexes.conformances)) ||
(actual.kind === "interface" &&
required.kind === "interface" &&
interfaceImplies(indexes, actual.interfaceRevisionId, required.interfaceRevisionId));
const canAccessAttachment = (
owner: AttachmentOwner,
conformance: Pick<Conformance, "atomId" | "interfaceRevisionId"> | undefined,
) =>
owner.kind === "workspace" ||
(conformance !== undefined &&
owner.atomId === conformance.atomId &&
owner.interfaceRevisionId === conformance.interfaceRevisionId);
const collectIdentityIndexes = (
workspace: WorkspaceRevision,
issues: CapabilityValidationIssue[],
): ValidationIndexes => {
const atoms = new Map<string, WorkspaceRevision["atoms"][number]>();
for (const [index, atom] of workspace.atoms.entries()) {
const path = `atoms[${index}]`;
requireText(issues, atom.id, `${path}.id`, "Atom ID");
requireText(issues, atom.displayName, `${path}.displayName`, "Atom name");
if (atoms.has(atom.id)) {
issue(issues, "duplicate-atom", `${path}.id`, `Duplicate atom ${atom.id}`);
} else {
atoms.set(atom.id, atom);
}
}
const interfaces = new Map<string, InterfaceIndexEntry>();
for (const [interfaceIndex, revision] of workspace.interfaceImports.entries()) {
const path = `interfaceImports[${interfaceIndex}]`;
requireText(issues, revision.interfaceId, `${path}.interfaceId`, "Interface ID");
if (revision.template)
issue(issues, "invalid-value-type", path, "Unapplied generic interface cannot enter an installed workspace");
requireText(issues, revision.revisionId, `${path}.revisionId`, "Interface revision ID");
requireText(issues, revision.displayName, `${path}.displayName`, "Interface name");
validateSource(issues, revision.source, `${path}.source`);
if (revision.application) {
try {
if (
appliedInterfaceId(revision.application) !== revision.revisionId ||
revision.application.source.repository !== revision.source.repository ||
revision.application.source.commit !== revision.source.commit
)
issue(
issues,
"invalid-value-type",
path,
"Applied interface identity does not match its exact provenance and arguments",
);
} catch (error) {
if (!(error instanceof GenericTypeError)) throw error;
issue(issues, "invalid-value-type", path, error.message);
}
}
const memberIds = new Set<string>();
const operations = new Map<OperationId, InterfaceOperationEntry>();
for (const [memberIndex, member] of revision.members.entries()) {
const memberPath = `${path}.members[${memberIndex}]`;
if (
member.kind === "relationship" &&
member.keyType !== undefined &&
(!["string", "boolean", "int64"].includes(member.keyType) ||
!["many", "many-unique"].includes(member.cardinality))
)
issue(
issues,
"invalid-query-contract",
memberPath,
"Keyed relationships require many cardinality and string, boolean or int64 keys",
);
if (member.kind !== "operation" && member.kind !== "module" && member.queryRead) {
const contract = member.queryRead.execution;
const getter = member.kind === "value" ? "get" : "resolve";
if (contract !== "native" && contract !== "rpc-permitted" && contract !== "portable")
issue(issues, "invalid-query-contract", memberPath, "Unknown query execution contract");
if (member.operations.filter((operation) => operation.displayName === getter).length !== 1)
issue(issues, "invalid-query-contract", memberPath, `Queryable members require exactly one ${getter}`);
if (member.kind === "relationship" && contract === "rpc-permitted")
issue(issues, "invalid-query-contract", memberPath, "RPC query relationships are not supported");
if (member.kind === "value") {
const queryValue = (type: ValueType): boolean =>
type.kind === "scalar" ||
(type.kind === "variant" && isStorableType(type)) ||
((type.kind === "optional" || type.kind === "list") && queryValue(type.value)) ||
(type.kind === "record" &&
Object.keys(type.fields).length > 0 &&
Object.values(type.fields).every(queryValue));
if (!queryValue(member.valueType))
issue(
issues,
"invalid-query-contract",
memberPath,
"Queryable values require scalar leaves in optional, list, or nonempty record shapes",
);
}
}
if (member.kind === "value" && member.nativeField) {
const names = member.operations.map((o) => o.displayName);
if (
!isStorableType(member.valueType) ||
!names.includes("get") ||
names.some((n) => !["get", "set", "edit"].includes(n)) ||
(names.includes("set") && names.includes("edit"))
)
issue(
issues,
"invalid-state-binding",
memberPath,
"Fields require a storable value, a native getter and at most one setter or editor",
);
if (member.queryRead && member.queryRead.execution !== "native")
issue(issues, "invalid-query-contract", memberPath, "Fields cannot have package-backed query reads");
}
requireText(issues, member.id, `${memberPath}.id`, "Member ID");
requireText(issues, member.displayName, `${memberPath}.displayName`, "Member name");
if (memberIds.has(member.id)) {
issue(issues, "duplicate-interface-member", `${memberPath}.id`, `Duplicate member ${member.id}`);
}
memberIds.add(member.id);
for (const [operationIndex, operation] of member.operations.entries()) {
const operationPath = `${memberPath}.operations[${operationIndex}]`;
requireText(issues, operation.id, `${operationPath}.id`, "Operation ID");
requireText(issues, operation.displayName, `${operationPath}.displayName`, "Operation name");
validateOperationEventShape(issues, operation, operationPath);
if (operations.has(operation.id)) {
issue(issues, "duplicate-interface-operation", `${operationPath}.id`, `Duplicate operation ${operation.id}`);
} else {
operations.set(operation.id, { operation, member });
}
}
}
if (interfaces.has(revision.revisionId)) {
issue(
issues,
"duplicate-interface-revision",
`${path}.revisionId`,
`Duplicate interface revision ${revision.revisionId}`,
);
} else {
interfaces.set(revision.revisionId, { revision, operations });
}
}
const packages = new Map<string, PackageIndexEntry>();
for (const [packageIndex, revision] of workspace.packageImports.entries()) {
const path = `packageImports[${packageIndex}]`;
if (
revision.semanticMajor !== undefined &&
(!Number.isSafeInteger(revision.semanticMajor) || revision.semanticMajor < 1)
) {
issue(
issues,
"invalid-semantic-major",
`${path}.semanticMajor`,
"Semantic major must be a positive safe integer",
);
}
requireText(issues, revision.packageId, `${path}.packageId`, "Package ID");
requireText(issues, revision.revisionId, `${path}.revisionId`, "Package revision ID");
requireText(issues, revision.displayName, `${path}.displayName`, "Package name");
validateSource(issues, revision.source, `${path}.source`);
const exports = new Map<string, PackageExport>();
for (const [exportIndex, entry] of revision.exports.entries()) {
const exportPath = `${path}.exports[${exportIndex}]`;
if (entry.application) {
try {
const definition = revision.genericExports?.find((candidate) => candidate.id === entry.application!.exportId);
if (!definition) throw new Error("Missing generic export definition");
const expected = specializePackageExport(revision, definition, entry.application.arguments, {
arguments: new Map(),
self: entry.application.self,
applyInterface: (id, args) => {
const applied = workspace.interfaceImports.find(
(contract) =>
contract.application?.definitionId === id && isDeepStrictEqual(contract.application.arguments, args),
);
if (applied) return applied.revisionId;
const concrete = workspace.interfaceImports.find((contract) => contract.revisionId === id);
if (concrete && !concrete.template && args.length === 0) return id;
throw new Error(`Missing closed interface application ${id}`);
},
// Retained obligations are discharged against candidate conformances below.
implementsInterface: (target, required) =>
(revision.argumentRequirements ?? []).some(
(obligation) => obligation.required === required && isDeepStrictEqual(obligation.target, target),
),
});
if (!isDeepStrictEqual(entry, expected))
throw new Error("Specialized export differs from its definition, source or arguments");
} catch (error) {
issue(issues, "invalid-operation", exportPath, error instanceof Error ? error.message : String(error));
}
}
requireText(issues, entry.id, `${exportPath}.id`, "Package export ID");
requireText(issues, entry.displayName, `${exportPath}.displayName`, "Package export name");
if (exports.has(entry.id)) {
issue(issues, "duplicate-package-export", `${exportPath}.id`, `Duplicate package export ${entry.id}`);
} else {
exports.set(entry.id, entry);
}
const portIds = new Set<string>();
for (const [portIndex, port] of entry.dependencyPorts.entries()) {
const portPath = `${exportPath}.dependencyPorts[${portIndex}]`;
requireText(issues, port.id, `${portPath}.id`, "Dependency port ID");
requireText(issues, port.displayName, `${portPath}.displayName`, "Dependency port name");
if (portIds.has(port.id)) {
issue(issues, "duplicate-dependency-port", `${portPath}.id`, `Duplicate dependency port ${port.id}`);
}
portIds.add(port.id);
}
}
if (packages.has(revision.revisionId)) {
issue(
issues,
"duplicate-package-revision",
`${path}.revisionId`,
`Duplicate package revision ${revision.revisionId}`,
);
} else {
packages.set(revision.revisionId, { revision, exports });
}
}
const conformances = new Map<string, Conformance>();
const conformancePaths = new Map<string, string>();
const conformanceIds = new Set<string>();
for (const [index, conformance] of workspace.conformances.entries()) {
const path = `conformances[${index}]`;
if (
conformance.semanticMajor !== undefined &&
(!Number.isSafeInteger(conformance.semanticMajor) || conformance.semanticMajor < 1)
) {
issue(
issues,
"invalid-semantic-major",
`${path}.semanticMajor`,
"Semantic major must be a positive safe integer",
);
}
if (conformance.id !== undefined) {
requireText(issues, conformance.id, `${path}.id`, "Conformance ID");
if (conformanceIds.has(conformance.id))
issue(issues, "duplicate-conformance-id", `${path}.id`, `Duplicate conformance ID ${conformance.id}`);
conformanceIds.add(conformance.id);
}
const key = conformanceKey(conformance.atomId, conformance.interfaceRevisionId);
if (conformances.has(key)) {
issue(
issues,
"duplicate-conformance",
path,
`Duplicate conformance for ${conformance.atomId} as ${conformance.interfaceRevisionId}: ` +
[conformances.get(key)!, conformance]
.map((candidate) =>
candidate.ruleOrigin
? `rule ${candidate.ruleOrigin.ruleId} from ${candidate.ruleOrigin.sourceInterfaceRevisionId} at line ${candidate.ruleOrigin.line}`
: `conformance ${candidate.id ?? "(unnamed)"}`,
)
.join(" conflicts with "),
);
} else {
conformances.set(key, conformance);
conformancePaths.set(key, path);
}
}
const constructors = new Map<AtomId, AtomConstructorBinding>();
for (const [index, constructor] of workspace.constructors.entries()) {
const path = `constructors[${index}]`;
if (constructors.has(constructor.atomId)) {
issue(issues, "duplicate-constructor", path, `Atom ${constructor.atomId} has more than one constructor`);
} else {
constructors.set(constructor.atomId, constructor);
}
}
const attachments = new Map<string, AttachmentIndexEntry>();
const projections = new Map<string, { edge: EdgeDefinition; endpoint: EdgeEndpoint; endpointIndex: 0 | 1 }>();
const addAttachment = (attachment: PersistentAttachment, owner: AttachmentOwner, path: string) => {
const key = attachmentKey(attachment);
if (attachments.has(key)) {
issue(issues, "duplicate-attachment", `${path}.id`, `Duplicate ${attachment.kind} attachment ${attachment.id}`);
} else {
attachments.set(key, { attachment, owner, path });
}
if (attachment.kind === "edge") {
for (const [endpointIndex, endpoint] of attachment.endpoints.entries()) {
const endpointPath = `${path}.endpoints[${endpointIndex}]`;
if (projections.has(endpoint.projectionId)) {
issue(
issues,
"duplicate-projection",
`${endpointPath}.projectionId`,
`Duplicate edge projection ${endpoint.projectionId}`,
);
} else {
projections.set(endpoint.projectionId, {
edge: attachment,
endpoint,
endpointIndex: endpointIndex as 0 | 1,
});
}
}
}
};
for (const [index, attachment] of workspace.sharedAttachments.entries()) {
addAttachment(attachment, { kind: "workspace" }, `sharedAttachments[${index}]`);
}
for (const [conformanceIndex, conformance] of workspace.conformances.entries()) {
for (const [attachmentIndex, attachment] of conformance.privateAttachments.entries()) {
addAttachment(
attachment,
{
kind: "conformance",
atomId: conformance.atomId,
interfaceRevisionId: conformance.interfaceRevisionId,
},
`conformances[${conformanceIndex}].privateAttachments[${attachmentIndex}]`,
);
}
}
return {
atoms,
interfaces,
packages,
conformances,
conformancePaths,
attachments,
projections,
constructors,
};
};
const validateInterfaces = (
workspace: WorkspaceRevision,
issues: CapabilityValidationIssue[],
indexes: ValidationIndexes,
) => {
for (const [packageIndex, pkg] of workspace.packageImports.entries()) {
for (const obligation of pkg.argumentRequirements ?? []) {
const satisfied =
obligation.target.kind === "atom"
? indexes.conformances.has(conformanceKey(obligation.target.atomId, obligation.required))
: interfaceImplies(indexes, obligation.target.interfaceRevisionId, obligation.required);
if (!satisfied)
issue(
issues,
"unsatisfied-interface",
`packageImports[${packageIndex}]`,
`Generic argument does not implement ${obligation.required}`,
);
}
}
for (const [interfaceIndex, revision] of workspace.interfaceImports.entries()) {
const path = `interfaceImports[${interfaceIndex}]`;
for (const required of revision.requiredInterfaces ?? []) {
if (!indexes.interfaces.has(required))
issue(issues, "unresolved-reference", path, `Unknown prerequisite interface ${required}`);
else if (interfaceImplies(indexes, required, revision.revisionId))
issue(
issues,
"cyclic-conformance-requirement",
path,
`Cyclic prerequisite involving ${revision.revisionId} and ${required}`,
);
}
for (const obligation of revision.argumentRequirements ?? []) {
const satisfied =
obligation.target.kind === "atom"
? indexes.conformances.has(conformanceKey(obligation.target.atomId, obligation.required))
: interfaceImplies(indexes, obligation.target.interfaceRevisionId, obligation.required);
if (!satisfied)
issue(issues, "unsatisfied-interface", path, `Generic argument does not implement ${obligation.required}`);
}
for (const [memberIndex, member] of revision.members.entries()) {
const memberPath = `${path}.members[${memberIndex}]`;
if (member.kind === "value") {
validateValueType(issues, member.valueType, `${memberPath}.valueType`, indexes);
} else if (member.kind === "relationship") {
validateConstraint(issues, member.target, `${memberPath}.target`, indexes);
} else if (member.kind === "module") {
if (member.subject) validateConstraint(issues, member.subject, `${memberPath}.subject`, indexes);
} else {
validateValueType(issues, member.inputType, `${memberPath}.inputType`, indexes);
validateValueType(issues, member.outputType, `${memberPath}.outputType`, indexes);
}
for (const [operationIndex, operation] of member.operations.entries()) {
const operationPath = `${memberPath}.operations[${operationIndex}]`;
validateValueType(issues, operation.inputType, `${operationPath}.inputType`, indexes);
validateValueType(issues, operation.outputType, `${operationPath}.outputType`, indexes);
if (operation.eventType) {
validateValueType(issues, operation.eventType, `${operationPath}.eventType`, indexes);
}
}
}
}
};
const validateAttachments = (issues: CapabilityValidationIssue[], indexes: ValidationIndexes) => {
for (const { attachment, owner, path } of indexes.attachments.values()) {
requireText(issues, attachment.id, `${path}.id`, `${attachment.kind} attachment ID`);
requireText(issues, attachment.displayName, `${path}.displayName`, `${attachment.kind} name`);
if (attachment.kind === "state") {
if (!indexes.atoms.has(attachment.attachedTo)) {
issue(issues, "unresolved-reference", `${path}.attachedTo`, `Unknown atom ${attachment.attachedTo}`);
}
validateValueType(issues, attachment.valueType, `${path}.valueType`, indexes);
const containsRpcType = (type: ValueType): boolean =>
type.kind === "object-ref" ||
type.kind === "change" ||
(type.kind === "record" && Object.values(type.fields).some(containsRpcType)) ||
(type.kind === "variant" && Object.values(type.cases).some((entry) => containsRpcType(entry.value))) ||
((type.kind === "optional" || type.kind === "list") && containsRpcType(type.value));
if (
(!["journal", "subscription"].includes(attachment.storagePolicy.kind) &&
containsRpcType(attachment.valueType)) ||
(attachment.storagePolicy.kind === "crdt-document" && containsRpcType(attachment.storagePolicy.updateType))
) {
issue(
issues,
"invalid-attachment",
`${path}.valueType`,
"Managed object references belong in graph relationships; captured changes are transport-only",
);
}
if (attachment.storagePolicy.kind === "crdt-document") {
validateValueType(issues, attachment.storagePolicy.updateType, `${path}.storagePolicy.updateType`, indexes);
}
if (attachment.storagePolicy.kind === "journal") {
if (!journalPayloadType(attachment.valueType) || attachment.defaultValue !== undefined)
issue(issues, "invalid-attachment", path, "Journals require finite typed payloads and start empty");
const limits = attachment.storagePolicy.limits;
const ceilings = {
maxEntries: 10000,
maxBytes: 1048576,
maxPublicationBytes: 262144,
maxProtectedSubscriptions: 256,
};
if (
!limits ||
Object.keys(limits).length !== 4 ||
Object.entries(ceilings).some(
([key, ceiling]) =>
!Number.isSafeInteger(limits[key as keyof typeof limits]) ||
limits[key as keyof typeof limits] < 1 ||
limits[key as keyof typeof limits] > ceiling,
) ||
limits.maxPublicationBytes > limits.maxBytes
)
issue(issues, "invalid-attachment", path, "Invalid bounded journal limits");
}
if (attachment.storagePolicy.kind === "subscription") {
validateValueType(issues, attachment.storagePolicy.payload, `${path}.storagePolicy.payload`, indexes);
if (
!journalPayloadType(attachment.valueType) ||
!journalPayloadType(attachment.storagePolicy.payload) ||
attachment.defaultValue !== undefined
)
issue(
issues,
"invalid-attachment",
path,
"Subscriptions require finite typed payloads/parameters and start unattached",
);
}
if (owner.kind === "conformance") {
if (attachment.attachedTo !== owner.atomId) {
issue(
issues,
"invalid-attachment",
`${path}.attachedTo`,
`Private state must attach to its conformance atom ${owner.atomId}`,
);
}
}
continue;
}
if (attachment.endpoints.length !== 2) {
issue(issues, "invalid-attachment", `${path}.endpoints`, `Edge ${attachment.id} must have exactly two endpoints`);
}
for (const [endpointIndex, endpoint] of attachment.endpoints.entries()) {
const endpointPath = `${path}.endpoints[${endpointIndex}]`;
if (
endpoint.keyType !== undefined &&
(!["string", "boolean", "int64"].includes(endpoint.keyType) ||
endpoint.ordered ||
!["many", "many-unique"].includes(endpoint.cardinality))
) {
issue(
issues,
"invalid-attachment",
`${endpointPath}.keyType`,
"Keyed projections require string/boolean/int64 keys, many cardinality, and no ordering",
);
}
requireText(issues, endpoint.projectionId, `${endpointPath}.projectionId`, "Projection ID");
if (endpoint.onDelete !== undefined && !["restrict", "detach", "cascade-other"].includes(endpoint.onDelete)) {
issue(
issues,
"invalid-attachment",
`${endpointPath}.onDelete`,
"Deletion policy must be restrict, detach, or cascade-other",
);
}
requireText(issues, endpoint.displayName, `${endpointPath}.displayName`, "Projection name");
validateConstraint(issues, endpoint.constraint, `${endpointPath}.constraint`, indexes);
}
if (attachment.endpoints.every((endpoint) => endpoint.keyType))
issue(
issues,
"invalid-attachment",
`${path}.endpoints`,
"A v0 map has one keyed projection and one unkeyed inverse",
);
if (owner.kind === "conformance") {
if (
!attachment.endpoints.some((endpoint) =>
atomSatisfiesConstraint(owner.atomId, endpoint.constraint, indexes.conformances),
)
) {
issue(
issues,
"invalid-attachment",
`${path}.endpoints`,
`Private edge has no endpoint compatible with conformance atom ${owner.atomId}`,
);
}
}
}
};
const uniquePrimitiveList = (issues: CapabilityValidationIssue[], primitives: readonly string[], path: string) => {
if (primitives.length === 0) {
issue(issues, "invalid-dependency-binding", path, "Dependency port requires at least one primitive");
}
const seen = new Set<string>();
for (const primitive of primitives) {
if (seen.has(primitive)) {
issue(issues, "invalid-dependency-binding", path, `Dependency primitive ${primitive} is listed more than once`);
}
seen.add(primitive);
}
};
const validatePackages = (
workspace: WorkspaceRevision,
issues: CapabilityValidationIssue[],
indexes: ValidationIndexes,
) => {
for (const [packageIndex, revision] of workspace.packageImports.entries()) {
const path = `packageImports[${packageIndex}]`;
for (const [exportIndex, entry] of revision.exports.entries()) {
const exportPath = `${path}.exports[${exportIndex}]`;
validateValueType(issues, entry.inputType, `${exportPath}.inputType`, indexes);
validateValueType(issues, entry.outputType, `${exportPath}.outputType`, indexes);
if (entry.preparation) {
const port = entry.dependencyPorts.find((port) => port.id === entry.preparation!.port);
if (
entry.execution?.kind !== "transaction" ||
entry.kind === "constructor" ||
(entry.kind === "operation" && entry.mode !== "call") ||
!entry.preparation.key ||
port?.requirement.kind !== "creation" ||
!valueTypesEqual(port.requirement.inputType, entry.inputType) ||
entry.outputType.kind !== "object-ref" ||
(entry.outputType.expectation.kind === "atom" &&
entry.outputType.expectation.atomId !== port.requirement.atomId)
)
issue(
issues,
"invalid-dependency-binding",
exportPath,
"Prepared creation requires a transaction returning its declared creation, with matching captured input types and a fixed key",
);
}
if (entry.acceptanceExportId !== undefined) {
const check = revision.exports.find((candidate) => candidate.id === entry.acceptanceExportId);
if (
entry.execution?.kind !== "transaction" ||
!entry.execution.targets.includes("server") ||
!check ||
check.execution?.kind !== "snapshot" ||
check.execution.targets.length !== 1 ||
check.execution.targets[0] !== "server" ||
check.acceptanceExportId !== undefined ||
entry.kind !== check.kind ||
entry.kind === "constructor" ||
!valueTypesEqual(entry.inputType, check.inputType) ||
!valueTypesEqual(check.outputType, valueType.unit) ||
(entry.kind === "operation" &&
(check.kind !== "operation" ||
check.mode !== "call" ||
!isDeepStrictEqual(entry.receiverRequirement, check.receiverRequirement)))
)
issue(
issues,
"invalid-operation",
`${exportPath}.acceptanceExportId`,
"Acceptance requires a server snapshot export with matching kind, receiver and input, unit output, and no further acceptance hook",
);
}
if (entry.kind === "operation") {
validateOperationEventShape(issues, entry, exportPath);
if (entry.eventType) {
validateValueType(issues, entry.eventType, `${exportPath}.eventType`, indexes);
}
if (entry.receiverRequirement.kind === "exact-atom") {
if (!indexes.atoms.has(entry.receiverRequirement.atomId)) {
issue(
issues,
"unresolved-reference",
`${exportPath}.receiverRequirement.atomId`,
`Unknown atom ${entry.receiverRequirement.atomId}`,
);
}
} else if (entry.receiverRequirement.kind === "all-interfaces") {
const seen = new Set<string>();
for (const [
requirementIndex,
interfaceRevisionId,
] of entry.receiverRequirement.interfaceRevisionIds.entries()) {
const requirementPath = `${exportPath}.receiverRequirement.interfaceRevisionIds[${requirementIndex}]`;
if (!indexes.interfaces.has(interfaceRevisionId)) {
issue(
issues,
"unresolved-reference",
requirementPath,
`Unknown interface revision ${interfaceRevisionId}`,
);
}
if (seen.has(interfaceRevisionId)) {
issue(
issues,
"invalid-operation",
requirementPath,
`Receiver interface ${interfaceRevisionId} is duplicated`,
);
}
seen.add(interfaceRevisionId);
}
}
} else if (entry.kind === "constructor") {
if (!indexes.atoms.has(entry.constructsAtom)) {
issue(issues, "unresolved-reference", `${exportPath}.constructsAtom`, `Unknown atom ${entry.constructsAtom}`);
}
const expected = valueType.atomRef(entry.constructsAtom);
if (!valueTypesEqual(entry.outputType, expected)) {
issue(
issues,
"invalid-constructor",
`${exportPath}.outputType`,
`Constructor must return ${typeLabel(expected)}, not ${typeLabel(entry.outputType)}`,
);
}
}
for (const [portIndex, port] of entry.dependencyPorts.entries()) {
const portPath = `${exportPath}.dependencyPorts[${portIndex}]`;
switch (port.requirement.kind) {
case "reflection":
if (
!port.requirement.selectors.length ||
new Set(port.requirement.selectors).size !== port.requirement.selectors.length ||
port.requirement.selectors.some(
(kind) =>
!["active", "atoms", "interfaces", "conformances", "queries", "indexes", "presentations"].includes(
kind,
),
)
)
issue(issues, "invalid-dependency-binding", portPath, "Reflection requires distinct supported selectors");
break;
case "query":
if (
!indexes.packages
.get(port.requirement.packageRevisionId)
?.revision.queries?.some(
(query) => port.requirement.kind === "query" && query.id === port.requirement.queryId,
)
)
issue(
issues,
"unresolved-reference",
portPath,
`Unknown query ${port.requirement.packageRevisionId}:${port.requirement.queryId}`,
);
break;
case "state":
validateValueType(issues, port.requirement.valueType, `${portPath}.requirement.valueType`, indexes);
uniquePrimitiveList(issues, port.requirement.primitives, `${portPath}.requirement.primitives`);
break;
case "edge":
validateConstraint(issues, port.requirement.target, `${portPath}.requirement.target`, indexes);
uniquePrimitiveList(issues, port.requirement.primitives, `${portPath}.requirement.primitives`);
break;
case "interface":
if (!indexes.interfaces.has(port.requirement.interfaceRevisionId)) {
issue(
issues,
"unresolved-reference",
`${portPath}.requirement.interfaceRevisionId`,
`Unknown interface revision ${port.requirement.interfaceRevisionId}`,
);
}
break;
case "creation":
case "constructor":
if (port.requirement.inputType)
validateValueType(issues, port.requirement.inputType, `${portPath}.requirement.inputType`, indexes);
if (!indexes.atoms.has(port.requirement.atomId)) {
issue(
issues,
"unresolved-reference",
`${portPath}.requirement.atomId`,
`Unknown atom ${port.requirement.atomId}`,
);
}
}
}
}
}
};
const findAttachment = <Kind extends PersistentAttachment["kind"]>(
indexes: ValidationIndexes,
kind: Kind,
id: Kind extends "state" ? SlotId : EdgeTypeId,
): AttachmentIndexEntry | undefined => indexes.attachments.get(`${kind}\u0000${id}`);
const validateAttachmentAccess = (
issues: CapabilityValidationIssue[],
entry: AttachmentIndexEntry,
conformance: Pick<Conformance, "atomId" | "interfaceRevisionId"> | undefined,
path: string,
) => {
if (!canAccessAttachment(entry.owner, conformance)) {
issue(
issues,
"private-attachment-access",
path,
`Attachment ${entry.attachment.id} is private to conformance ${
entry.owner.kind === "conformance" ? `${entry.owner.atomId} as ${entry.owner.interfaceRevisionId}` : ""
}`,
);
return false;
}
return true;
};
const validateTraversal = (
issues: CapabilityValidationIssue[],
indexes: ValidationIndexes,
atomId: AtomId,
traversal: { edgeTypeId: EdgeTypeId; projectionId: EdgeProjectionId },
path: string,
conformance?: Pick<Conformance, "atomId" | "interfaceRevisionId">,
) => {
const attachment = findAttachment(indexes, "edge", traversal.edgeTypeId);
if (!attachment || attachment.attachment.kind !== "edge") {
issue(issues, "invalid-dependency-binding", `${path}.edgeTypeId`, `Unknown traversal edge ${traversal.edgeTypeId}`);
return undefined;
}
const projection = edgeProjection(attachment.attachment, traversal.projectionId);
if (!projection) {
issue(
issues,
"invalid-dependency-binding",
`${path}.projectionId`,
`Traversal projection ${traversal.projectionId} is not on edge ${traversal.edgeTypeId}`,
);
return undefined;
}
if (!projection.endpoint.publicTraversal)
validateAttachmentAccess(issues, attachment, conformance, `${path}.edgeTypeId`);
if (!atomSatisfiesConstraint(atomId, projection.endpoint.constraint, indexes.conformances)) {
issue(
issues,
"invalid-dependency-binding",
`${path}.projectionId`,
`Traversal projection ${traversal.projectionId} cannot originate at receiver atom ${atomId}`,
);
}
if (projection.endpoint.cardinality !== "exactly-one") {
issue(
issues,
"invalid-dependency-binding",
`${path}.projectionId`,
"Dependency traversal must select exactly one object",
);
}
return projection;
};
const validateBoundDependencies = (
issues: CapabilityValidationIssue[],
params: {
dependencies: BoundDependency[];
dependencyPorts: DependencyPort[];
atomId: AtomId;
conformance?: Pick<Conformance, "atomId" | "interfaceRevisionId">;
path: string;
indexes: ValidationIndexes;
requirementGraph: Map<string, Set<string>>;
graphSourceKey?: string;
},
) => {
const ports = new Map(params.dependencyPorts.map((port) => [port.id, port]));
const bindings = new Map<string, BoundDependency>();
for (const [index, dependency] of params.dependencies.entries()) {
const path = `${params.path}.dependencies[${index}]`;
if (bindings.has(dependency.portId)) {
issue(
issues,
"invalid-dependency-binding",
`${path}.portId`,
`Dependency port ${dependency.portId} is bound more than once`,
);
continue;
}
bindings.set(dependency.portId, dependency);
const port = ports.get(dependency.portId);
if (!port) {
issue(issues, "invalid-dependency-binding", `${path}.portId`, `Unknown dependency port ${dependency.portId}`);
continue;
}
const requirement = port.requirement;
const binding = dependency.binding;
if (requirement.kind !== binding.kind) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding`,
`Port ${port.id} requires ${requirement.kind}, not ${binding.kind}`,
);
continue;
}
switch (requirement.kind) {
case "reflection": {
const selected = binding as Extract<DependencyBinding, { kind: "reflection" }>;
if (
selected.via ||
JSON.stringify([...selected.selectors].sort()) !== JSON.stringify([...requirement.selectors].sort())
)
issue(
issues,
"invalid-dependency-binding",
path,
"Reflection requires an explicit workspace binding with exactly the declared selectors",
);
break;
}
case "query": {
const selected = binding as Extract<DependencyBinding, { kind: "query" }>;
const query = params.indexes.packages
.get(requirement.packageRevisionId)
?.revision.queries?.find((query) => query.id === requirement.queryId);
if (
!query ||
selected.queryId !== requirement.queryId ||
selected.packageRevisionId !== requirement.packageRevisionId
) {
issue(issues, "invalid-dependency-binding", path, "Query port must bind its exact checked query export");
break;
}
if (query.root.kind === "workspace-interface") {
if (selected.scope !== "workspace" || selected.via)
issue(
issues,
"invalid-dependency-binding",
path,
"Workspace-interface queries require an explicit workspace scope without an object traversal",
);
break;
}
if (selected.scope !== "object") {
issue(issues, "invalid-dependency-binding", path, "Object-root queries require an object dependency binding");
break;
}
const traversal = selected.via
? validateTraversal(
issues,
params.indexes,
params.atomId,
selected.via,
`${path}.binding.via`,
params.conformance,
)
: undefined;
const targets = selected.via
? traversal
? [...params.indexes.atoms.keys()].filter((atom) =>
atomSatisfiesConstraint(atom as AtomId, traversal.target.constraint, params.indexes.conformances),
)
: []
: [params.atomId];
for (const atom of targets)
if (!params.indexes.conformances.has(conformanceKey(atom as AtomId, query.root.interface)))
issue(
issues,
"unsatisfied-interface",
path,
`Query root ${atom} does not conform to ${query.root.interface}`,
);
break;
}
case "state": {
const stateBinding = binding as Extract<DependencyBinding, { kind: "state" }>;
const attachment = findAttachment(params.indexes, "state", stateBinding.slotId);
if (!attachment || attachment.attachment.kind !== "state") {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.slotId`,
`Unknown state attachment ${stateBinding.slotId}`,
);
break;
}
validateAttachmentAccess(issues, attachment, params.conformance, `${path}.binding.slotId`);
if (["journal", "subscription"].includes(attachment.attachment.storagePolicy.kind))
issue(
issues,
"invalid-dependency-binding",
path,
"Journal storage requires checked journal interface calls, not ordinary state ports",
);
const traversal = stateBinding.via
? validateTraversal(
issues,
params.indexes,
params.atomId,
stateBinding.via,
`${path}.binding.via`,
params.conformance,
)
: undefined;
if (
stateBinding.via
? traversal &&
!atomSatisfiesConstraint(
attachment.attachment.attachedTo,
traversal.target.constraint,
params.indexes.conformances,
)
: attachment.attachment.attachedTo !== params.atomId
) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.slotId`,
stateBinding.via
? `State ${stateBinding.slotId} is not attached to the traversal target`
: `State ${stateBinding.slotId} is not attached to receiver atom ${params.atomId}`,
);
}
if (
requirement.primitives.includes("edit") &&
(attachment.attachment.storagePolicy.kind !== "crdt-document" ||
!valueTypesEqual(attachment.attachment.valueType, attachment.attachment.storagePolicy.updateType))
)
issue(issues, "invalid-dependency-binding", path, "Edit ports require a matching CRDT slot");
if (!valueTypesEqual(requirement.valueType, attachment.attachment.valueType)) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.slotId`,
`State port expects ${typeLabel(requirement.valueType)}, not ${typeLabel(attachment.attachment.valueType)}`,
);
}
break;
}
case "edge": {
const edgeBinding = binding as Extract<DependencyBinding, { kind: "edge" }>;
const attachment = findAttachment(params.indexes, "edge", edgeBinding.edgeTypeId);
if (!attachment || attachment.attachment.kind !== "edge") {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.edgeTypeId`,
`Unknown edge attachment ${edgeBinding.edgeTypeId}`,
);
break;
}
validateAttachmentAccess(issues, attachment, params.conformance, `${path}.binding.edgeTypeId`);
const projection = edgeProjection(attachment.attachment, edgeBinding.projectionId);
if (!projection) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.projectionId`,
`Projection ${edgeBinding.projectionId} is not on edge ${edgeBinding.edgeTypeId}`,
);
break;
}
if (projection.target.keyType && requirement.primitives.includes("connect"))
issue(
issues,
"invalid-dependency-binding",
path,
"Connect through the keyed projection so the caller supplies a map key",
);
const traversal = edgeBinding.via
? validateTraversal(
issues,
params.indexes,
params.atomId,
edgeBinding.via,
`${path}.binding.via`,
params.conformance,
)
: undefined;
const originMatches = edgeBinding.via
? traversal &&
(projection.endpoint.constraint.kind === "atom"
? atomSatisfiesConstraint(
projection.endpoint.constraint.atomId,
traversal.target.constraint,
params.indexes.conformances,
)
: constraintsEqual(projection.endpoint.constraint, traversal.target.constraint))
: atomSatisfiesConstraint(params.atomId, projection.endpoint.constraint, params.indexes.conformances);
if (!originMatches) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.projectionId`,
edgeBinding.via
? `Projection ${edgeBinding.projectionId} cannot originate at the traversal target`
: `Projection ${edgeBinding.projectionId} cannot originate at receiver atom ${params.atomId}`,
);
}
if (
!constraintSatisfiesConstraint(projection.target.constraint, requirement.target, params.indexes) ||
requirement.cardinality !== projection.endpoint.cardinality ||
requirement.keyType !== projection.endpoint.keyType
) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding`,
`Edge port target, cardinality or key type does not match projection ${edgeBinding.projectionId}`,
);
}
break;
}
case "interface": {
const interfaceBinding = binding as Extract<DependencyBinding, { kind: "interface" }>;
if (interfaceBinding.receiver !== requirement.receiver || (interfaceBinding.receiver && interfaceBinding.via)) {
issue(
issues,
"invalid-dependency-binding",
path,
"Reference-selected interface ports require an explicit matching from input binding",
);
break;
}
if (interfaceBinding.interfaceRevisionId !== requirement.interfaceRevisionId) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.interfaceRevisionId`,
`Interface port requires ${requirement.interfaceRevisionId}`,
);
break;
}
const selected = requirement.operations?.map(
(id) => params.indexes.interfaces.get(requirement.interfaceRevisionId)?.operations.get(id)?.operation,
);
if (
selected?.some((op) => op?.scope === "class") &&
(interfaceBinding.via || interfaceBinding.receiver || selected.some((op) => op?.scope !== "class"))
) {
issue(
issues,
"invalid-dependency-binding",
path,
"Static interface dependencies require a receiverless selection of class operations on the bound atom",
);
break;
}
// The reference supplies its own checked witness at invocation time.
// This is not a requirement that the current receiver implements the interface.
if (interfaceBinding.receiver === "input") break;
const traversal = interfaceBinding.via
? validateTraversal(
issues,
params.indexes,
params.atomId,
interfaceBinding.via,
`${path}.binding.via`,
params.conformance,
)
: undefined;
const candidateAtoms = interfaceBinding.via
? traversal
? ([...params.indexes.atoms.keys()].filter((atomId) =>
atomSatisfiesConstraint(atomId as AtomId, traversal.target.constraint, params.indexes.conformances),
) as AtomId[])
: []
: [params.atomId];
for (const atomId of candidateAtoms) {
const targetKey = conformanceKey(atomId, requirement.interfaceRevisionId);
if (!params.indexes.conformances.has(targetKey)) {
issue(
issues,
"unsatisfied-interface",
`${path}.binding.interfaceRevisionId`,
`${interfaceBinding.via ? "Related" : "Receiver"} atom ${atomId} does not conform to ${requirement.interfaceRevisionId}`,
);
} else if (params.graphSourceKey) {
params.requirementGraph.get(params.graphSourceKey)?.add(targetKey);
}
}
break;
}
case "constructor": {
const constructorBinding = binding as Extract<DependencyBinding, { kind: "constructor" }>;
if (constructorBinding.atomId !== requirement.atomId) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.atomId`,
`Constructor port requires atom ${requirement.atomId}`,
);
}
if (!params.indexes.constructors.has(requirement.atomId)) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.atomId`,
`Atom ${requirement.atomId} has no constructor binding`,
);
}
if (requirement.inputType) {
const selected = params.indexes.constructors.get(requirement.atomId);
const implementation = selected
? params.indexes.packages.get(selected.packageRevisionId)?.exports.get(selected.exportId)
: undefined;
if (implementation && !valueTypesEqual(requirement.inputType, implementation.inputType)) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding.atomId`,
`Constructor port requires input ${typeLabel(requirement.inputType)}, but selected constructor accepts ${typeLabel(implementation.inputType)}`,
);
}
}
break;
}
}
}
for (const port of params.dependencyPorts) {
if (!bindings.has(port.id)) {
issue(
issues,
"invalid-dependency-binding",
`${params.path}.dependencies`,
`Missing binding for dependency port ${port.id}`,
);
}
}
};
const validatePackageReceiver = (
issues: CapabilityValidationIssue[],
params: {
entry: PackageOperationExport;
atomId: AtomId;
path: string;
indexes: ValidationIndexes;
requirementGraph: Map<string, Set<string>>;
graphSourceKey: string;
},
) => {
const receiver = params.entry.receiverRequirement;
if (receiver.kind === "exact-atom" && receiver.atomId !== params.atomId) {
issue(
issues,
"incompatible-package-receiver",
params.path,
`Package export requires atom ${receiver.atomId}, not ${params.atomId}`,
);
}
if (receiver.kind === "all-interfaces") {
for (const requiredInterface of receiver.interfaceRevisionIds) {
const targetKey = conformanceKey(params.atomId, requiredInterface);
if (!params.indexes.conformances.has(targetKey)) {
issue(
issues,
"unsatisfied-interface",
params.path,
`Atom ${params.atomId} does not conform to ${requiredInterface}`,
);
} else {
params.requirementGraph.get(params.graphSourceKey)?.add(targetKey);
}
}
}
};
const validateConformances = (
workspace: WorkspaceRevision,
issues: CapabilityValidationIssue[],
indexes: ValidationIndexes,
) => {
const requirementGraph = new Map<string, Set<string>>();
for (const key of indexes.conformances.keys()) {
requirementGraph.set(key, new Set());
}
for (const [index, conformance] of workspace.conformances.entries()) {
const path = `conformances[${index}]`;
const key = conformanceKey(conformance.atomId, conformance.interfaceRevisionId);
if (!indexes.atoms.has(conformance.atomId)) {
issue(issues, "unresolved-reference", `${path}.atomId`, `Unknown atom ${conformance.atomId}`);
}
const interfaceEntry = indexes.interfaces.get(conformance.interfaceRevisionId);
if (!interfaceEntry) {
issue(
issues,
"unresolved-reference",
`${path}.interfaceRevisionId`,
`Unknown interface revision ${conformance.interfaceRevisionId}`,
);
continue;
}
const bindings = new Map<OperationId, Binding>();
for (const required of interfaceEntry.revision.requiredInterfaces ?? []) {
const requiredKey = conformanceKey(conformance.atomId, required);
if (!indexes.conformances.has(requiredKey))
issue(issues, "unsatisfied-interface", path, `Conformance requires ${conformance.atomId} as ${required}`);
else requirementGraph.get(key)?.add(requiredKey);
}
for (const [bindingIndex, entry] of conformance.operationBindings.entries()) {
const bindingPath = `${path}.operationBindings[${bindingIndex}]`;
if (bindings.has(entry.operationId)) {
issue(
issues,
"duplicate-operation-binding",
`${bindingPath}.operationId`,
`Operation ${entry.operationId} is bound more than once`,
);
continue;
}
bindings.set(entry.operationId, entry.binding);
const operationEntry = interfaceEntry.operations.get(entry.operationId);
if (!operationEntry) {
issue(
issues,
"unknown-operation-binding",
`${bindingPath}.operationId`,
`Operation ${entry.operationId} does not belong to interface ${conformance.interfaceRevisionId}`,
);
continue;
}
const operation = operationEntry.operation;
let binding: ConcreteBinding;
if (entry.binding.kind === "interface") {
const target = indexes.interfaces
.get(entry.binding.interfaceRevisionId)
?.operations.get(entry.binding.operationId)?.operation;
if (!target || !signaturesMatch(operation, target) || operation.scope !== target.scope) {
issue(
issues,
"invalid-forwarding-binding",
bindingPath,
"Forwarding requires matching signatures, modes and receiver scopes",
);
continue;
}
try {
binding = resolveForwardedBinding(
workspace,
conformance.atomId,
conformance.interfaceRevisionId,
entry.operationId,
).binding;
} catch (error) {
issue(issues, "invalid-forwarding-binding", bindingPath, String(error));
continue;
}
} else binding = entry.binding;
const member = operationEntry.member;
if (member.kind === "value" && member.nativeField) {
const primitive = { get: "read", set: "write", edit: "edit" }[operation.displayName];
const getter = member.operations.find((o) => o.displayName === "get");
let read: Binding | undefined;
try {
if (getter)
read = resolveForwardedBinding(
workspace,
conformance.atomId,
conformance.interfaceRevisionId,
getter.id,
).binding;
} catch {}
if (
binding.kind !== "state" ||
binding.primitive !== primitive ||
read?.kind !== "state" ||
read.primitive !== "read" ||
read.slotId !== binding.slotId
)
issue(
issues,
"invalid-state-binding",
bindingPath,
"Field operations must resolve to the same native state slot",
);
}
if (
member.kind === "value" &&
operation.displayName === "edit" &&
(binding.kind !== "state" || binding.primitive !== "edit")
)
issue(
issues,
"invalid-state-binding",
bindingPath,
"CRDT edit operations must resolve to a native state edit capability",
);
if (
member.kind !== "operation" &&
member.kind !== "module" &&
member.queryRead &&
operation.displayName === (member.kind === "value" ? "get" : "resolve")
) {
const native =
member.kind === "value"
? (binding.kind === "state" && binding.primitive === "read") ||
(binding.kind === "journal" && binding.primitive === "head") ||
(binding.kind === "subscription" && binding.primitive === "status")
: binding.kind === "edge" && binding.primitive === "resolve";
if (!native && (member.queryRead.execution === "native" || binding.kind !== "package"))
issue(
issues,
"invalid-query-contract",
bindingPath,
"This queryable contract requires a native read binding; a justification cannot weaken it",
);
else if (!native && member.queryRead.execution === "rpc-permitted" && !entry.queryReason?.trim())
issue(
issues,
"query-provider-reason-required",
bindingPath,
"Package-backed query reads require a concrete query-reason justification",
);
}
if (operation.scope === "class" && (!conformance.id || operation.mode !== "call")) {
issue(
issues,
"invalid-package-binding",
bindingPath,
"Class capabilities require an explicit conformance ID and call mode",
);
continue;
}
// The target conformance validates the concrete provider and its private
// authority. Forwarding exposes that operation, not its attachment ports.
if (entry.binding.kind === "interface") continue;
if (operation.scope === "class" && binding.kind !== "package") {
issue(
issues,
"invalid-package-binding",
bindingPath,
"Class capabilities must bind a free package function, not instance storage",
);
continue;
}
if (binding.kind === "lifecycle") {
if (
binding.primitive !== "delete" ||
operationEntry.member.kind !== "operation" ||
operation.mode !== "call" ||
operation.inputType.kind !== "builtin" ||
operation.inputType.name !== "unit" ||
operation.outputType.kind !== "builtin" ||
operation.outputType.name !== "unit"
)
issue(
issues,
"invalid-lifecycle-binding",
bindingPath,
"Deletion implements an object call from unit to unit",
);
continue;
}
if (binding.kind === "journal") {
const attachment = findAttachment(indexes, "state", binding.slotId);
if (
!attachment ||
attachment.attachment.kind !== "state" ||
attachment.attachment.storagePolicy.kind !== "journal"
) {
issue(issues, "invalid-state-binding", bindingPath, "Expected native journal storage");
continue;
}
validateAttachmentAccess(issues, attachment, conformance, bindingPath);
if (
attachment.attachment.attachedTo !== conformance.atomId ||
!["publish", "readPage", "head"].includes(binding.primitive) ||
!signaturesMatch(operation, journalPrimitiveSignature(attachment.attachment.valueType, binding.primitive))
)
issue(
issues,
"invalid-state-binding",
bindingPath,
"Journal primitive does not match this object operation's signature",
);
continue;
}
if (binding.kind === "subscription") {
const attachment = findAttachment(indexes, "state", binding.slotId);
if (
!attachment ||
attachment.attachment.kind !== "state" ||
attachment.attachment.storagePolicy.kind !== "subscription"
) {
issue(issues, "invalid-state-binding", bindingPath, "Expected native subscription storage");
continue;
}
validateAttachmentAccess(issues, attachment, conformance, bindingPath);
const slot = attachment.attachment;
if (slot.storagePolicy.kind !== "subscription") continue;
if (slot.attachedTo !== conformance.atomId)
issue(issues, "invalid-state-binding", bindingPath, "Subscription belongs to another atom");
if (binding.primitive === "attach") {
try {
subscriptionContract(
slot.storagePolicy.payload,
slot.valueType,
operation,
[...indexes.interfaces.values()].map((value) => value.revision),
);
} catch (error) {
issue(issues, "invalid-state-binding", bindingPath, String(error));
}
} else if (
!["status", "pause", "resume", "retire"].includes(binding.primitive) ||
!signaturesMatch(operation, {
mode: "call",
inputType: valueType.unit,
outputType: binding.primitive === "status" ? subscriptionStatusType : valueType.unit,
})
)
issue(issues, "invalid-state-binding", bindingPath, "Subscription primitive signature mismatch");
continue;
}
if (binding.kind === "state") {
const attachment = findAttachment(indexes, "state", binding.slotId);
if (!attachment || attachment.attachment.kind !== "state") {
issue(
issues,
"invalid-state-binding",
`${bindingPath}.binding.slotId`,
`Unknown state attachment ${binding.slotId}`,
);
continue;
}
validateAttachmentAccess(issues, attachment, conformance, `${bindingPath}.binding.slotId`);
if (["journal", "subscription"].includes(attachment.attachment.storagePolicy.kind))
issue(issues, "invalid-state-binding", bindingPath, "Use native journal primitives, not state access");
if (attachment.attachment.attachedTo !== conformance.atomId) {
issue(
issues,
"invalid-state-binding",
`${bindingPath}.binding.slotId`,
`State ${binding.slotId} is not attached to atom ${conformance.atomId}`,
);
}
if (operationEntry.member.kind !== "value") {
issue(
issues,
"invalid-state-binding",
`${bindingPath}.binding`,
"State primitives may only implement value members",
);
}
if (
binding.primitive === "edit" &&
(attachment.attachment.storagePolicy.kind !== "crdt-document" ||
!valueTypesEqual(attachment.attachment.valueType, attachment.attachment.storagePolicy.updateType))
)
issue(
issues,
"invalid-state-binding",
bindingPath,
"Edit requires a CRDT slot with a matching materialized profile type",
);
const expected = statePrimitiveSignature(attachment.attachment, binding.primitive);
if (!signaturesMatch(operation, expected)) {
issue(
issues,
"invalid-state-binding",
`${bindingPath}.binding`,
`State primitive provides ${describeSignature(expected)}, but operation requires ${describeSignature(operation)}`,
);
}
continue;
}
if (binding.kind === "edge") {
const attachment = findAttachment(indexes, "edge", binding.edgeTypeId);
if (!attachment || attachment.attachment.kind !== "edge") {
issue(
issues,
"invalid-edge-binding",
`${bindingPath}.binding.edgeTypeId`,
`Unknown edge attachment ${binding.edgeTypeId}`,
);
continue;
}
const projection = edgeProjection(attachment.attachment, binding.projectionId);
if (projection?.target.keyType && binding.primitive === "connect")
issue(
issues,
"invalid-edge-binding",
bindingPath,
"Connect through the keyed projection so the caller supplies a map key",
);
// An owned endpoint may explicitly export read-only traversal, including
// a native inverse relationship view. This never exports mutation rights.
if (!(binding.primitive === "resolve" && projection?.endpoint.publicTraversal)) {
validateAttachmentAccess(issues, attachment, conformance, `${bindingPath}.binding.edgeTypeId`);
}
const relationshipMember = operationEntry.member.kind === "relationship" ? operationEntry.member : undefined;
const operationEdge = relationshipMember
? {
...attachment.attachment,
endpoints: attachment.attachment.endpoints.map((endpoint, index) =>
index === (projection?.endpointIndex === 0 ? 1 : 0)
? { ...endpoint, constraint: relationshipMember.target }
: endpoint,
) as [EdgeEndpoint, EdgeEndpoint],
}
: attachment.attachment;
const expected = edgePrimitiveSignature(operationEdge, binding.projectionId, binding.primitive);
if (!projection || !expected) {
issue(
issues,
"invalid-edge-binding",
`${bindingPath}.binding.projectionId`,
`Projection ${binding.projectionId} is not on edge ${binding.edgeTypeId}`,
);
continue;
}
if (!atomSatisfiesConstraint(conformance.atomId, projection.endpoint.constraint, indexes.conformances)) {
issue(
issues,
"invalid-edge-binding",
`${bindingPath}.binding.projectionId`,
`Projection ${binding.projectionId} cannot originate at atom ${conformance.atomId}`,
);
}
if (operationEntry.member.kind !== "relationship") {
issue(
issues,
"invalid-edge-binding",
`${bindingPath}.binding`,
"Edge primitives may only implement relationship members",
);
} else if (
!constraintSatisfiesConstraint(projection.target.constraint, operationEntry.member.target, indexes) ||
operationEntry.member.cardinality !== projection.endpoint.cardinality ||
(operationEntry.member.keyType !== undefined && operationEntry.member.keyType !== projection.endpoint.keyType)
) {
issue(
issues,
"invalid-edge-binding",
`${bindingPath}.binding`,
`Edge projection target or cardinality does not match ${conformance.atomId}/${conformance.interfaceRevisionId}/${operationEntry.member.displayName}`,
);
}
if (!signaturesMatch(operation, expected)) {
issue(
issues,
"invalid-edge-binding",
`${bindingPath}.binding`,
`Edge primitive provides ${describeSignature(expected)}, but operation requires ${describeSignature(operation)}`,
);
}
continue;
}
const packageEntry = indexes.packages.get(binding.packageRevisionId);
const packageExport = packageEntry?.exports.get(binding.exportId);
if (!packageEntry) {
issue(
issues,
"invalid-package-binding",
`${bindingPath}.binding.packageRevisionId`,
`Unknown package revision ${binding.packageRevisionId}`,
);
continue;
}
if (!packageExport) {
issue(
issues,
"invalid-package-binding",
`${bindingPath}.binding.exportId`,
`Unknown export ${binding.exportId} in package ${binding.packageRevisionId}`,
);
continue;
}
if (operation.scope === "class" ? packageExport.kind !== "function" : packageExport.kind !== "operation") {
issue(
issues,
"invalid-package-binding",
`${bindingPath}.binding.exportId`,
`Package export ${binding.exportId} must be ${operation.scope === "class" ? "a free function" : "an instance operation"}`,
);
continue;
}
if (
!signaturesMatch(operation, {
...packageExport,
mode: packageExport.kind === "operation" ? packageExport.mode : "call",
})
) {
issue(
issues,
"invalid-package-binding",
`${bindingPath}.binding.exportId`,
`Package export signature does not match ${describeSignature(operation)}`,
);
}
if (packageExport.kind === "operation")
validatePackageReceiver(issues, {
entry: packageExport,
atomId: conformance.atomId,
path: `${bindingPath}.binding.exportId`,
indexes,
requirementGraph,
graphSourceKey: key,
});
if (
operation.scope === "class" &&
packageExport.dependencyPorts.some(({ requirement }) => {
if (["constructor", "creation", "reflection"].includes(requirement.kind)) return false;
if (requirement.kind !== "interface") return true;
if (requirement.receiver === "input") return false;
const contract = indexes.interfaces.get(requirement.interfaceRevisionId);
return (
!requirement.operations?.length ||
!requirement.operations.every((id) =>
contract?.revision.members
.flatMap((m) => m.operations)
.some((op) => op.id === id && op.scope === "class"),
)
);
})
) {
issue(
issues,
"invalid-package-binding",
bindingPath,
"Class functions require receiverless dependencies such as creation ports",
);
}
validateBoundDependencies(issues, {
dependencies: binding.dependencies,
dependencyPorts: packageExport.dependencyPorts,
atomId: conformance.atomId,
conformance,
path: `${bindingPath}.binding`,
indexes,
requirementGraph,
graphSourceKey: key,
});
const check = packageEntry.exports.get(packageExport.acceptanceExportId!);
if (check) {
validateBoundDependencies(issues, {
dependencies: binding.acceptanceDependencies ?? [],
dependencyPorts: check.dependencyPorts,
atomId: conformance.atomId,
conformance,
path: `${bindingPath}.binding.acceptance`,
indexes,
requirementGraph,
graphSourceKey: key,
});
} else if (binding.acceptanceDependencies !== undefined)
issue(issues, "invalid-package-binding", bindingPath, "Acceptance ports require a declared acceptance export");
}
for (const member of interfaceEntry.revision.members) {
const start = member.operations.find((op) => op.mode === "watch-start");
const stop = member.operations.find((op) => op.mode === "watch-stop");
if (!start || !stop || ![start, stop].some((op) => bindings.get(op.id)?.kind === "interface")) continue;
try {
const a = resolveForwardedBinding(workspace, conformance.atomId, conformance.interfaceRevisionId, start.id);
const b = resolveForwardedBinding(workspace, conformance.atomId, conformance.interfaceRevisionId, stop.id);
const target = indexes.interfaces
.get(a.conformance.interfaceRevisionId)
?.revision.members.find((m) => m.operations.some((op) => op.id === a.entry.operationId));
if (a.conformance !== b.conformance || !target?.operations.some((op) => op.id === b.entry.operationId))
issue(
issues,
"invalid-forwarding-binding",
path,
"Forwarded watch start/stop must resolve to the same target member",
);
const get = member.operations.find((op) => op.displayName === (member.kind === "value" ? "get" : "resolve"));
if (get && (a.binding.kind === "state" || a.binding.kind === "edge")) {
const read = resolveForwardedBinding(
workspace,
conformance.atomId,
conformance.interfaceRevisionId,
get.id,
).binding;
const matches =
a.binding.kind === "state"
? read.kind === "state" && read.slotId === a.binding.slotId
: read.kind === "edge" &&
read.edgeTypeId === a.binding.edgeTypeId &&
read.projectionId === a.binding.projectionId;
if (!matches)
issue(
issues,
"invalid-forwarding-binding",
path,
"Native forwarded watch must observe its getter's storage",
);
}
} catch (error) {
issue(issues, "invalid-forwarding-binding", path, String(error));
}
}
const materializedMembers = new Set<string>();
for (const [materializationIndex, materialization] of (conformance.relationshipMaterializations ?? []).entries()) {
const materializationPath = `${path}.relationshipMaterializations[${materializationIndex}]`;
if (materializedMembers.has(materialization.memberId)) {
issue(
issues,
"duplicate-relationship-materialization",
materializationPath,
`Relationship ${materialization.memberId} has more than one materialization recipe`,
);
continue;
}
materializedMembers.add(materialization.memberId);
const member = interfaceEntry.revision.members.find((entry) => entry.id === materialization.memberId);
if (!member || member.kind !== "relationship") {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.memberId`,
`Member ${materialization.memberId} is not a relationship on ${conformance.interfaceRevisionId}`,
);
continue;
}
if (member.cardinality !== "optional-one") {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.memberId`,
"A lazily constructed relationship must be optional-one before construction",
);
}
const resolveOperation = member.operations.find((entry) => entry.displayName === "resolve");
const resolveBinding = resolveOperation ? bindings.get(resolveOperation.id) : undefined;
if (!resolveBinding || resolveBinding.kind !== "edge") {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.memberId`,
"A materialized relationship must bind its resolve operation to an edge",
);
continue;
}
if (resolveBinding.edgeTypeId !== materialization.edgeTypeId) {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.edgeTypeId`,
"Materialization and relationship resolution must use the same edge",
);
}
const attachment = findAttachment(indexes, "edge", materialization.edgeTypeId);
if (!attachment || attachment.attachment.kind !== "edge") {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.edgeTypeId`,
`Unknown materialization edge ${materialization.edgeTypeId}`,
);
continue;
}
validateAttachmentAccess(issues, attachment, conformance, `${materializationPath}.edgeTypeId`);
const hostProjection = edgeProjection(attachment.attachment, resolveBinding.projectionId);
const constructedProjection = edgeProjection(attachment.attachment, materialization.constructedProjectionId);
if (
!hostProjection ||
!constructedProjection ||
hostProjection.endpointIndex === constructedProjection.endpointIndex
) {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.constructedProjectionId`,
"Materialization must connect through the opposite projection of the resolved edge",
);
continue;
}
if (!atomSatisfiesConstraint(materialization.constructorAtomId, member.target, indexes.conformances)) {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.constructorAtomId`,
`Constructed atom ${materialization.constructorAtomId} does not satisfy the relationship target`,
);
}
if (
!atomSatisfiesConstraint(
materialization.constructorAtomId,
constructedProjection.endpoint.constraint,
indexes.conformances,
) ||
!atomSatisfiesConstraint(conformance.atomId, constructedProjection.target.constraint, indexes.conformances)
) {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.constructedProjectionId`,
"Constructed projection does not connect the constructed atom back to the host atom",
);
}
const constructor = indexes.constructors.get(materialization.constructorAtomId);
const constructorExport = constructor
? indexes.packages.get(constructor.packageRevisionId)?.exports.get(constructor.exportId)
: undefined;
if (!constructor || !constructorExport || constructorExport.kind !== "constructor") {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.constructorAtomId`,
`Constructed atom ${materialization.constructorAtomId} has no valid constructor`,
);
} else if (!valueTypesEqual(constructorExport.inputType, valueType.atomRef(conformance.atomId))) {
issue(
issues,
"invalid-relationship-materialization",
`${materializationPath}.constructorAtomId`,
`Component constructor must accept ${typeLabel(valueType.atomRef(conformance.atomId))}`,
);
}
}
for (const operation of interfaceEntry.operations.values()) {
if (!bindings.has(operation.operation.id)) {
issue(
issues,
"missing-operation-binding",
`${path}.operationBindings`,
`Missing binding for operation ${operation.operation.id}`,
);
}
}
}
const visitState = new Map<string, "visiting" | "visited">();
const visit = (key: string, stack: string[]) => {
const state = visitState.get(key);
if (state === "visited") {
return;
}
if (state === "visiting") {
const cycleStart = stack.indexOf(key);
const cycle = [...stack.slice(Math.max(0, cycleStart)), key];
issue(
issues,
"cyclic-conformance-requirement",
indexes.conformancePaths.get(key) ?? "conformances",
`Cyclic conformance requirement: ${cycle.join(" -> ")}`,
);
return;
}
visitState.set(key, "visiting");
for (const dependency of requirementGraph.get(key) ?? []) {
visit(dependency, [...stack, key]);
}
visitState.set(key, "visited");
};
for (const key of requirementGraph.keys()) {
visit(key, []);
}
};
const validateConstructors = (
workspace: WorkspaceRevision,
issues: CapabilityValidationIssue[],
indexes: ValidationIndexes,
) => {
const noGraph = new Map<string, Set<string>>();
for (const [index, constructor] of workspace.constructors.entries()) {
const path = `constructors[${index}]`;
if (!indexes.atoms.has(constructor.atomId)) {
issue(issues, "invalid-constructor", `${path}.atomId`, `Unknown atom ${constructor.atomId}`);
}
const packageEntry = indexes.packages.get(constructor.packageRevisionId);
const packageExport = packageEntry?.exports.get(constructor.exportId);
if (!packageEntry) {
issue(
issues,
"invalid-constructor",
`${path}.packageRevisionId`,
`Unknown package revision ${constructor.packageRevisionId}`,
);
continue;
}
if (!packageExport) {
issue(issues, "invalid-constructor", `${path}.exportId`, `Unknown export ${constructor.exportId}`);
continue;
}
if (packageExport.kind !== "constructor") {
issue(
issues,
"invalid-constructor",
`${path}.exportId`,
`Export ${constructor.exportId} is ${packageExport.kind}, not a constructor`,
);
continue;
}
if (packageExport.constructsAtom !== constructor.atomId) {
issue(
issues,
"invalid-constructor",
`${path}.exportId`,
`Export constructs ${packageExport.constructsAtom}, not ${constructor.atomId}`,
);
}
validateBoundDependencies(issues, {
dependencies: constructor.dependencies,
dependencyPorts: packageExport.dependencyPorts,
atomId: constructor.atomId,
path,
indexes,
requirementGraph: noGraph,
});
}
};
export const validateWorkspaceRevision = (workspace: WorkspaceRevision): CapabilityValidationIssue[] => {
const issues: CapabilityValidationIssue[] = [];
requireText(issues, workspace.id, "id", "Workspace revision ID");
requireText(issues, workspace.workspaceId, "workspaceId", "Workspace ID");
requireText(issues, workspace.sourceRootCommit, "sourceRootCommit", "Source root commit");
const indexes = collectIdentityIndexes(workspace, issues);
validateInterfaces(workspace, issues, indexes);
validateAttachments(issues, indexes);
validatePackages(workspace, issues, indexes);
validateConformances(workspace, issues, indexes);
validateConstructors(workspace, issues, indexes);
issues.push(
...validateExecutionContracts(workspace),
...validateInitialization(workspace),
...validatePresentations(workspace),
...validateModules(workspace),
...validateWorkspaceControllers(workspace),
);
return issues;
};
export interface CompiledConformance {
source: Conformance;
operationBindings: ReadonlyMap<OperationId, Binding>;
}
export interface CompiledWorkspaceRevision {
source: WorkspaceRevision;
atoms: ReadonlyMap<AtomId, WorkspaceRevision["atoms"][number]>;
interfaces: ReadonlyMap<InterfaceRevisionId, InterfaceRevision>;
packages: ReadonlyMap<PackageRevisionId, PackageRevision>;
attachments: ReadonlyMap<string, OwnedAttachment>;
conformances: ReadonlyMap<string, CompiledConformance>;
constructors: ReadonlyMap<AtomId, AtomConstructorBinding>;
}
export type CompileWorkspaceRevisionResult =
| { ok: true; plan: CompiledWorkspaceRevision }
| { ok: false; issues: CapabilityValidationIssue[] };
export const compileWorkspaceRevision = (workspace: WorkspaceRevision): CompileWorkspaceRevisionResult => {
const snapshot = structuredClone(workspace) as WorkspaceRevision;
const issues = validateWorkspaceRevision(snapshot);
if (issues.length > 0) {
return { ok: false, issues };
}
const attachments = new Map<string, OwnedAttachment>();
for (const attachment of snapshot.sharedAttachments) {
attachments.set(attachmentKey(attachment), {
attachment,
owner: { kind: "workspace" },
});
}
const conformances = new Map<string, CompiledConformance>();
for (const conformance of snapshot.conformances) {
for (const attachment of conformance.privateAttachments) {
attachments.set(attachmentKey(attachment), {
attachment,
owner: {
kind: "conformance",
atomId: conformance.atomId,
interfaceRevisionId: conformance.interfaceRevisionId,
},
});
}
conformances.set(conformanceKey(conformance.atomId, conformance.interfaceRevisionId), {
source: conformance,
operationBindings: new Map(conformance.operationBindings.map((entry) => [entry.operationId, entry.binding])),
});
}
return {
ok: true,
plan: {
source: snapshot,
atoms: new Map(snapshot.atoms.map((atom) => [atom.id, atom])),
interfaces: new Map(snapshot.interfaceImports.map((revision) => [revision.revisionId, revision])),
packages: new Map(snapshot.packageImports.map((revision) => [revision.revisionId, revision])),
attachments,
conformances,
constructors: new Map(snapshot.constructors.map((entry) => [entry.atomId, entry])),
},
};
};
export const resolveConformance = (
plan: CompiledWorkspaceRevision,
atomId: AtomId,
interfaceRevisionId: InterfaceRevisionId,
): CompiledConformance | undefined => plan.conformances.get(conformanceKey(atomId, interfaceRevisionId));
export const resolveOperationBinding = (
plan: CompiledWorkspaceRevision,
atomId: AtomId,
interfaceRevisionId: InterfaceRevisionId,
operationId: OperationId,
): ConcreteBinding | undefined => {
if (!resolveConformance(plan, atomId, interfaceRevisionId)?.operationBindings.has(operationId)) return undefined;
return resolveForwardedBinding(plan.source, atomId, interfaceRevisionId, operationId).binding;
};
export type ResolvedOperationPlan =
| {
kind: "state";
binding: Extract<Binding, { kind: "state" }>;
attachment: OwnedAttachment & { attachment: StateSlotDefinition };
}
| {
kind: "edge";
binding: Extract<Binding, { kind: "edge" }>;
attachment: OwnedAttachment & { attachment: EdgeDefinition };
}
| {
kind: "package";
binding: Extract<Binding, { kind: "package" }>;
packageRevision: PackageRevision;
packageExport: PackageOperationExport | PackageFunctionExport;
dependencies: Array<{
port: DependencyPort;
binding: DependencyBinding;
}>;
};
export const resolveOperationPlan = (
plan: CompiledWorkspaceRevision,
atomId: AtomId,
interfaceRevisionId: InterfaceRevisionId,
operationId: OperationId,
): ResolvedOperationPlan | undefined => {
const binding = resolveOperationBinding(plan, atomId, interfaceRevisionId, operationId);
if (!binding) {
return undefined;
}
if (binding.kind === "state") {
const owned = plan.attachments.get(`state\u0000${binding.slotId}`);
if (!owned || owned.attachment.kind !== "state") {
return undefined;
}
return {
kind: "state",
binding,
attachment: owned as OwnedAttachment & {
attachment: StateSlotDefinition;
},
};
}
if (binding.kind === "edge") {
const owned = plan.attachments.get(`edge\u0000${binding.edgeTypeId}`);
if (!owned || owned.attachment.kind !== "edge") {
return undefined;
}
return {
kind: "edge",
binding,
attachment: owned as OwnedAttachment & { attachment: EdgeDefinition },
};
}
if (binding.kind === "lifecycle" || binding.kind === "journal" || binding.kind === "subscription") return undefined; // These native providers require the current kernel.
const packageRevision = plan.packages.get(binding.packageRevisionId);
const packageExport = packageRevision?.exports.find(
(entry): entry is PackageOperationExport | PackageFunctionExport =>
entry.id === binding.exportId && (entry.kind === "operation" || entry.kind === "function"),
);
if (!packageRevision || !packageExport) {
return undefined;
}
const bound = new Map(binding.dependencies.map((entry) => [entry.portId, entry.binding]));
return {
kind: "package",
binding,
packageRevision,
packageExport,
dependencies: packageExport.dependencyPorts.map((port) => ({
port,
binding: bound.get(port.id)!,
})),
};
};
export interface CapabilityClosure {
conformances: Array<{
atomId: AtomId;
interfaceRevisionId: InterfaceRevisionId;
}>;
packageRevisionIds: PackageRevisionId[];
attachmentIds: Array<SlotId | EdgeTypeId>;
constructorAtomIds: AtomId[];
}
export const computeCapabilityClosure = (
plan: CompiledWorkspaceRevision,
roots: Array<{ atomId: AtomId; interfaceRevisionId: InterfaceRevisionId }>,
): CapabilityClosure => {
const conformances = new Map<
string,
{
atomId: AtomId;
interfaceRevisionId: InterfaceRevisionId;
}
>();
const packages = new Set<PackageRevisionId>();
const attachments = new Set<SlotId | EdgeTypeId>();
const constructors = new Set<AtomId>();
const queued = [...roots];
const visited = new Set<string>();
const sourceConformances = new Map([...plan.conformances].map(([key, value]) => [key, value.source]));
const includeDependencies = (atomId: AtomId, dependencies: readonly BoundDependency[]) => {
for (const dependency of dependencies) {
switch (dependency.binding.kind) {
case "query": {
packages.add(dependency.binding.packageRevisionId);
const query = plan.packages
.get(dependency.binding.packageRevisionId)
?.checkedQueries?.find(
(query) => dependency.binding.kind === "query" && query.declaration.id === dependency.binding.queryId,
);
if (dependency.binding.via) attachments.add(dependency.binding.via.edgeTypeId);
for (const effect of query?.effects ?? []) {
for (const conformance of sourceConformances.values())
if (conformance.interfaceRevisionId === effect.interfaceRevisionId)
queued.push({ atomId: conformance.atomId, interfaceRevisionId: conformance.interfaceRevisionId });
}
break;
}
case "state":
attachments.add(dependency.binding.slotId);
if (dependency.binding.via) {
attachments.add(dependency.binding.via.edgeTypeId);
}
break;
case "edge":
attachments.add(dependency.binding.edgeTypeId);
if (dependency.binding.via) {
attachments.add(dependency.binding.via.edgeTypeId);
}
break;
case "interface": {
if (dependency.binding.via) {
attachments.add(dependency.binding.via.edgeTypeId);
}
const targetAtoms = dependency.binding.via
? (() => {
const attachment = plan.attachments.get(`edge\u0000${dependency.binding.via!.edgeTypeId}`)?.attachment;
if (!attachment || attachment.kind !== "edge") return [];
const projection = edgeProjection(attachment, dependency.binding.via!.projectionId);
return projection
? [...plan.atoms.keys()].filter((candidate) =>
atomSatisfiesConstraint(candidate, projection.target.constraint, sourceConformances),
)
: [];
})()
: [atomId];
for (const targetAtom of targetAtoms) {
queued.push({
atomId: targetAtom,
interfaceRevisionId: dependency.binding.interfaceRevisionId,
});
}
break;
}
case "creation": {
for (const owner of dependency.binding.owners)
queued.push({ atomId: dependency.binding.atomId, interfaceRevisionId: owner.interfaceRevisionId });
break;
}
case "constructor": {
constructors.add(dependency.binding.atomId);
const constructor = plan.constructors.get(dependency.binding.atomId);
if (constructor) {
packages.add(constructor.packageRevisionId);
includeDependencies(dependency.binding.atomId, constructor.dependencies);
}
}
}
}
};
while (queued.length > 0) {
const root = queued.shift()!;
const key = conformanceKey(root.atomId, root.interfaceRevisionId);
if (visited.has(key)) {
continue;
}
visited.add(key);
const conformance = plan.conformances.get(key);
if (!conformance) {
continue;
}
conformances.set(key, {
atomId: conformance.source.atomId,
interfaceRevisionId: conformance.source.interfaceRevisionId,
});
for (const interfaceRevisionId of plan.interfaces.get(root.interfaceRevisionId)?.requiredInterfaces ?? []) {
queued.push({ atomId: root.atomId, interfaceRevisionId });
}
for (const module of conformance.source.moduleBindings ?? []) {
packages.add(module.packageRevisionId);
const member = plan.interfaces
.get(root.interfaceRevisionId)
?.members.find((member) => member.id === module.memberId);
if (member?.kind === "module" && member.subject?.kind === "interface")
queued.push({ atomId: root.atomId, interfaceRevisionId: member.subject.interfaceRevisionId });
}
for (const binding of conformance.operationBindings.values()) {
if (binding.kind === "state" || binding.kind === "journal" || binding.kind === "subscription") {
attachments.add(binding.slotId);
} else if (binding.kind === "edge") {
attachments.add(binding.edgeTypeId);
} else if (binding.kind === "interface") {
queued.push({ atomId: root.atomId, interfaceRevisionId: binding.interfaceRevisionId });
} else if (binding.kind === "package") {
packages.add(binding.packageRevisionId);
includeDependencies(root.atomId, binding.dependencies);
const packageRevision = plan.packages.get(binding.packageRevisionId);
const operation = packageRevision?.exports.find(
(entry): entry is PackageOperationExport => entry.id === binding.exportId && entry.kind === "operation",
);
if (operation?.receiverRequirement.kind === "all-interfaces") {
for (const interfaceRevisionId of operation.receiverRequirement.interfaceRevisionIds) {
queued.push({ atomId: root.atomId, interfaceRevisionId });
}
}
}
}
for (const materialization of conformance.source.relationshipMaterializations ?? []) {
attachments.add(materialization.edgeTypeId);
constructors.add(materialization.constructorAtomId);
const constructor = plan.constructors.get(materialization.constructorAtomId);
if (constructor) {
packages.add(constructor.packageRevisionId);
includeDependencies(materialization.constructorAtomId, constructor.dependencies);
}
}
}
return {
conformances: [...conformances.entries()]
.sort(([left], [right]) => left.localeCompare(right))
.map(([, conformance]) => conformance),
packageRevisionIds: [...packages].sort(),
attachmentIds: [...attachments].sort(),
constructorAtomIds: [...constructors].sort(),
};
};