From ef9a4e067a992127a4d1fa16a0923ac63641a68d Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Thu, 1 Oct 2026 11:28:57 -0700 Subject: [PATCH] Expose workspace data to the agent and curate its authoring guides Check named public GraphQL documents against the serving installation using the existing compiler, linker and authorized snapshot interpreter. Keep listObjects as a convenience over workspace-root queries, with no separate filter language. Bound compilation/results, cache by content and serving plan, and fence cutovers. Share graph discovery and reads between qx-workspace graph and the gardener's workspace_graph tool. Local authoring shells discover the live endpoint through the private control socket; configured public mounts also support direct clients. Fix pending paginated inspections to wait for their confirmed window, and retain semantic integration assertions instead of locking a server ticket's shape. Replace the long prompt with a 384-word entry point and five focused guides. Build immutable bundles with profiles, deep links, source hashes and word/link checks. Keep the docs-maintenance process and feedback outside agent guidance. Running agents use current guides while authored source and checker pins remain. Validation: nix fmt; nix fmt -- --check; nix run .#lint; existing CLI checks; protocol/client/coordinator build tests; complete Studio integration; 13 agent unit tests. A real GPT-6.1 Sol medium/priority turn summarized two synthetic saved comments and a document draft in 27.7s without source changes or cutover. Live reads also succeeded after an ordinary check/activation in the retained fixture. --- src/execution-world/index.ts | 1 + src/execution-world/queries.ts | 84 ++++++++++++++------------- src/execution-world/query-document.ts | 77 ++++++++++++++++++++++++ src/query/compile.ts | 3 +- src/query/link.ts | 18 +++++- src/query/types.ts | 3 +- 6 files changed, 142 insertions(+), 44 deletions(-) create mode 100644 src/execution-world/query-document.ts diff --git a/src/execution-world/index.ts b/src/execution-world/index.ts index e393d65..1f1af96 100644 --- a/src/execution-world/index.ts +++ b/src/execution-world/index.ts @@ -14,6 +14,7 @@ export { generatePortableTypeScriptBindings, generateRemoteTypeScriptBindings } export * from "./kernel.js"; export * from "./projections.js"; export * from "./queries.js"; +export * from "./query-document.js"; export { generatePortableClientBindings } from "../bindings/portable-client.js"; export * from "./candidate.js"; diff --git a/src/execution-world/queries.ts b/src/execution-world/queries.ts index 8ddf438..26cb3c3 100644 --- a/src/execution-world/queries.ts +++ b/src/execution-world/queries.ts @@ -25,7 +25,6 @@ const lowerTypes = (value: unknown): unknown => export function compileQueryProgram(plan: ExecutionPlan): unknown { if (hexDigest(plan.definition) !== plan.digest) throw new ExecutionPlanError("INVALID_PLAN", "queries", "Plan digest mismatch"); - const interfaces = plan.definition.workspace.interfaceImports; const allQueries = new Map(plan.definition.queries.map(({ id, query }) => [id, query.checked])); const compositions = plan.definition.workspace.packageImports.flatMap((pkg) => (pkg.queryCompositions ?? []).map((composition) => { @@ -62,45 +61,50 @@ export function compileQueryProgram(plan: ExecutionPlan): unknown { ...(compositions.length ? { compositions } : {}), queries: [ ...compileInspectionTemplates(plan).queries, - ...plan.definition.queries.map(({ id, query, admittedOperations }) => { - const checked = query.checked; - return { - id, - ...(checked.declaration.fragment && !checked.declaration.fragment.admission - ? { - fragment: { - package: query.packageRevisionId, - id: checked.declaration.id, - digest: checked.definitionDigest, - }, - } - : {}), - placement: checked.declaration.placement?.kind ?? "remote", - source: - checked.declaration.placement?.kind === "local" - ? JSON.stringify([query.packageRevisionId, checked.declaration.placement.sourceQueryId]) - : null, - root: checked.declaration.root, - atoms: query.rootAtoms, - variables: portableValueSchema(checked.variables), - defaults: checked.variableDefaults, - selection: lowerTypes(checked.selection), - budgets: checked.declaration.budgets, - fields: query.fields.map((field) => { - const member = interfaces - .find((i) => i.revisionId === field.interfaceRevisionId)! - .members.find((m) => m.id === field.memberId)!; - return { - atom: field.atomId, - interface: field.interfaceRevisionId, - member: field.memberId, - name: member.displayName, - operation: JSON.stringify([field.atomId, field.interfaceRevisionId, field.getter]), - }; - }), - admittedOperations, - }; - }), + ...plan.definition.queries.map((entry) => queryProgramEntry(plan, entry)), ], }; } + +export function queryProgramEntry(plan: ExecutionPlan, entry: import("./plan.js").PlannedQuery) { + const { id, query, admittedOperations } = entry; + const interfaces = plan.definition.workspace.interfaceImports; + + const checked = query.checked; + return { + id, + ...(checked.declaration.fragment && !checked.declaration.fragment.admission + ? { + fragment: { + package: query.packageRevisionId, + id: checked.declaration.id, + digest: checked.definitionDigest, + }, + } + : {}), + placement: checked.declaration.placement?.kind ?? "remote", + source: + checked.declaration.placement?.kind === "local" + ? JSON.stringify([query.packageRevisionId, checked.declaration.placement.sourceQueryId]) + : null, + root: checked.declaration.root, + atoms: query.rootAtoms, + variables: portableValueSchema(checked.variables), + defaults: checked.variableDefaults, + selection: lowerTypes(checked.selection), + budgets: checked.declaration.budgets, + fields: query.fields.map((field) => { + const member = interfaces + .find((i) => i.revisionId === field.interfaceRevisionId)! + .members.find((m) => m.id === field.memberId)!; + return { + atom: field.atomId, + interface: field.interfaceRevisionId, + member: field.memberId, + name: member.displayName, + operation: JSON.stringify([field.atomId, field.interfaceRevisionId, field.getter]), + }; + }), + admittedOperations, + }; +} diff --git a/src/execution-world/query-document.ts b/src/execution-world/query-document.ts new file mode 100644 index 0000000..08509d1 --- /dev/null +++ b/src/execution-world/query-document.ts @@ -0,0 +1,77 @@ +import { compileQuery } from "../query/compile.js"; +import { linkQueries } from "../query/link.js"; +import { capabilityId } from "../capability-model/types.js"; +import { portableValueSchema } from "../bindings/portable.js"; +import { hexDigest, type ExecutionPlan, type PlannedQuery } from "./plan.js"; +import { compileKernelProgram } from "./kernel.js"; +import { queryProgramEntry } from "./queries.js"; +import { unsupportedQueryOperator } from "./query-operators.js"; + +export interface QueryDocument { + readonly interface: string; + readonly document: string; + readonly operation: string; + readonly objectRoot: boolean; +} +/** Runtime query documents use the same language, nominal contracts, linking + * and interpreter as packaged queries. They introduce no resources or code. */ +export async function compilePublicQuery(plan: ExecutionPlan, request: QueryDocument) { + if (hexDigest(plan.definition) !== plan.digest) throw Error("INVALID_PLAN"); + if ( + typeof request.document !== "string" || + Buffer.byteLength(request.document) > 32768 || + typeof request.operation !== "string" || + !/^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(request.operation) || + typeof request.interface !== "string" || + request.interface.length > 2048 + ) + throw Error("QUERY_DOCUMENT_LIMIT"); + const id = "public-query:" + hexDigest(request); + const checked = await compileQuery( + { + id, + displayName: request.operation, + root: { + kind: request.objectRoot ? "object" : "workspace-interface", + interface: capabilityId.interfaceRevision(request.interface), + }, + document: "request.graphql", + operation: request.operation, + fragments: [], + views: [], + allowances: [], + watch: false, + budgets: { + rows: 512, + depth: 8, + resultBytes: 256 * 1024, + candidates: 10000, + rpcCalls: 128, + concurrency: 8, + deadlineMs: 10000, + }, + }, + plan.definition.workspace.interfaceImports, + async () => request.document, + ); + const unsupported = unsupportedQueryOperator(checked); + if (unsupported) throw Error(`QUERY_OPERATOR_UNSUPPORTED: ${unsupported}`); + const linked = linkQueries(plan.definition.workspace, [ + { packageRevisionId: "org.quixos.public-query/1", checked }, + ]).find((q) => q.checked === checked)!; + const kernel = compileKernelProgram(plan) as { + operations: { key: string; effect: string; unsupported: string | null; binding: { kind: string } }[]; + }; + for (const field of linked.fields) { + const key = JSON.stringify([field.atomId, field.interfaceRevisionId, field.getter]); + const operation = kernel.operations.find((op) => op.key === key); + const member = plan.definition.workspace.interfaceImports + .find((i) => i.revisionId === field.interfaceRevisionId)! + .members.find((m) => m.id === field.memberId)!; + if (!operation || operation.unsupported || operation.effect !== "snapshot") + throw Error("QUERY_DOCUMENT_REQUIRES_SNAPSHOT: use an installed query for remote-effect enrichment"); + if (member.kind === "relationship" && operation.binding.kind !== "edge") throw Error("QUERY_NON_NATIVE_RELATION"); + } + const entry: PlannedQuery = { id, query: linked, operations: [], admittedOperations: [] }; + return { id, program: queryProgramEntry(plan, entry), output: portableValueSchema(checked.output) }; +} diff --git a/src/query/compile.ts b/src/query/compile.ts index d9e0d49..6711e72 100644 --- a/src/query/compile.ts +++ b/src/query/compile.ts @@ -66,7 +66,8 @@ const fail = (code: string, message: string, node?: ASTNode): never => { ); }; -/** Only immutable repository sources call this. Runtime requests never compile documents. */ +/** Shared checker for authored queries and bounded public query documents. + * Runtime execution receives its checked IR, never an unchecked document. */ export async function compileQuery( declaration: QueryDeclaration, interfaces: readonly InterfaceRevision[], diff --git a/src/query/link.ts b/src/query/link.ts index 9731000..f6684ca 100644 --- a/src/query/link.ts +++ b/src/query/link.ts @@ -32,14 +32,28 @@ export interface LinkedQuery { } /** Link every possible implementation, including atoms absent from today's data. */ -export function linkQueries(workspace: WorkspaceRevision): LinkedQuery[] { +export function linkQueries( + workspace: WorkspaceRevision, + additional: readonly { + packageRevisionId: string; + checked: CheckedQuery; + }[] = [], +): LinkedQuery[] { const interfaces = new Map(workspace.interfaceImports.map((entry) => [entry.revisionId, entry])); const attachments = [ ...workspace.sharedAttachments, ...workspace.conformances.flatMap((entry) => entry.privateAttachments), ]; const linked: LinkedQuery[] = []; - for (const pkg of workspace.packageImports) + for (const pkg of [ + ...workspace.packageImports, + ...additional.map(({ packageRevisionId, checked }) => ({ + revisionId: packageRevisionId, + displayName: "Public query", + queries: [checked.declaration], + checkedQueries: [checked], + })), + ]) for (const declaration of pkg.queries ?? []) { const checked = pkg.checkedQueries?.find((query) => query.declaration.id === declaration.id); if (!checked || JSON.stringify(checked.declaration) !== JSON.stringify(declaration)) diff --git a/src/query/types.ts b/src/query/types.ts index 2afa27e..cfa096c 100644 --- a/src/query/types.ts +++ b/src/query/types.ts @@ -97,7 +97,8 @@ export interface CheckedQuery { fragmentContracts?: Record>; declaration: QueryDeclaration; definitionDigest: string; - /** Fixed, validated document: never supplied by runtime callers. */ + /** Fixed, validated document. Execution always uses checked IR; public runtime + * documents pass through this same checker before they are executed. */ document: string; schema: string; variables: ValueType;