Host checked adapter-object controllers through bounded registries (WI-13)

This commit is contained in:
Timothy J. Aveni
2026-09-21 18:00:27 -07:00
parent 1e08ccd937
commit d824634859
14 changed files with 235 additions and 7 deletions
+46 -4
View File
@@ -6,6 +6,7 @@ export function validateControllers(
controllers: readonly PackageController[],
interfaces: readonly InterfaceRevision[],
prefix: string,
queries: readonly import("../query/types.js").CheckedQuery[] = [],
): CapabilityValidationIssue[] {
const issues: CapabilityValidationIssue[] = [],
ids = new Set<string>();
@@ -15,6 +16,37 @@ export function validateControllers(
if (!controller.id.trim() || ids.has(controller.id))
fail(`${path}.id`, "Controller identities must be nonempty and unique within the package");
ids.add(controller.id);
if (controller.registry !== undefined) {
const query = queries.find((query) => query.declaration.id === controller.registry);
const selected = (entries: import("../query/types.js").QuerySelection[] | undefined, name: string) =>
entries?.find((entry) => entry.name === name && entry.key === name && !entry.conditions.length);
const root = selected(query?.selection, "root"),
objects = selected(root?.selection, "objects"),
entries = selected(objects?.selection, "entries"),
node = selected(entries?.selection, "node"),
qx = selected(node?.selection, "_qx"),
ref = selected(qx?.selection, "ref");
const all = objects?.arguments.all;
if (
!query ||
query.declaration.root.kind !== "workspace-interface" ||
query.declaration.root.interface !== controller.subject ||
query.declaration.placement?.kind === "local" ||
query.effects.some((effect) => effect.execution === "rpc-permitted") ||
query.variables.kind !== "record" ||
Object.keys(query.variables.fields).length ||
!ref ||
all?.kind !== "literal" ||
!/^\d+$/.test(String(all.value)) ||
!Number.isSafeInteger(Number(all.value)) ||
Number(all.value) < 1 ||
Number(all.value) > 64
)
fail(
`${path}.registry`,
"Controller registry requires a native/portable workspace query over its subject, no variables, and root.objects(all: 1..64).entries.node._qx.ref without aliases or conditional selections",
);
}
const iface = interfaces.find((i) => i.revisionId === controller.subject && !i.template);
if (!iface) {
fail(`${path}.subject`, "Controller subject must be an imported closed interface");
@@ -54,11 +86,20 @@ export function validateControllers(
}
export function validateWorkspaceControllers(workspace: WorkspaceRevision): CapabilityValidationIssue[] {
return workspace.packageImports.flatMap((pkg, index) =>
validateControllers(pkg.controllers ?? [], workspace.interfaceImports, `packageImports[${index}].controllers`),
validateControllers(
pkg.controllers ?? [],
workspace.interfaceImports,
`packageImports[${index}].controllers`,
pkg.checkedQueries,
),
);
}
/** The file is declarative metadata, not an executable registration callback. */
export function parseControllerCatalog(text: string, interfaces: readonly InterfaceRevision[]): PackageController[] {
export function parseControllerCatalog(
text: string,
interfaces: readonly InterfaceRevision[],
queries: readonly import("../query/types.js").CheckedQuery[] = [],
): PackageController[] {
if (Buffer.byteLength(text) > 1048576) throw new Error("Controller catalog exceeds 1 MiB");
const value: unknown = JSON.parse(text);
const record = (value: unknown): value is Record<string, unknown> =>
@@ -75,12 +116,13 @@ export function parseControllerCatalog(text: string, interfaces: readonly Interf
for (const entry of value.controllers)
if (
!record(entry) ||
Object.keys(entry).length !== fields.length ||
Object.keys(entry).some((key) => ![...fields, "registry"].includes(key)) ||
(entry.registry !== undefined && typeof entry.registry !== "string") ||
fields.some((key) => typeof entry[key] !== "string")
)
throw new Error("Invalid controller entry: expected exactly id, subject, snapshot, remote, input strings");
const controllers = value.controllers as PackageController[];
const issues = validateControllers(controllers, interfaces, "controllers");
const issues = validateControllers(controllers, interfaces, "controllers", queries);
if (issues.length) throw new Error(issues.map((i) => `${i.path}: ${i.message}`).join("\n"));
return controllers;
}
+2
View File
@@ -326,6 +326,8 @@ export interface PackageRevision {
/** Checked adapter registration. Implementations remain ordinary object capabilities. */
export interface PackageController {
/** Optional opt-in discovery: a bounded checked workspace query in this package. */
registry?: string;
id: string;
subject: InterfaceRevisionId;
snapshot: OperationId;