Support explicitly reference-selected read-only interface dependencies

This commit is contained in:
Timothy J. Aveni
2026-09-20 22:15:15 -07:00
parent 266d2d98d0
commit c0669d2ed8
12 changed files with 1081 additions and 893 deletions
+17
View File
@@ -66,6 +66,23 @@ export function validateExecutionContracts(workspace: WorkspaceRevision): Capabi
: requirement.application.definitionId,
);
const selected = requirement.operations;
if (requirement.receiver === "input") {
if (
entry.execution?.kind !== "snapshot" ||
entry.execution.targets.length !== 1 ||
entry.execution.targets[0] !== "server" ||
!selected?.length
)
fail(
portPath,
"Reference-selected reads currently require a server-only snapshot and an explicit selection; local acquisition of dynamic inputs is not implemented",
);
for (const id of selected ?? []) {
const operation = contract && operations(id, contract);
if (operation?.execution?.kind !== "snapshot" || operation.scope === "class")
fail(portPath, "Reference-selected dependencies currently support object snapshot operations only");
}
}
if (selected && (!selected.length || new Set(selected).size !== selected.length))
fail(portPath, "Selected interface operations must be nonempty and distinct");
for (const id of selected ?? [])
+2
View File
@@ -38,6 +38,7 @@ export type GenericDependencyPort = Omit<DependencyPort, "requirement"> & {
kind: "interface";
application: InterfaceApplicationExpression;
operations?: import("./types.js").OperationId[];
receiver?: "input";
}
| { kind: "creation"; target: ObjectTypeExpression; inputType: ValueTypeExpression }
| { kind: "constructor"; target: ObjectTypeExpression; inputType: ValueTypeExpression };
@@ -100,6 +101,7 @@ export const specializePackageExport = (
kind: "interface",
interfaceRevisionId: substitution.application(requirement.application),
...(requirement.operations ? { operations: requirement.operations } : {}),
...(requirement.receiver ? { receiver: requirement.receiver } : {}),
},
};
case "creation":
+2
View File
@@ -246,6 +246,7 @@ export type DependencyPortRequirement =
interfaceRevisionId: InterfaceRevisionId;
/** Exact public operation identities, not implementation export names. */
operations?: OperationId[];
receiver?: "input";
}
| { kind: "creation"; atomId: AtomId; inputType: ValueType }
| { kind: "constructor"; atomId: AtomId; inputType?: ValueType };
@@ -323,6 +324,7 @@ export type DependencyBinding =
kind: "interface";
interfaceRevisionId: InterfaceRevisionId;
via?: EdgeTraversal;
receiver?: "input";
}
| {
kind: "creation";
+12
View File
@@ -1418,6 +1418,15 @@ const validateBoundDependencies = (
}
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,
@@ -1427,6 +1436,9 @@ const validateBoundDependencies = (
);
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,