Expose authoritative lifecycle deletion through capability bindings
This commit is contained in:
@@ -195,6 +195,7 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[
|
||||
}
|
||||
};
|
||||
const binding = (parent: GraphNode, value: Binding, atomId: string, context: unknown) => {
|
||||
if (value.kind === "lifecycle") return;
|
||||
if (value.kind !== "package") {
|
||||
dependency(parent, value, atomId);
|
||||
return;
|
||||
|
||||
@@ -125,7 +125,7 @@ export function validateExecutionContracts(workspace: WorkspaceRevision): Capabi
|
||||
if (!revision) continue;
|
||||
for (const entry of conformance.operationBindings) {
|
||||
const operation = operations(entry.operationId, revision);
|
||||
if (!operation?.execution || operation.execution.kind === "remote") continue;
|
||||
if (!operation) continue;
|
||||
const path = `${conformance.atomId}/${revision.revisionId}/${entry.operationId}`;
|
||||
let binding;
|
||||
try {
|
||||
@@ -142,6 +142,19 @@ export function validateExecutionContracts(workspace: WorkspaceRevision): Capabi
|
||||
const target = interfaces.get(entry.binding.interfaceRevisionId);
|
||||
if (target) supports(operation.execution, operations(entry.binding.operationId, target)?.execution, path);
|
||||
}
|
||||
if (binding.kind === "lifecycle") {
|
||||
if (
|
||||
operation.execution?.kind !== "transaction" ||
|
||||
operation.execution.targets.length !== 1 ||
|
||||
operation.execution.targets[0] !== "server"
|
||||
)
|
||||
fail(
|
||||
path,
|
||||
"Deletion requires an explicitly server-only transaction; partial replicas cannot enumerate incoming references",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (!operation.execution || operation.execution.kind === "remote") continue;
|
||||
if (binding.kind === "package") {
|
||||
const provider = packages.get(binding.packageRevisionId)?.exports.find((e) => e.id === binding.exportId);
|
||||
supports(operation.execution, provider?.execution, path);
|
||||
|
||||
@@ -358,6 +358,7 @@ export interface RelationshipMaterialization {
|
||||
}
|
||||
|
||||
export type Binding =
|
||||
| { kind: "lifecycle"; primitive: "delete" }
|
||||
| {
|
||||
kind: "interface";
|
||||
interfaceRevisionId: InterfaceRevisionId;
|
||||
|
||||
@@ -70,6 +70,7 @@ export type CapabilityValidationIssueCode =
|
||||
| "invalid-attachment"
|
||||
| "invalid-state-binding"
|
||||
| "invalid-edge-binding"
|
||||
| "invalid-lifecycle-binding"
|
||||
| "invalid-package-binding"
|
||||
| "invalid-forwarding-binding"
|
||||
| "invalid-dependency-binding"
|
||||
@@ -1696,6 +1697,24 @@ const validateConformances = (
|
||||
);
|
||||
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 === "state") {
|
||||
const attachment = findAttachment(indexes, "state", binding.slotId);
|
||||
if (!attachment || attachment.attachment.kind !== "state") {
|
||||
@@ -2324,6 +2343,7 @@ export const resolveOperationPlan = (
|
||||
attachment: owned as OwnedAttachment & { attachment: EdgeDefinition },
|
||||
};
|
||||
}
|
||||
if (binding.kind === "lifecycle") return undefined; // Old runtime has no lifecycle provider.
|
||||
const packageRevision = plan.packages.get(binding.packageRevisionId);
|
||||
const packageExport = packageRevision?.exports.find(
|
||||
(entry): entry is PackageOperationExport | PackageFunctionExport =>
|
||||
@@ -2469,7 +2489,7 @@ export const computeCapabilityClosure = (
|
||||
attachments.add(binding.edgeTypeId);
|
||||
} else if (binding.kind === "interface") {
|
||||
queued.push({ atomId: root.atomId, interfaceRevisionId: binding.interfaceRevisionId });
|
||||
} else {
|
||||
} else if (binding.kind === "package") {
|
||||
packages.add(binding.packageRevisionId);
|
||||
includeDependencies(root.atomId, binding.dependencies);
|
||||
const packageRevision = plan.packages.get(binding.packageRevisionId);
|
||||
|
||||
Reference in New Issue
Block a user