Compare commits

...
Author SHA1 Message Date
Quixos Subtree Publisher b50a7f080c Publish quixos-protocol from 8014b4ba717313e1b494c59a8e2781df58bf9a85 2026-10-11 05:28:35 +00:00
Timothy J. Aveni 2139f138ca Write Agent bootstrap plans to files instead of bounded command output 2026-10-10 22:28:35 -07:00
Timothy J. Aveni 8fe374fe25 Run canonical application source in independent development Graphs
Initialize camino-dev from an exact canonical source pin with native Git/jj history, compiler-bound Workspace identity, and retained instructions/toolchain. Share the strict pin decoder with source acquisition; retain monorepo fixture behavior separately.

Qualified the maintained real Git/jj/compiler regression, all 57 Reader repositories, packaged launcher startup/session, retained-Graph restart and Chromium Text/settings opening without page errors. Lint and documentation checks pass. Hosted source lifecycle qualification and maintained-copy removal remain pending.
2026-10-10 19:37:35 -07:00
Timothy J. Aveni ad2a851b94 Consolidate Reader source ownership and pinned application instances
Add exact recursive source checkout, independent Git/jj developer workbenches and source-only backup/recovery commands, with Workspace instance binding outside source. Qualify two empty Reader Graphs, packaged source commands, Agent VM compilation, exact Gitea read grants and reconciled Reader settings/annotation stability. Publish checked Reader Package/root revisions to canonical review branches and retention tags, retain all 57 repositories independently in private versioned storage, verify downloaded-backup recovery and advance the example pin.

Add bounded Central pin acquisition and selection-specific read-only identities, qualified against all 57 canonical repositories without write authority. Replace regex fork dependency discovery with compiler-parsed lock imports and rewrite proposals; preserve source identities and unrelated files, passing instance binding through packed and ordinary host checks.

Hosted run-pin versus authoring preparation and maintained duplicate removal remain pending. Live source selections, Template defaults and default source branches are unchanged.
2026-10-10 18:38:57 -07:00
Quixos Subtree Publisher 721b730263 Publish quixos-protocol from a61bdda658be4878403b8d60aecb71fe5a614ac0 2026-10-10 10:49:05 +00:00
Timothy J. Aveni c2c01a9878 Implement progressive query admission, session boundaries and event-driven Studio observations 2026-10-10 03:49:05 -07:00
Quixos Subtree Publisher 9fae801b7f Publish quixos-protocol from 286ada5419c4fc2184b387cea4715fae1a74bde5 2026-10-10 00:10:14 +00:00
Timothy J. Aveni 2ae7f00acb Reuse checked bootstrap catalogs and coordinate execution deadlines 2026-10-09 17:10:14 -07:00
Timothy J. Aveni 041a9e26ad Document package architecture and organize current guidance and project backlog 2026-10-09 10:33:39 -07:00
Quixos Subtree Publisher 7f3fdfe64e Publish quixos-protocol from d1ab9c912133511cfd3de65550965e0e8806678f 2026-10-08 19:32:06 +00:00
Timothy J. Aveni 0a7bdf7657 Qualify restricted SDK syntax independently of build architecture 2026-10-08 12:32:06 -07:00
Quixos Subtree Publisher 137dcaa101 Publish quixos-protocol from 2ef07e2524ed167421be56b81a5f498a2759ca5d 2026-10-08 19:30:33 +00:00
Timothy J. Aveni a86de3fe5b Qualify restricted SDK syntax independently of build architecture 2026-10-08 12:30:33 -07:00
Timothy J. Aveni 165510341d Repair packed agent startup and bound activation metadata retention 2026-10-08 11:19:19 -07:00
Quixos Subtree Publisher b406738249 Publish quixos-protocol from 11d2ad06170609a09118cbabd4245b5828a148d1 2026-10-08 18:19:19 +00:00
40 changed files with 1962 additions and 78 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
"sourceCommit": "c4319441699b7b8c5af7e70dade6e4ad8cccddea",
"sourceCommit": "8014b4ba717313e1b494c59a8e2781df58bf9a85",
"sourcePath": "quixos-protocol",
"exportName": "quixos-protocol",
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
+13
View File
@@ -6,6 +6,7 @@
kind,
generator,
contractOnly ? false,
workspaceId ? null,
system ? builtins.currentSystem,
}:
let
@@ -164,6 +165,11 @@ let
${queryGeneratorFlags root} \
--checked-resources ${checkedResources node} \
--source-root-commit ${pkgs.lib.escapeShellArg node.commit} \
${
pkgs.lib.optionalString (
workspaceId != null
) "--workspace-id ${pkgs.lib.escapeShellArg workspaceId}"
} \
--checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots node} \
--graph-out "$out/graph.json" --schemas-out "$out/package-schemas.json" \
${
@@ -357,6 +363,13 @@ let
)
);
in
assert
workspaceId == null
|| (
kind == "workspace"
&& builtins.isString workspaceId
&& builtins.match "[a-zA-Z0-9][a-zA-Z0-9:._-]{0,255}" workspaceId != null
);
pkgs.runCommand (if contractOnly then "qx-contract" else "qx-checked-candidate") { } ''
mkdir -p "$out"
cp ${
+8
View File
@@ -3,6 +3,7 @@ import path from "node:path";
import { profile } from "./profile.js";
import { loadQxSources, resolveQxSources } from "./source-loader.js";
import { compileQuery, compileRepositoryQuery } from "../query/compile.js";
import { queryTreeProjections } from "../query/tree.js";
import type { QueryDeclaration } from "../query/types.js";
import { compileQueryComposition, type QueryComposition } from "../query/composition.js";
import { checkQueryTemplate } from "../query/templates.js";
@@ -390,6 +391,13 @@ const createResourceGraphResolver = (
};
pkg.checkedQueries = [];
for (const query of pkg.queries!) pkg.checkedQueries.push(await compile(query.id));
const projections = pkg.checkedQueries.flatMap(queryTreeProjections);
for (const projection of projections) {
if (pkg.queries!.some((query) => query.id === projection.declaration.id))
throw new Error(`Generated Query Tree projection conflicts with ${projection.declaration.id}`);
pkg.queries!.push(projection.declaration);
pkg.checkedQueries.push(projection);
}
}
if (generated?.compositions?.length) {
const pkg = compiled.resource.revision;
@@ -97,6 +97,7 @@ export async function checkAuthoring(
(phase, ms) => {
timings[phase] = ms;
},
resource.kind === "workspace" ? context.workspaceId : undefined,
);
} finally {
timings.immutableCheckMs = Math.round(performance.now() - buildStarted);
+7 -1
View File
@@ -20,7 +20,12 @@ export async function authoringContext(start: string) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
if (text !== undefined) {
const graph = JSON.parse(text) as { resources: AuthoringResource[] };
const graph = JSON.parse(text) as { resources: AuthoringResource[]; workspaceId?: string };
if (
graph.workspaceId !== undefined &&
(typeof graph.workspaceId !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,255}$/.test(graph.workspaceId))
)
throw new Error("Managed workbench has an invalid Workspace identity");
if (!Array.isArray(graph.resources)) throw new Error("Managed resource inventory is malformed");
const resources: AuthoringResource[] = [{ kind: "workspace", directory: "root" }];
const identities = new Set<string>(),
@@ -52,6 +57,7 @@ export async function authoringContext(start: string) {
}
return {
workbench,
workspaceId: graph.workspaceId,
resources,
async baseline() {
const result = await loadQuixosLock(path.join(workbench, "root/quixos.lock"));
+4
View File
@@ -112,7 +112,10 @@ export async function buildImmutableCandidate(
logFile: string,
contractOnly = false,
observe?: (phase: "nixEvaluationMs" | "nixBuildMs", ms: number) => void,
workspaceId?: string,
): Promise<string> {
if (workspaceId !== undefined && (kind !== "workspace" || !/^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,255}$/.test(workspaceId)))
throw new Error("Instance binding requires a valid Workspace identity and a Workspace root");
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source.commit))
throw new Error("An immutable candidate requires an exact commit");
const url = new URL(source.repository);
@@ -200,6 +203,7 @@ export async function buildImmutableCandidate(
"--arg",
"contractOnly",
contractOnly ? "true" : "false",
...(workspaceId === undefined ? [] : ["--argstr", "workspaceId", workspaceId]),
"--add-root",
gcRoot,
"--indirect",
@@ -0,0 +1,93 @@
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { randomUUID } from "node:crypto";
import { mkdir, mkdtemp, lstat, rename, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { materializeWorkspaceSources, workspaceSourcePin, type WorkspaceSourcePin } from "./source-closure.js";
const exec = promisify(callback);
/** Developer workflow, not an ordinary Workspace Agent's implicit permission
* to publish against canonical repositories. Creates independent Git/jj copies,
* with instance binding outside committed source. No Graph is started here.
*/
export async function prepareDevelopmentSources(options: {
source: WorkspaceSourcePin;
directory: string;
cacheDirectory: string;
locations?: Record<string, string>;
}) {
const source = workspaceSourcePin(options.source);
const destination = path.resolve(options.directory);
const cache = path.resolve(options.cacheDirectory);
if (cache === destination || cache.startsWith(destination + path.sep))
throw Error("The retained source cache must live outside the disposable workbench");
if (
await lstat(destination).catch((error: NodeJS.ErrnoException) => {
if (error.code !== "ENOENT") throw error;
return null;
})
)
throw Error("Choose a new development workbench directory; existing checkouts are never overwritten");
const closure = await materializeWorkspaceSources({
source,
directory: options.cacheDirectory,
locations: options.locations,
});
const origins = new Set<string>();
for (const entry of closure.resources) {
const identity = `${entry.kind}:${entry.repository}`;
if (origins.has(identity)) throw Error("Multiple revisions of one repository require explicit editable selection");
origins.add(identity);
}
const parent = path.dirname(destination);
await mkdir(parent, { recursive: true });
const staging = await mkdtemp(path.join(parent, ".development-sources-"));
const workspaceId = randomUUID();
try {
const resources = [];
for (const entry of closure.resources) {
const relative = entry.kind === "workspace" ? "root" : "resources/" + entry.directory;
const checkout = path.join(staging, relative);
await mkdir(path.dirname(checkout), { recursive: true });
await exec("git", [
"clone",
"--no-hardlinks",
"--",
path.join(path.resolve(options.cacheDirectory), entry.directory),
checkout,
]);
await exec("git", ["remote", "set-url", "origin", entry.repository], { cwd: checkout });
await exec("git", ["checkout", "--detach", entry.commit], { cwd: checkout });
await exec("jj", ["git", "init", "--colocate"], {
cwd: checkout,
env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0" },
});
await exec("jj", ["describe", "-m", "Develop canonical application source"], {
cwd: checkout,
env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0" },
});
if (entry.kind !== "workspace")
resources.push({
kind: entry.kind,
directory: relative,
source: { resolver: "git", repository: entry.repository, commit: entry.commit },
});
}
await mkdir(path.join(staging, ".quixos"), { mode: 0o700 });
await writeFile(
path.join(staging, ".quixos/resource-graph.json"),
JSON.stringify({ formatVersion: 1, workspaceId, resources }, null, 2) + "\n",
{ mode: 0o600 },
);
await writeFile(
path.join(staging, ".quixos/development-source.json"),
JSON.stringify({ source, workspaceId }, null, 2) + "\n",
{ mode: 0o600 },
);
await rename(staging, destination);
return { directory: destination, source, workspaceId, repositories: closure.resources.length };
} finally {
await rm(staging, { recursive: true, force: true });
}
}
+117
View File
@@ -0,0 +1,117 @@
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { retentionTagForCommit } from "../resource-lock/index.js";
import { materializeWorkspaceSources, workspaceSourcePin, type WorkspaceSourcePin } from "./source-closure.js";
const exec = promisify(callback);
type SourceBackup = {
formatVersion: 1;
root: WorkspaceSourcePin;
repositories: Array<{ repository: string; commits: string[]; bundle: string; sha256: string }>;
};
async function hash(filename: string) {
const digest = createHash("sha256");
for await (const chunk of createReadStream(filename)) digest.update(chunk);
return digest.digest("hex");
}
/** Source-only backup: no Graph state, checkout instructions or credentials.
* Git bundles retain exact commits and their ancestors without needing a forge.
* Storage/retention of the resulting directory belongs to the operator. */
export async function backupWorkspaceSources(options: {
source: WorkspaceSourcePin;
directory: string;
output: string;
locations?: Record<string, string>;
}): Promise<SourceBackup> {
const output = path.resolve(options.output);
if (
await lstat(output).catch((error: NodeJS.ErrnoException) => {
if (error.code !== "ENOENT") throw error;
return null;
})
)
throw Error("Source backup destination must not exist");
const closure = await materializeWorkspaceSources(options);
const grouped = new Map<string, Map<string, string>>();
for (const entry of closure.resources) {
const commits = grouped.get(entry.repository) ?? new Map<string, string>();
commits.set(entry.commit, path.join(path.resolve(options.directory), entry.directory));
grouped.set(entry.repository, commits);
}
await mkdir(path.dirname(output), { recursive: true });
const staging = await mkdtemp(path.join(path.dirname(output), ".source-backup-"));
const manifest: SourceBackup = { formatVersion: 1, root: closure.root, repositories: [] };
try {
for (const [repository, commits] of [...grouped].sort(([a], [b]) => a.localeCompare(b))) {
const bundle = createHash("sha256").update(repository).digest("hex") + ".bundle";
const gitDirectory = await mkdtemp(path.join(staging, ".git-"));
try {
await exec("git", ["init", "--bare", gitDirectory]);
const refs: string[] = [];
for (const [commit, checkout] of commits) {
const ref = retentionTagForCommit(commit);
await exec("git", ["-C", gitDirectory, "fetch", "--no-tags", "--", checkout, `${ref}:${ref}`]);
refs.push(ref);
}
await exec("git", ["-C", gitDirectory, "bundle", "create", path.join(staging, bundle), ...refs]);
} finally {
await rm(gitDirectory, { recursive: true, force: true });
}
manifest.repositories.push({
repository,
commits: [...commits.keys()].sort(),
bundle,
sha256: await hash(path.join(staging, bundle)),
});
}
await writeFile(path.join(staging, "backup.json"), JSON.stringify(manifest, null, 2) + "\n", { mode: 0o600 });
await rename(staging, output);
return manifest;
} finally {
await rm(staging, { recursive: true, force: true });
}
}
export async function restoreWorkspaceSources(options: { backup: string; directory: string }) {
const backup = path.resolve(options.backup);
const filename = path.join(backup, "backup.json");
const info = await lstat(filename);
if (!info.isFile() || info.isSymbolicLink() || info.size > 1024 * 1024) throw Error("Invalid source backup manifest");
const manifest = JSON.parse(await readFile(filename, "utf8")) as SourceBackup;
if (
manifest.formatVersion !== 1 ||
!Array.isArray(manifest.repositories) ||
!manifest.repositories.length ||
manifest.repositories.length > 256
)
throw Error("Invalid source backup manifest");
const root = workspaceSourcePin(manifest.root);
const locations: Record<string, string> = {};
for (const entry of manifest.repositories) {
if (!entry || !Array.isArray(entry.commits) || !entry.commits.length || entry.commits.length > 256)
throw Error("Invalid source backup entry");
for (const commit of entry.commits) workspaceSourcePin({ repository: entry.repository, commit });
if (Object.hasOwn(locations, entry.repository)) throw Error("Duplicate source backup repository");
const expected = createHash("sha256").update(entry.repository).digest("hex") + ".bundle";
if (entry.bundle !== expected || !/^[a-f0-9]{64}$/.test(entry.sha256))
throw Error("Invalid source backup bundle identity");
const bundle = path.join(backup, expected);
const stat = await lstat(bundle);
if (!stat.isFile() || stat.isSymbolicLink() || (await hash(bundle)) !== entry.sha256)
throw Error("Source backup bundle integrity mismatch");
locations[entry.repository] = pathToFileURL(bundle).href;
}
if (
!manifest.repositories.some((entry) => entry.repository === root.repository && entry.commits.includes(root.commit))
)
throw Error("Source backup does not retain its root commit");
// Every discovered dependency must use the backup. Never fall through to the
// original forge if a manifest omits a required repository.
return materializeWorkspaceSources({ source: root, directory: options.directory, locations, requireLocations: true });
}
@@ -2,7 +2,9 @@ import { buildImmutableCandidate } from "./checked-build.js";
// Installed platform driver, run only inside the sealed checker's unprivileged
// evaluator. Authored flakes never become the outer evaluator program.
const [repository, commit, log] = process.argv.slice(2);
if (!repository || !commit || !log || process.argv.length !== 5)
throw Error("source-check-driver REPOSITORY COMMIT LOG");
process.stdout.write((await buildImmutableCandidate({ repository, commit }, "workspace", log)) + "\n");
const [repository, commit, log, workspaceId] = process.argv.slice(2);
if (!repository || !commit || !log || !workspaceId || process.argv.length !== 6)
throw Error("source-check-driver REPOSITORY COMMIT LOG WORKSPACE_ID");
process.stdout.write(
(await buildImmutableCandidate({ repository, commit }, "workspace", log, false, undefined, workspaceId)) + "\n",
);
+176
View File
@@ -0,0 +1,176 @@
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { createHash, randomUUID } from "node:crypto";
import { mkdir, mkdtemp, lstat, realpath, rename, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { loadQuixosLock, retentionTagForCommit } from "../resource-lock/index.js";
const exec = promisify(callback);
import { workspaceSourcePin, type WorkspaceSourcePin } from "./source-pin.js";
export { workspaceSourcePin, type WorkspaceSourcePin } from "./source-pin.js";
export type SourceClosureEntry = WorkspaceSourcePin & {
kind: "workspace" | "interface" | "package" | "build";
directory: string;
};
export type WorkspaceSourceClosure = {
formatVersion: 1;
root: WorkspaceSourcePin;
resources: SourceClosureEntry[];
};
/** Trusted transport substitutions preserve source identity. A mirror or bundle
* is explicitly selected, never tried automatically after an origin failure.
* Git authentication stays in the caller's Git configuration, outside metadata.
*/
export async function materializeWorkspaceSources(options: {
source: WorkspaceSourcePin;
directory: string;
locations?: Record<string, string>;
concurrency?: number;
maxRepositories?: number;
requireLocations?: boolean;
/** Optional acquisition boundary, enforced before fetching each dependency.
* It limits canonical identity; explicit transport substitutions remain a
* separate trusted operator decision. */
repositoryPrefix?: string;
}): Promise<WorkspaceSourceClosure> {
const source = workspaceSourcePin(options.source);
const prefix = options.repositoryPrefix ? new URL(options.repositoryPrefix) : null;
if (
prefix &&
(prefix.protocol !== "https:" ||
prefix.username ||
prefix.password ||
prefix.search ||
prefix.hash ||
!prefix.pathname.endsWith("/") ||
/[%\\\\]/.test(prefix.pathname))
)
throw Error("Source repository prefix requires a credential-free HTTPS directory URL");
const concurrency = options.concurrency ?? 4,
maxRepositories = options.maxRepositories ?? 256;
if (
!Number.isInteger(concurrency) ||
concurrency < 1 ||
concurrency > 16 ||
!Number.isInteger(maxRepositories) ||
maxRepositories < 1 ||
maxRepositories > 4096
)
throw Error("Invalid source closure limits");
const locations = new Map(Object.entries(options.locations ?? {}));
for (const [canonical, location] of locations) {
workspaceSourcePin({ repository: canonical, commit: source.commit });
const url = new URL(location);
if (
!["https:", "file:"].includes(url.protocol) ||
url.username ||
url.password ||
url.search ||
url.hash ||
(url.protocol === "file:" && url.hostname !== "")
)
throw Error("Invalid source transport substitution");
}
await mkdir(options.directory, { recursive: true, mode: 0o700 });
const directory = await realpath(options.directory);
const entries = new Map<string, SourceClosureEntry>();
const pending: SourceClosureEntry[] = [];
const enqueue = (pin: WorkspaceSourcePin, kind: SourceClosureEntry["kind"]) => {
pin = workspaceSourcePin(pin);
const repository = new URL(pin.repository);
if (
prefix &&
(repository.origin !== prefix.origin ||
!repository.pathname.startsWith(prefix.pathname) ||
/[%\\\\]/.test(repository.pathname))
)
throw Error(`Source repository is outside the acquisition boundary: ${pin.repository}`);
if (options.requireLocations && !locations.has(pin.repository))
throw Error(`Source backup omits required repository: ${pin.repository}`);
const key = `${kind}\0${pin.repository}\0${pin.commit}`;
if (entries.has(key)) return;
if (entries.size >= maxRepositories) throw Error("Workspace source closure exceeds repository limit");
const name = `${kind}-${createHash("sha256").update(key).digest("hex").slice(0, 24)}`;
const entry = { ...pin, kind, directory: name };
entries.set(key, entry);
pending.push(entry);
};
enqueue(source, "workspace");
const git = async (cwd: string, args: string[]) =>
(
await exec("git", args, {
cwd,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
maxBuffer: 4 * 1024 * 1024,
})
).stdout.trim();
const checkout = async (entry: SourceClosureEntry) => {
const destination = path.join(directory, entry.directory);
const verify = async () => {
const info = await lstat(destination);
if (!info.isDirectory() || info.isSymbolicLink() || (await realpath(destination)) !== destination)
throw Error("Source checkout must be a contained directory");
if ((await git(destination, ["rev-parse", "HEAD"])) !== entry.commit)
throw Error(`Source commit mismatch: ${entry.repository}`);
if (await git(destination, ["status", "--porcelain", "--untracked-files=all"]))
throw Error(`Source checkout was modified: ${entry.repository}`);
};
const existing = await lstat(destination).catch((error: NodeJS.ErrnoException) => {
if (error.code !== "ENOENT") throw error;
return null;
});
if (!existing) {
const staging = await mkdtemp(path.join(directory, ".fetch-"));
try {
await git(staging, ["init"]);
await git(staging, ["remote", "add", "origin", entry.repository]);
const ref = retentionTagForCommit(entry.commit);
// Full history supports normal jj development and independent bundles.
const location = locations.get(entry.repository) ?? entry.repository;
await git(staging, [
"fetch",
"--no-tags",
"--",
location.startsWith("file:") ? fileURLToPath(location) : location,
`${ref}:${ref}`,
]);
await git(staging, ["checkout", "--detach", entry.commit]);
try {
await rename(staging, destination);
} catch (error) {
if (!["EEXIST", "ENOTEMPTY"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error;
}
} finally {
await rm(staging, { recursive: true, force: true });
}
}
await verify();
const lock = await loadQuixosLock(path.join(destination, "quixos.lock"));
if (!lock.ok)
throw Error(`Invalid source lock in ${entry.repository}: ${lock.diagnostics.map((d) => d.message).join("; ")}`);
return lock.lock.resources;
};
// Bounded breadth-first batches resolve diamonds once and avoid recursive
// promise deadlocks. Completion order cannot affect the recorded closure.
while (pending.length) {
const batch = pending.splice(0, concurrency);
const results = await Promise.allSettled(batch.map(checkout));
for (const result of results) {
if (result.status === "rejected") throw result.reason;
for (const resource of result.value) enqueue(resource.source, resource.kind);
}
}
const closure: WorkspaceSourceClosure = {
formatVersion: 1,
root: source,
resources: [...entries.values()].sort((a, b) =>
`${a.kind}:${a.repository}@${a.commit}`.localeCompare(`${b.kind}:${b.repository}@${b.commit}`),
),
};
const temporary = path.join(directory, `.closure-${randomUUID()}.json`);
await writeFile(temporary, JSON.stringify(closure, null, 2) + "\n", { flag: "wx", mode: 0o600 });
await rename(temporary, path.join(directory, "sources.json"));
return closure;
}
+48
View File
@@ -0,0 +1,48 @@
import { readFile } from "node:fs/promises";
import path from "node:path";
import { loadQuixosLock, parseQuixosLockDocument, resolveQuixosLock } from "../resource-lock/index.js";
import { editStructure } from "./structural-edits.js";
import { workspaceSourcePin, type WorkspaceSourcePin } from "./source-closure.js";
/** Inspect/rewrite the root and its declared imports only, using the same lock
* grammar as compilation. Return proposed file contents; publishing them and
* selecting a Graph remain the caller's separate responsibilities. */
export async function workspaceSourceLocks(directory: string, input: unknown = []) {
if (!Array.isArray(input) || input.length > 256) throw Error("Invalid source lock replacements");
const replacements = new Map<string, WorkspaceSourcePin>();
const key = (source: WorkspaceSourcePin) => `${source.repository}\0${source.commit}`;
for (const entry of input) {
if (!entry || typeof entry !== "object") throw Error("Invalid source lock replacement");
const from = workspaceSourcePin(entry.from),
to = workspaceSourcePin(entry.to);
if (replacements.has(key(from))) throw Error("Duplicate source lock replacement");
replacements.set(key(from), to);
}
const lock = await loadQuixosLock(path.join(directory, "quixos.lock"));
if (!lock.ok) throw Error(`Invalid source lock: ${lock.diagnostics.map((d) => d.message).join("; ")}`);
const files = new Map<string, string>();
for (const filename of lock.lock.sourceFiles!) {
let source = await readFile(path.join(directory, filename), "utf8");
const parsed = parseQuixosLockDocument(source, filename);
if (!parsed.ok) throw Error("Source lock changed during inspection");
for (const dependency of parsed.document.resources) {
const replacement = replacements.get(key(dependency.source));
if (replacement)
source = editStructure(source, {
operation: "dependency",
kind: dependency.kind,
name: dependency.binding,
source: replacement,
});
}
files.set(filename, source);
}
const checked = await resolveQuixosLock(files.get("quixos.lock")!, async (filename) => {
const source = files.get(filename);
if (source === undefined) throw Error("Source lock import changed during inspection");
return source;
});
if (!checked.ok)
throw Error(`Invalid rewritten source locks: ${checked.diagnostics.map((d) => d.message).join("; ")}`);
return { lock: checked.lock, files: [...files].map(([path, source]) => ({ path, source })) };
}
+18
View File
@@ -0,0 +1,18 @@
export type WorkspaceSourcePin = { repository: string; commit: string };
export function workspaceSourcePin(value: unknown): WorkspaceSourcePin {
if (!value || typeof value !== "object" || Array.isArray(value)) throw Error("Invalid Workspace source pin");
const pin = value as WorkspaceSourcePin;
const url = new URL(pin.repository);
if (
url.protocol !== "https:" ||
url.username ||
url.password ||
url.search ||
url.hash ||
typeof pin.commit !== "string" ||
!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(pin.commit)
)
throw Error("Workspace source pins require credential-free HTTPS and an exact commit");
return { repository: pin.repository, commit: pin.commit };
}
+64 -3
View File
@@ -54,6 +54,61 @@ const planSummary = (plan: Awaited<ReturnType<typeof planStructure>>) => ({
const main = async () => {
const [command, ...args] = process.argv.slice(2);
if (command === "source-locks") {
if (!args[0] || (args.length !== 1 && (args.length !== 3 || args[1] !== "--replacements")))
throw Error("source-locks DIRECTORY [--replacements JSON_FILE]");
const { workspaceSourceLocks } = await import("./source-locks.js");
const replacements = args[2] ? JSON.parse(await readFile(args[2], "utf8")) : [];
process.stdout.write(JSON.stringify(await workspaceSourceLocks(path.resolve(args[0]), replacements)) + "\n");
return;
}
if (["source-checkout", "source-develop", "source-backup", "source-restore"].includes(command!)) {
const args = ["source", command.slice("source-".length), ...process.argv.slice(3)];
const { materializeWorkspaceSources } = await import("./source-closure.js");
if (!args[2]) throw Error("source requires a pinned source JSON file");
const flags = new Map<string, string>();
for (let i = 3; i < args.length; i += 2) {
if (
![
"--dir",
...(args[1] !== "restore" ? ["--locations"] : []),
...(args[1] === "checkout" ? ["--repository-prefix"] : []),
...(args[1] === "develop" ? ["--cache"] : []),
...(args[1] === "backup" ? ["--output"] : []),
].includes(args[i]) ||
!args[i + 1] ||
flags.has(args[i])
)
throw Error("Invalid source command options; use qx-workspace help source");
flags.set(args[i], args[i + 1]);
}
if (!flags.get("--dir")) throw Error("source requires --dir DIRECTORY");
if (args[1] === "restore") {
const { restoreWorkspaceSources } = await import("./source-backup.js");
const result = await restoreWorkspaceSources({ backup: args[2], directory: path.resolve(flags.get("--dir")!) });
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
return;
}
const locationsFile = flags.get("--locations");
const options = {
source: JSON.parse(await readFile(args[2], "utf8")),
directory: path.resolve(flags.get("--dir")!),
...(locationsFile ? { locations: JSON.parse(await readFile(locationsFile, "utf8")) } : {}),
...(flags.has("--repository-prefix") ? { repositoryPrefix: flags.get("--repository-prefix") } : {}),
};
let result;
if (args[1] === "develop") {
if (!flags.get("--cache")) throw Error("source develop requires --cache DIRECTORY");
const { prepareDevelopmentSources } = await import("./development-sources.js");
result = await prepareDevelopmentSources({ ...options, cacheDirectory: path.resolve(flags.get("--cache")!) });
} else if (args[1] === "backup") {
if (!flags.get("--output")) throw Error("source backup requires --output DIRECTORY");
const { backupWorkspaceSources } = await import("./source-backup.js");
result = await backupWorkspaceSources({ ...options, output: path.resolve(flags.get("--output")!) });
} else result = await materializeWorkspaceSources(options);
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
return;
}
if (command === "source-manifest") {
if (args.length !== 1) throw new Error("source-manifest WORKSPACE_ROOT");
process.stdout.write(JSON.stringify(await qxSourceManifest(args[0])) + "\n");
@@ -197,10 +252,16 @@ const main = async () => {
}
if (command === "check-committed" && !args.includes("--help")) {
const [kind, repository, commit, log, ...extra] = args;
if (!["workspace", "interface", "package", "build"].includes(kind) || !log || extra.length)
throw new Error("usage: quixos-qx check-committed workspace|interface|package|build REPOSITORY COMMIT LOG_FILE");
if (
!["workspace", "interface", "package", "build"].includes(kind) ||
!log ||
(extra.length !== 0 && (kind !== "workspace" || extra.length !== 2 || extra[0] !== "--workspace-id" || !extra[1]))
)
throw new Error(
"usage: quixos-qx check-committed workspace|interface|package|build REPOSITORY COMMIT LOG_FILE [--workspace-id ID]",
);
process.stdout.write(
`${await buildImmutableCandidate({ repository, commit }, kind as "workspace" | "interface" | "package" | "build", log)}\n`,
`${await buildImmutableCandidate({ repository, commit }, kind as "workspace" | "interface" | "package" | "build", log, false, undefined, extra[1])}\n`,
);
return;
}
+8 -3
View File
@@ -17,7 +17,7 @@ import {
} from "../capability-model/index.js";
const usage = `usage: quixos-workspace-compile --root DIRECTORY --checkout-root DIRECTORY
[--snapshot-map PATH] [--graph-out PATH] [--workspace-id ID] [--workspace-revision-id ID]
[--snapshot-map PATH] [--graph-out PATH] [--plan-out PATH] [--workspace-id ID] [--workspace-revision-id ID]
[--source-root-commit GIT_REV] [--baseline PLAN_JSON] [--evolution-out PATH]
[--reviews REVIEW_JSON] [--schemas-out PATH] [--execution-plan-out PATH] [--build-contexts-out PATH]
[--query-generator BUILD_MODULE_PATH] [--checked-resources NIX_MAP]
@@ -41,6 +41,7 @@ const parseArgs = (args: string[]) => {
rootDirectory,
checkoutRoot,
graphOut: values.get("--graph-out"),
planOut: values.get("--plan-out"),
schemasOut: values.get("--schemas-out"),
executionPlanOut: values.get("--execution-plan-out"),
buildContextsOut: values.get("--build-contexts-out"),
@@ -84,6 +85,7 @@ const main = async () => {
`${JSON.stringify(
{
formatVersion: 1,
workspaceId: assembled.workspace.workspaceId,
quixos: assembled.lock.quixos,
directResources: [...assembled.directResources.entries()].map(([bindingKey, node]) => {
const [kind, binding] = bindingKey.split("\0");
@@ -118,7 +120,9 @@ const main = async () => {
if (options.executionPlanOut || options.buildContextsOut) {
const executionStarted = performance.now();
const plan = compileExecutionPlan(assembled.workspace);
if (options.executionPlanOut) await writeFile(options.executionPlanOut, `${JSON.stringify(plan, null, 2)}\n`);
// Machine-consumed immutable metadata should not spend its bounded input
// reservation on indentation. Its parsed allocation is charged separately.
if (options.executionPlanOut) await writeFile(options.executionPlanOut, `${JSON.stringify(plan)}\n`);
if (options.buildContextsOut)
await writeFile(options.buildContextsOut, JSON.stringify(packageBuildContexts(plan.definition.bindingSchema)));
profile("execution-plan-and-contexts", executionStarted);
@@ -155,7 +159,8 @@ const main = async () => {
`${JSON.stringify(planEvolution(baseline, candidate, { reviews }), null, 2)}\n`,
);
}
process.stdout.write(`${JSON.stringify(candidate, null, 2)}\n`);
if (options.planOut) await writeFile(options.planOut, `${JSON.stringify(candidate)}\n`);
else process.stdout.write(`${JSON.stringify(candidate, null, 2)}\n`);
};
main().catch((error: unknown) => {
+11 -5
View File
@@ -1,5 +1,11 @@
# Closed execution plans and artifacts
This page documents current compiler/runtime representations. Its universal
artifact inventory, React metadata and overlapping world/installation structures
are [known architectural gaps](../../../docs/ARCHITECTURE_STATUS.md), not the
[core package contract](../../../docs/ARCHITECTURE.md). Historical LF task numbers
refer to the original implementation sequence, not an active queue.
`compileExecutionPlan` validates a workspace, links its checked queries, resolves
public forwarding, and emits one versioned plan with an SHA-256 digest. It retains
exact interface/package revisions and generic applications, owner initialization,
@@ -60,11 +66,11 @@ the core codec's canonical ordering and domain-separated SHA-256 framing. The
monorepo `camino-execution-world` check compares the compiler and core implementations.
No machine-local Nix path enters the world descriptor.
The current binding schema is the closed workspace schema. This is conservative:
an unrelated contract change can require checking/rebuilding several packages.
LF-05 may narrow it to exact checked per-package closures, but must use the same
schema for generation, receipts, and world validation. It must not substitute an
older package schema just because the source revision did not change.
Reusable package compilation now receives a canonical per-package build contract;
exact source provenance and workspace linking remain separate. See
[package build contracts](../../../docs/PACKAGE_BUILD_CONTRACTS.md). Generation,
receipts and checking must agree on that contract; unchanged source alone does
not justify reusing an incompatible binding schema.
## Transaction-port ABI
+47 -23
View File
@@ -2,7 +2,11 @@ import { graphExecutionRepresentation } from "./graph-execution.js";
import { lstat } from "node:fs/promises";
import { join } from "node:path";
import { hexDigest, ExecutionPlanError, type ExecutionPlan } from "./plan.js";
import { loadExecutionInstallation, type PackageInstallationSelection } from "./installation.js";
import {
loadExecutionInstallation,
createClientContractCatalog,
type PackageInstallationSelection,
} from "./installation.js";
import {
candidateMetadataBudget,
readCandidateMetadata,
@@ -11,7 +15,10 @@ import {
} from "./metadata.js";
const rootPattern = /^\/nix\/store\/[a-z0-9]{32}-[^/\s]+$/;
async function readJson(root: string, file: string, scope: CandidateMetadataScope) {
const input = await readCandidateMetadata(join(root, file), scope, 32 * 1024 * 1024);
// Query Trees add checked projections to an already large application plan.
// The Reader now exceeds 32 MiB even without indentation. This file bound
// does not replace the stricter aggregate/owner parse-allocation budgets.
const input = await readCandidateMetadata(join(root, file), scope, 64 * 1024 * 1024);
try {
return {
value: JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.bytes)),
@@ -36,33 +43,37 @@ export async function loadCheckedInstallationCandidate(
if (!rootPattern.test(root) || !(await lstat(root)).isDirectory())
throw Error("Candidate must be an immutable Nix output directory");
const scope = (resources.budget ?? candidateMetadataBudget).acquire(resources.owner ?? root);
let releaseValidation = () => {};
try {
// Validate the duplicated source contract by digest, then discard its tree
// before allocating the substantially larger checked execution plan.
const workspaceDigest = await (async () => {
const input = await readJson(root, "candidate.json", scope);
try {
const { executionContracts: _legacyContracts, ...workspace } = input.value;
return hexDigest(workspace);
} finally {
input.releaseTree();
}
})();
// Sequential reads avoid partially rejected Promise.all() work allocating
// after the failed candidate's reservation has been released.
const values = [];
for (const file of [
"candidate.json",
"execution-plan.json",
"execution-artifacts.json",
"source.json",
"entry.json",
"kernel.json",
"queries.json",
"graph-execution.json",
]) {
const parsed = await readJson(root, file, scope);
values.push(parsed.value);
// candidate.json duplicates the checked plan for validation. Nothing in
// the installed candidate retains this input tree after validation.
if (file === "candidate.json") releaseValidation = parsed.releaseTree;
}
const [candidate, plan, artifacts, source, entryRequest, kernel, queries, graph] = values;
const { executionContracts: _legacyContracts, ...workspace } = candidate;
const [plan, artifacts, source, entryRequest, kernel, queries] = values;
if (
plan?.definition?.codec !== "camino-execution-plan-v1" ||
hexDigest(plan.definition) !== plan.digest ||
hexDigest(workspace) !== hexDigest(plan.definition.workspace)
workspaceDigest !== hexDigest(plan.definition.workspace)
)
throw new ExecutionPlanError(
"CANDIDATE_PLAN_MISMATCH",
@@ -113,14 +124,24 @@ export async function loadCheckedInstallationCandidate(
"Stored interpreter input must match the checked build and plan",
);
const expectedGraph = graphExecutionRepresentation(plan, installation, kernel, queries);
if (hexDigest(graph) !== hexDigest(expectedGraph) || artifacts.execution !== hexDigest(graph))
throw new ExecutionPlanError(
"CANDIDATE_EXECUTION_MISMATCH",
root,
"Stored graph execution differs from checked inputs",
);
// The stored graph embeds the same kernel/query programs. Validate it in a
// short-lived scope and retain the representation that shares those trees.
await (async () => {
const input = await readJson(root, "graph-execution.json", scope);
try {
const digest = hexDigest(input.value);
if (digest !== hexDigest(expectedGraph) || artifacts.execution !== digest)
throw new ExecutionPlanError(
"CANDIDATE_EXECUTION_MISMATCH",
root,
"Stored graph execution differs from checked inputs",
);
} finally {
input.releaseTree();
}
})();
const provenance = {
workspaceRevision: workspace.id as string,
workspaceRevision: plan.definition.workspace.id as string,
source: { repository: source.repository as string, commit: source.commit as string },
generator: artifacts.generator as string,
packages: (plan as ExecutionPlan).definition.packages.map((pkg) => ({
@@ -139,30 +160,33 @@ export async function loadCheckedInstallationCandidate(
programs: artifacts.programs,
}),
};
const catalog = createClientContractCatalog(plan, installation, scope, kernel);
return {
reference,
workspace: plan.definition.workspace.workspaceId,
world: hexDigest(graph),
world: hexDigest(expectedGraph),
plan: kernel.plan as string,
graphExecution: graph,
graphExecution: expectedGraph,
provenance,
entry,
kernel,
queries,
installation,
executionPlan: plan as ExecutionPlan,
clientContractCatalog: catalog.select,
metadata: {
get bytes() {
return scope.bytes;
},
release: scope.release,
release: () => {
catalog.close();
scope.release();
},
},
};
} catch (error) {
scope.release();
throw error;
} finally {
releaseValidation();
}
}
+15 -6
View File
@@ -11,6 +11,7 @@ import type { BindingSchema } from "../bindings/index.js";
import type { PackageBuildContract } from "./build-contract.js";
import { preparationArtifact, type ReactPreparationArtifact } from "../react-build/declarations.js";
import { moduleTypeContext } from "./module-contracts.js";
import { queryTreeBlueprint, queryTreeType } from "./query-trees.js";
/** Public shape only: no slot inventory, implementation binding, or workspace identity. */
export interface PublicOperationContract {
@@ -76,6 +77,7 @@ export interface PublicQueryContract {
sourceQuery?: string;
input: unknown;
output: unknown;
tree?: ReturnType<typeof queryTreeBlueprint>;
}
export function publicQueryContract(pkg: PackageRevision, queryId: string): PublicQueryContract {
const query = pkg.checkedQueries?.find((q) => q.declaration.id === queryId);
@@ -88,9 +90,10 @@ export function publicQueryContract(pkg: PackageRevision, queryId: string): Publ
definitionDigest: query.definitionDigest,
root: query.declaration.root,
placement: placement?.kind ?? "server",
...(placement?.kind === "local" ? { sourceQuery: placement.sourceQueryId } : {}),
...(placement?.kind === "local" ? { sourceQuery: query.treeProjection?.tree ?? placement.sourceQueryId } : {}),
input: portableValueSchema(query.variables),
output: portableValueSchema(query.output),
...(query.tree ? { tree: queryTreeBlueprint(pkg.revisionId, query) } : {}),
};
}
export function linkPackageQuery(plan: ExecutionPlan, contract: PublicQueryContract) {
@@ -156,10 +159,10 @@ export function generatePackageQueryClientBindings(
checkedQueries: pkg.checkedQueries?.filter((query) => !internal.includes(query.declaration.id)),
};
const queries = (pkg.checkedQueries ?? [])
.filter((query) => !query.declaration.fragment)
.filter((query) => !query.declaration.fragment && !query.treeProjection)
.map(
(query) =>
`${JSON.stringify(query.declaration.displayName)}: definePackageQuery<${tsType(query.variables)}, ${tsType(query.output)}, ${JSON.stringify(publicQueryContract(pkg, query.declaration.id).placement)}>(${JSON.stringify(publicQueryContract(pkg, query.declaration.id))})`,
`${JSON.stringify(query.declaration.displayName)}: ${query.tree ? `definePackageQueryTree<${tsType(query.variables)}, ${queryTreeType(query)}, ${JSON.stringify(query.declaration.root.interface)}>` : `definePackageQuery<${tsType(query.variables)}, ${tsType(query.output)}, ${JSON.stringify(publicQueryContract(pkg, query.declaration.id).placement)}>`}(${JSON.stringify(publicQueryContract(pkg, query.declaration.id))})`,
);
const emitFragment = (provider: PackageRevision, id: string) => {
const query = provider.checkedQueries?.find((query) => query.declaration.id === id);
@@ -210,7 +213,7 @@ export function generatePackageQueryClientBindings(
fragments.push(
`${JSON.stringify(alias)}: {${[...entries].map(([name, value]) => `${JSON.stringify(name)}: ${value}`).join(",")}}`,
);
return `// Generated from checked package query contracts. Regenerate; do not edit.\nimport { definePackageQuery, definePackageFragment, type ManagedReference } from ${JSON.stringify(runtimeModule)};\nexport const queries = {${queries.join(",\n")}} as const;\nexport const fragments = {${fragments.join(",\n")}} as const;\n`;
return `// Generated from checked package query contracts. Regenerate; do not edit.\nimport { definePackageQuery, definePackageFragment, ${queries.some((query) => query.includes("definePackageQueryTree<")) ? "definePackageQueryTree, type QueryTreeShape, type QueryTreeOccurrence, " : ""}type ManagedReference } from ${JSON.stringify(runtimeModule)};\nexport const queries = {${queries.join(",\n")}} as const;\nexport const fragments = {${fragments.join(",\n")}} as const;\n`;
}
/** Receipt-covered bindings expose the package's imports, component subjects and
@@ -279,7 +282,7 @@ export function generatePackageComponentBindings(
.join("\n");
return (
interfaces +
'\nimport {createObjectRenderer,definePackageQuery,definePackageFragment,type ComponentType,type ComponentProps,type PresentationHandle} from "@quixos/camino-react";\n' +
'\nimport {createObjectRenderer,definePackageQuery,definePackageFragment,definePackageQueryTree,type QueryTreeShape,type QueryTreeOccurrence,type QueryTreeHandle,type ComponentType,type ComponentProps,type PresentationHandle} from "@quixos/camino-react";\n' +
subjectBindings +
"\n" +
queries +
@@ -319,7 +322,13 @@ export function generatePackageComponentBindings(
const handle = `PresentationHandle<${JSON.stringify(child.subject)}, ${extra}>`;
return `readonly ${JSON.stringify(child.name)}: ${child.each ? `readonly ${handle}[]` : child.with || child.when ? `${handle} | null` : handle}`;
});
const input = `${tsType(preparation.input ?? composition.output)} & {readonly children: {${children.join(";")}}}`;
const trees = (preparation.trees ?? []).map((tree) => {
const query = pkg.checkedQueries?.find((query) => query.declaration.id === tree.query);
if (!query?.tree)
throw new ExecutionPlanError("MISSING_QUERY_TREE", tree.query, "Expected a checked Query Tree");
return `readonly ${JSON.stringify(tree.name)}: QueryTreeHandle<${queryTreeType(query)}>`;
});
const input = `${tsType(preparation.input ?? composition.output)} & {readonly children: {${children.join(";")}}${trees.length ? `;readonly trees:{${trees.join(";")}}` : ""}}`;
return `${JSON.stringify(component.displayName)}: ComponentType<ComponentProps<${JSON.stringify(component.subject)}, ${JSON.stringify(module.contract)}, ${moduleArguments(find(module.interfaceRevisionId))}, ${input}> & ExtraModuleProps<${JSON.stringify(key)}>>;`;
})
.join("\n") +
+17 -14
View File
@@ -35,20 +35,23 @@ export interface InspectionTemplate {
}
/** A finite installed template per interface/lane, not a per-client query compiler.
* Flags select public cache boundaries; normal scope identity bounds specialization. */
export function compileInspectionTemplates(plan: ExecutionPlan) {
const kernel = compileKernelProgram(plan) as {
operations: {
key: string;
atom: string;
interface: string;
operation: string;
effect: string;
targets: string[];
unsupported: string | null;
output: unknown;
binding: { kind: string };
}[];
};
export interface InspectionKernel {
operations: {
key: string;
atom: string;
interface: string;
operation: string;
effect: string;
targets: string[];
unsupported: string | null;
output: unknown;
binding: { kind: string };
}[];
}
export function compileInspectionTemplates(
plan: ExecutionPlan,
kernel: InspectionKernel = compileKernelProgram(plan) as InspectionKernel,
) {
const queries: unknown[] = [],
contracts: PublicQueryContract[] = [],
templates: InspectionTemplate[] = [];
+72 -2
View File
@@ -1,4 +1,4 @@
import { compileInspectionTemplates, type InspectionTemplate } from "./inspection.js";
import { compileInspectionTemplates, type InspectionTemplate, type InspectionKernel } from "./inspection.js";
import path from "node:path";
import { loadCheckedArtifactWorld } from "./receipts.js";
import { contentArtifactUrl, loadArtifactWorld, type LoadedArtifactWorld } from "./files.js";
@@ -45,6 +45,7 @@ export function clientContractCatalog(
plan: ExecutionPlan,
installation: ExecutionInstallation,
requestedPackages: readonly string[],
checkedKernel?: InspectionKernel,
): ClientContractCatalog {
if (installation.plan !== plan.digest || hexDigest(plan.definition) !== plan.digest)
throw new ExecutionPlanError(
@@ -69,7 +70,7 @@ export function clientContractCatalog(
.map((operation) => publicOperationContract(iface, operation)),
),
);
const inspection = compileInspectionTemplates(plan);
const inspection = compileInspectionTemplates(plan, checkedKernel);
return {
inspections: inspection.templates,
resultInterfaces: Object.fromEntries(
@@ -98,6 +99,75 @@ export function clientContractCatalog(
};
}
/** Candidate-owned compiler output, never graph state or authorization. Compile
* once, then project each requested package set without hashing/recompiling the
* plan. Reserve bounded metadata before compilation; disposal releases both the
* catalog and its charge. Callers must supply the candidate's checked inputs. */
export function createClientContractCatalog(
plan: ExecutionPlan,
installation: ExecutionInstallation,
metadata: CandidateMetadataScope,
checkedKernel?: InspectionKernel,
) {
const revisions = plan.definition.workspace.packageImports.map((pkg) => pkg.revisionId);
const installed = new Set<string>(revisions);
let catalog: ClientContractCatalog | undefined;
let release: (() => void) | undefined;
let closed = false;
const freeze = (value: unknown, seen = new WeakSet<object>()) => {
if (!value || typeof value !== "object" || seen.has(value)) return;
seen.add(value);
for (const child of Object.values(value)) freeze(child, seen);
Object.freeze(value);
};
return {
select(requestedPackages: readonly string[]): ClientContractCatalog {
if (closed) throw Error("CANDIDATE_METADATA_CLOSED");
for (const revision of requestedPackages)
if (!installed.has(revision))
throw new ExecutionPlanError("UNINSTALLED_PACKAGE", revision, "Requested catalog package is not installed");
if (!catalog) {
// Like other candidate admission charges, this is an allocation estimate,
// not a V8 heap limit. The process/cgroup ceiling bounds transient work.
const reservation = metadata.reserve(32 * 1024 * 1024);
try {
const compiled = clientContractCatalog(plan, installation, revisions, checkedKernel);
const bytes = Buffer.byteLength(JSON.stringify(compiled));
if (bytes > 8 * 1024 * 1024) throw Error("CANDIDATE_CATALOG_LIMIT");
freeze(compiled);
// No await between reservations: replacing the transient upper bound
// with a smaller retained estimate cannot race another admission.
reservation();
release = metadata.reserve(Math.max(64 * 1024, bytes * 4));
catalog = compiled;
} catch (error) {
reservation();
throw error;
}
}
const requested = new Set(requestedPackages);
return {
inspections: catalog.inspections,
resultInterfaces: catalog.resultInterfaces,
operations: catalog.operations,
queries: [
...catalog.queries.filter((query) => !installed.has(query.package)),
...requestedPackages.flatMap((revision) => catalog!.queries.filter((query) => query.package === revision)),
],
components: catalog.components.filter((component) => requested.has(component.packageRevision)),
moduleBindings: catalog.moduleBindings.filter((binding) => requested.has(binding.packageRevision)),
presentations: catalog.presentations.filter((presentation) => requested.has(presentation.packageRevision)),
};
},
close() {
closed = true;
catalog = undefined;
release?.();
release = undefined;
},
};
}
export interface PackageInstallationSelection {
readonly browser?: { readonly id?: string };
readonly service?: { readonly id?: string };
+5 -1
View File
@@ -9,7 +9,11 @@ export class CandidateMetadataBudget {
private bytes = 0;
private scopes = 0;
private readonly owners = new Map<string, number>();
constructor(limits = { bytes: 512 * 1024 * 1024, ownerBytes: 256 * 1024 * 1024, candidates: 128 }) {
// A cutover holds both the serving and proposed installation plus transient
// parsing space. The Reader with checked Query Trees retains ~176 MiB per
// installation; its replacement also needs transient parse/index space.
// Keep the aggregate ceiling independent and leave room for parsing overlap.
constructor(limits = { bytes: 768 * 1024 * 1024, ownerBytes: 512 * 1024 * 1024, candidates: 128 }) {
for (const value of Object.values(limits))
if (!Number.isSafeInteger(value) || value < 1) throw Error("INVALID_CANDIDATE_METADATA_BUDGET");
if (limits.ownerBytes > limits.bytes) throw Error("INVALID_CANDIDATE_METADATA_BUDGET");
+39 -3
View File
@@ -26,13 +26,41 @@ export interface ImportFinding {
export const restrictedSDKSpecifier = "quixos:restricted-sdk/v1";
export const restrictedSDKPath = "share/quixos/restricted/core.mjs";
/** Compare trusted SDK syntax without build-machine paths in source comments.
* Keep literal/directive spelling and every executable AST node. This is SDK
* compatibility only: artifact files and the executed host SDK still have exact
* byte digests, and authored SDK bytes are never loaded by the restricted host.
*/
export function restrictedSDKIdentity(source: string): string {
if (Buffer.byteLength(source) > 2 * 1024 * 1024) throw Error("RESTRICTED_SDK_ANALYSIS_LIMIT");
const syntax = parse(source, { sourceType: "module", attachComment: false });
const metadata = new Set([
"start",
"end",
"loc",
"comments",
"leadingComments",
"innerComments",
"trailingComments",
"parenStart",
]);
let nodes = 0;
const encoded = JSON.stringify(syntax, (key, value) => {
if (metadata.has(key)) return undefined;
if (value && typeof value === "object" && typeof value.type === "string" && ++nodes > 100000)
throw Error("RESTRICTED_SDK_ANALYSIS_LIMIT");
return value;
});
return hexDigest({ codec: "quixos-restricted-sdk-syntax-v1", syntax: JSON.parse(encoded) });
}
/** Mechanical candidate checks, not authorization or an engine-safety proof.
* The caller must first verify the manifest and all declared file digests. */
export async function inspectRestrictedImplementation(
revision: string,
implementation: ArtifactImplementation,
read: ArtifactReader,
qualifiedSDKDigest?: string,
qualifiedSDKIdentity?: string,
): Promise<ImportFinding[]> {
const entry = implementation.files.find((file) => file.path === implementation.path);
const sdk = implementation.files.find((file) => file.path === restrictedSDKPath);
@@ -48,10 +76,18 @@ export async function inspectRestrictedImplementation(
!/^[$A-Z_a-z][$\w]*$/.test(implementation.registryExport ?? "")
)
return [{ code: "RESTRICTED_CLOSURE", detail: "Expected one closed module and the restricted SDK", line: null }];
if (qualifiedSDKDigest && sdk.digest !== qualifiedSDKDigest)
return [{ code: "RESTRICTED_SDK_UNSUPPORTED", detail: "SDK differs from the qualified host profile", line: null }];
if (entry.bytes > 1024 * 1024 || sdk.bytes > 2 * 1024 * 1024)
return [{ code: "ANALYSIS_LIMIT", detail: "Restricted closure exceeds the source budget", line: null }];
if (qualifiedSDKIdentity) {
const source = Buffer.from(await read(revision, sdk.path, sdk.bytes)).toString("utf8");
try {
if (restrictedSDKIdentity(source) !== qualifiedSDKIdentity) throw Error("SDK syntax differs");
} catch {
return [
{ code: "RESTRICTED_SDK_UNSUPPORTED", detail: "SDK differs from the qualified host profile", line: null },
];
}
}
return inspectJavaScriptSurface(Buffer.from(await read(revision, entry.path, entry.bytes)).toString("utf8"), [
restrictedSDKSpecifier,
]);
+25 -2
View File
@@ -4,6 +4,7 @@ import { portableValueSchema } from "../bindings/portable.js";
import { ExecutionPlanError, hexDigest, type ExecutionPlan } from "./plan.js";
import type { ValueType } from "../capability-model/types.js";
import { compileQueryComposition } from "../query/composition.js";
import { queryTreeBlueprint } from "./query-trees.js";
const lowerTypes = (value: unknown): unknown =>
JSON.parse(
@@ -59,6 +60,12 @@ export function compileQueryProgram(plan: ExecutionPlan): unknown {
codec: "camino-query-program-v1",
plan: (compileKernelProgram(plan) as { plan: string }).plan,
...(compositions.length ? { compositions } : {}),
trees: plan.definition.queries
.filter((entry) => entry.query.checked.tree)
.map((entry) => ({
id: entry.id,
...queryTreeBlueprint(entry.query.packageRevisionId, entry.query.checked),
})),
queries: [
...compileInspectionTemplates(plan).queries,
...plan.definition.queries.map((entry) => queryProgramEntry(plan, entry)),
@@ -85,7 +92,10 @@ export function queryProgramEntry(plan: ExecutionPlan, entry: import("./plan.js"
placement: checked.declaration.placement?.kind ?? "remote",
source:
checked.declaration.placement?.kind === "local"
? JSON.stringify([query.packageRevisionId, checked.declaration.placement.sourceQueryId])
? JSON.stringify([
query.packageRevisionId,
checked.treeProjection?.tree ?? checked.declaration.placement.sourceQueryId,
])
: null,
root: checked.declaration.root,
atoms: query.rootAtoms,
@@ -105,6 +115,19 @@ export function queryProgramEntry(plan: ExecutionPlan, entry: import("./plan.js"
operation: JSON.stringify([field.atomId, field.interfaceRevisionId, field.getter]),
};
}),
admittedOperations,
admittedOperations: checked.tree
? [
...new Set([
...admittedOperations,
...plan.definition.queries
.filter(
(entry) =>
entry.query.packageRevisionId === query.packageRevisionId &&
entry.query.checked.treeProjection?.tree === checked.declaration.id,
)
.flatMap((entry) => entry.operations),
]),
].sort()
: admittedOperations,
};
}
+94
View File
@@ -0,0 +1,94 @@
import type { CheckedQuery, QueryTreeNode } from "../query/types.js";
import type { ValueType } from "../capability-model/types.js";
import { treeProjectionQuery } from "../query/tree.js";
import { tsType } from "../bindings/portable.js";
export function queryTreeBlueprint(packageId: string, query: CheckedQuery) {
const tree = query.tree!;
const projection = (id: string) =>
JSON.stringify([packageId, treeProjectionQuery(query.declaration.id, id, "local")]);
const admission = (id: string) =>
JSON.stringify([packageId, treeProjectionQuery(query.declaration.id, id, "replica")]);
return {
root: tree.root,
maxOccurrences: tree.maxOccurrences,
nodes: tree.nodes.map((node) => ({
...node,
projection: projection(node.projection),
admission: admission(node.projection),
bindings: node.bindings.map((binding) => ({
...binding,
projection: projection(binding.projection),
admission: admission(binding.projection),
})),
})),
};
}
/** Emit the shape of owned handles separately from graph result codecs. */
export function queryTreeType(query: CheckedQuery): string {
const tree = query.tree!;
const nodes = new Map(tree.nodes.map((node) => [node.id, node]));
const projections = new Map(tree.projections.map((projection) => [projection.id, projection]));
const rootOutput = (id: string) => {
const shape = projections.get(id)!.output;
if (shape.kind !== "record" || !shape.fields.root) throw Error("Unchecked Query Tree projection");
return shape.fields.root;
};
const shape = (id: string): string => {
const node = nodes.get(id)!;
return `QueryTreeShape<${tsType(rootOutput(node.projection))}, {${node.boundaries
.map(
(boundary) =>
`readonly ${JSON.stringify(boundary.name)}${boundary.conditions.length ? "?" : ""}: ${shape(boundary.node)}`,
)
.join(";")}}, {readonly root: {${node.bindings
.map(
(binding) =>
`readonly ${JSON.stringify(binding.name)}: ${bindingType(rootOutput(binding.projection), binding.targets, [binding.name])}`,
)
.join(";")}}}>`;
};
const bindingType = (
type: ValueType,
targets: QueryTreeNode["bindings"][number]["targets"],
prefix: string[],
): string => {
const at = (type: ValueType, targets: QueryTreeNode["bindings"][number]["targets"]): string => {
if (type.kind === "optional") return `(${at(type.value, targets)} | null)`;
if (type.kind === "list")
return `ReadonlyArray<${at(
type.value,
targets.map((target) => ({ ...target, path: target.path.slice(1) })),
)}>`;
const target = targets.find((target) => !target.path.length);
if (target) {
if (type.kind !== "record" || type.fields._camino?.kind !== "record" || !type.fields._camino.fields.ref)
throw Error("Unchecked binding identity");
return `QueryTreeOccurrence<${tsType(type.fields._camino.fields.ref)}, ${shape(target.node)}>`;
}
if (type.kind !== "record") return tsType(type);
return `{${Object.entries(type.fields)
.map(
([key, value]) =>
`readonly ${JSON.stringify(key)}: ${at(
value,
targets
.filter((target) => target.path[0] === key)
.map((target) => ({ ...target, path: target.path.slice(1) })),
)}`,
)
.join(";")}}`;
};
let selected = type;
for (const key of prefix) {
if (selected.kind !== "record" || !selected.fields[key]) throw Error("Unchecked binding field");
selected = selected.fields[key];
}
return at(
selected,
targets.map((target) => ({ ...target, path: target.path.slice(prefix.length) })),
);
};
return shape(tree.root);
}
+61 -4
View File
@@ -41,6 +41,7 @@ import { objectRow } from "./relational-schema.js";
import { workspaceQueryRootId } from "./root.js";
import { relationalCompiler } from "./relational-compile.js";
import { fragmentSources, type FragmentResolver } from "./fragments.js";
import { lowerQueryTree, stripQueryBoundaries } from "./tree.js";
import {
QueryCompileError,
type QueryDeclaration,
@@ -158,7 +159,7 @@ export async function compileQuery(
if (node.name.value.startsWith("__")) fail("QUERY_UNSUPPORTED_FEATURE", "Introspection is not supported", node);
},
Directive(node) {
if (!["include", "skip"].includes(node.name.value))
if (!["include", "skip", "boundary"].includes(node.name.value))
fail("QUERY_UNSUPPORTED_FEATURE", `Unsupported directive ${node.name.value}`, node);
},
});
@@ -421,6 +422,7 @@ export async function compileQuery(
for (const selection of set.selections) {
if (++expandedFields > 10000) fail("QUERY_WORK_LIMIT", "Expanded query exceeds 10000 fields", selection);
if (selection.kind === "FragmentSpread") {
if (selection.directives?.some((directive) => directive.name.value === "boundary")) continue;
if (fragmentContracts.get(selection.name.value)?.mode === "reference") continue;
add(
fragments.get(selection.name.value)!.selectionSet,
@@ -519,7 +521,7 @@ export async function compileQuery(
};
const conditions = (directives: readonly DirectiveNode[] = []) =>
directives
.filter((directive) => directive.name.value !== "live")
.filter((directive) => ["include", "skip"].includes(directive.name.value))
.map((directive) => ({
include: directive.name.value === "include",
value: argument(directive.arguments!.find((entry) => entry.name.value === "if")!.value),
@@ -540,6 +542,52 @@ export async function compileQuery(
const contract = fragmentContracts.get(node.name.value);
if (contract && generated.byName.get(parent.name)?.revisionId !== contract.root)
fail("FRAGMENT_ROOT_MISMATCH", "Imported fragment requires its exact nominal root interface", node);
const directive = node.directives?.find((directive) => directive.name.value === "boundary");
if (directive) {
const name = directive.arguments?.find((argument) => argument.name.value === "name")?.value;
if (name?.kind !== "StringValue" || !/^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(name.value))
return fail(
"QUERY_BOUNDARY_NAME",
"A boundary needs a literal identifier of at most 64 characters",
directive,
);
const root = generated.byName.get(parent.name)?.revisionId;
if (!root || root === workspaceQueryRootId)
return fail("QUERY_BOUNDARY_ROOT", "A boundary must select a nominal Graph Object", node);
if (contract?.mode === "reference")
fail("QUERY_BOUNDARY_REFERENCE", "Use an ordinary checked Fragment spread for a boundary", node);
const fragment = fragments.get(node.name.value)!;
const token = node.loc?.startToken;
return [
{
name: "$boundary",
key: "",
arguments: {},
conditions: inheritedConditions,
boundary: {
name: name.value,
root,
output: required(parent, fragment.selectionSet),
...(token ? { location: { file: node.loc!.source.name, line: token.line, column: token.column } } : {}),
},
selection: [
...(contract
? [
{
name: "$fragment",
key: "",
selection: [],
arguments: {},
conditions: [],
fragmentSpread: contract,
},
]
: []),
...selection(fragment.selectionSet, parent),
],
},
];
}
return [
...(contract
? [
@@ -598,6 +646,13 @@ export async function compileQuery(
];
});
const contract = generated.byName.get(parent.name);
const names = new Set<string>();
for (const field of selections)
if (field.boundary) {
if (names.has(field.boundary.name))
fail("QUERY_BOUNDARY_DUPLICATE", `Duplicate boundary ${field.boundary.name}`);
names.add(field.boundary.name);
}
if (!contract || contract.revisionId === workspaceQueryRootId) return selections;
// Fragment expansion may repeat metadata. Combine its children so a later
// implicit reference cannot overwrite an explicitly selected aggregate.
@@ -632,10 +687,11 @@ export async function compileQuery(
const normalized = print(document),
schema = printSchema(generated.schema);
const selections = selection(operations[0]!.selectionSet, generated.schema.getQueryType()!);
const tree = lowerQueryTree(declaration, selections, result);
const definitionDigest = createHash("sha256")
.update(
canonicalJson({
semantics: 6,
semantics: tree ? 7 : 6,
declaration,
normalized,
fragmentContracts: Object.fromEntries(fragmentContracts),
@@ -667,7 +723,8 @@ export async function compileQuery(
variables: { kind: "record", fields: variables },
output: result,
effects: [...effects.values()],
selection: selections,
selection: tree ? stripQueryBoundaries(selections) : selections,
...(tree ? { tree } : {}),
variableDefaults: Object.fromEntries(
(operations[0]!.variableDefinitions ?? [])
.filter((entry) => entry.defaultValue)
+10 -1
View File
@@ -11,6 +11,8 @@ import {
GraphQLList,
GraphQLNonNull,
GraphQLSchema,
GraphQLDirective,
DirectiveLocation,
specifiedDirectives,
visit,
type GraphQLOutputType,
@@ -294,7 +296,14 @@ export function querySchema(
return result;
};
const schema = new GraphQLSchema({
directives: specifiedDirectives,
directives: [
...specifiedDirectives,
new GraphQLDirective({
name: "boundary",
locations: [DirectiveLocation.FRAGMENT_SPREAD],
args: { name: { type: new GraphQLNonNull(GraphQLString) } },
}),
],
// Nominal projections can mention an interface not reached by a relation.
types: (() => {
const requested = new Set<string>();
+260
View File
@@ -0,0 +1,260 @@
import { valueType, type InterfaceRevisionId, type ValueType } from "../capability-model/types.js";
import {
QueryCompileError,
type QueryDeclaration,
type QuerySelection,
type QueryTreePlan,
type QueryTreeNode,
} from "./types.js";
import type { CheckedQuery } from "./types.js";
import { createHash } from "node:crypto";
import { canonicalJson } from "../capability-model/evolution.js";
export const stripQueryBoundaries = (fields: readonly QuerySelection[]): QuerySelection[] =>
fields
.filter((field) => !field.boundary)
.map((field) => ({ ...field, selection: stripQueryBoundaries(field.selection) }));
const containsBoundary = (fields: readonly QuerySelection[]): boolean =>
fields.some((field) => !!field.boundary || containsBoundary(field.selection));
/** GraphQL repeated paths form one projection. Equivalent checked selections
* reuse their binding; differing windows/guards never silently share it. */
function mergeTreeFields(fields: readonly QuerySelection[]): QuerySelection[] {
const result: QuerySelection[] = [];
for (const input of fields) {
const field = { ...input, selection: mergeTreeFields(input.selection) };
const previous = result.find(
(candidate) =>
!candidate.boundary &&
!field.boundary &&
!candidate.fragmentSpread &&
!field.fragmentSpread &&
candidate.key === field.key,
);
if (!previous) {
result.push(field);
continue;
}
const { selection: _left, ...left } = previous;
const { selection: _right, ...right } = field;
if (canonicalJson(left) !== canonicalJson(right)) {
if (containsBoundary(previous.selection) || containsBoundary(field.selection))
throw new QueryCompileError(
"QUERY_TREE_BINDING_CONFLICT",
`Alias differently guarded or parameterized paths selected as ${field.key}`,
);
result.push(field);
continue;
}
previous.selection = mergeTreeFields([...previous.selection, ...field.selection]);
}
const names = new Set<string>();
for (const field of result)
if (field.boundary) {
if (names.has(field.boundary.name))
throw new QueryCompileError("QUERY_BOUNDARY_DUPLICATE", `Duplicate boundary ${field.boundary.name}`);
names.add(field.boundary.name);
}
return result;
}
const required = (type: ValueType): ValueType =>
type.kind === "optional" || type.kind === "list" ? required(type.value) : type;
const fieldType = (type: ValueType, key: string): ValueType => {
const shape = required(type);
if (shape.kind !== "record" || !shape.fields[key])
throw new QueryCompileError("QUERY_TREE_SHAPE", `Missing checked field ${key}`);
return shape.fields[key];
};
const objectInterface = (fields: readonly QuerySelection[]): InterfaceRevisionId | undefined =>
fields
.find((field) => field.name === "_camino" && field.key === "_camino")
?.selection.find((field) => field.name === "ref")?.targetInterfaceRevisionId;
const identity = (fields: readonly QuerySelection[]): QuerySelection[] =>
fields
.filter((field) => field.name === "_camino" && field.key === "_camino")
.map((field) => ({ ...field, conditions: [], selection: field.selection.filter((child) => child.name === "ref") }));
function projectionType(type: ValueType, fields: readonly QuerySelection[]): ValueType {
if (type.kind === "optional") return valueType.optional(projectionType(type.value, fields));
if (type.kind === "list") return valueType.list(projectionType(type.value, fields));
if (type.kind !== "record") return type;
return {
kind: "record",
fields: Object.fromEntries(
fields
.filter((field) => !field.fragmentSpread)
.map((field) => {
const source = type.fields[field.key] ?? (field.key === "key" ? valueType.string : undefined);
if (!source) throw new QueryCompileError("QUERY_TREE_SHAPE", `Missing checked projection ${field.key}`);
return [field.key, projectionType(source, field.selection)];
}),
),
};
}
const keyField = (): QuerySelection => ({ name: "key", key: "key", conditions: [], arguments: {}, selection: [] });
/** Lower into complete projections and minimal binding projections. No runtime
* component inspection, executable client source, or graph handle value is involved.
* Each projection is rooted at its own nominal subject; lists retain entry keys.
*/
export function lowerQueryTree(
declaration: QueryDeclaration,
selection: QuerySelection[],
output: ValueType,
): QueryTreePlan | undefined {
if (!containsBoundary(selection)) return;
selection = mergeTreeFields(selection);
if (declaration.placement?.kind !== "replica" || declaration.fragment)
throw new QueryCompileError(
"QUERY_TREE_PLACEMENT",
"Declare a Query Tree as a replica query; its checked projections supply local views",
);
const root = selection.find((field) => field.name === "root" && field.key === "root");
if (!root || selection.some((field) => field !== root))
throw new QueryCompileError("QUERY_TREE_ROOT", "A Query Tree requires one unaliased root selection");
const plan: QueryTreePlan = { root: "root", nodes: [], projections: [], maxOccurrences: 0 };
const project = (id: string, subject: InterfaceRevisionId, fields: QuerySelection[], shape: ValueType) => {
if (plan.projections.length >= 256)
throw new QueryCompileError("QUERY_TREE_LIMIT", "Query Tree exceeds 256 projections");
plan.projections.push({
id,
root: id === "root/value" ? declaration.root : { kind: "object", interface: subject },
selection: [{ name: "root", key: "root", arguments: {}, conditions: [], selection: fields }],
output: { kind: "record", fields: { root: shape } },
});
return id;
};
const node = (
id: string,
subject: InterfaceRevisionId,
fields: QuerySelection[],
shape: ValueType,
occurrences: number,
): string => {
plan.maxOccurrences += occurrences;
if (!Number.isSafeInteger(plan.maxOccurrences) || plan.maxOccurrences > 256)
throw new QueryCompileError("QUERY_TREE_LIMIT", "Query Tree exceeds 256 bounded node occurrences");
const current: QueryTreeNode = {
id,
projection: project(id + "/value", subject, stripQueryBoundaries(fields), shape),
boundaries: [],
bindings: [],
};
plan.nodes.push(current);
for (const field of fields)
if (field.boundary) {
const child = field.boundary;
current.boundaries.push({
name: child.name,
conditions: field.conditions,
node: node(id + "/boundary/" + child.name, child.root, field.selection, child.output, occurrences),
});
}
for (const field of fields) {
if (field.boundary || !containsBoundary(field.selection)) continue;
const targets: { path: string[]; node: string }[] = [];
const prune = (fields: QuerySelection[], shape: ValueType, path: string[], bound: number): QuerySelection[] => {
if (shape.kind === "optional") return prune(fields, shape.value, path, bound);
if (shape.kind === "list") return prune(fields, shape.value, [...path, "[]"], bound * declaration.budgets.rows);
const target = objectInterface(fields);
if (target) {
targets.push({
path,
node: node(id + "/binding/" + path.map(encodeURIComponent).join("/"), target, fields, shape, bound),
});
return identity(fields);
}
const minimal = fields
.filter((field) => containsBoundary(field.selection))
.map((field) => ({
...field,
selection: prune(field.selection, fieldType(shape, field.key), [...path, field.key], bound),
}));
// Relationship occurrence identity is its entry key, including duplicate
// target Objects. Never substitute list position or Object identity.
if (minimal.some((field) => field.name === "node") && !minimal.some((field) => field.name === "key"))
minimal.unshift(fields.find((field) => field.name === "key" && field.key === "key") ?? keyField());
return minimal;
};
const type = fieldType(shape, field.key);
const minimal = { ...field, selection: prune(field.selection, type, [field.key], occurrences) };
const selected = [...identity(fields), minimal];
current.bindings.push({
name: field.key,
targets,
projection: project(
id + "/bindings/" + encodeURIComponent(field.key),
subject,
selected,
projectionType(shape, selected),
),
});
}
return id;
};
node("root", declaration.root.interface, root.selection, fieldType(output, "root"), 1);
return plan;
}
export const treeProjectionQuery = (tree: string, projection: string, role: "replica" | "local") =>
`${tree}/@tree/${projection}/${role}`;
/** Checked synthetic queries remain ordinary nominal queries for linking,
* authority, native input acquisition and execution. Public trees own the plan;
* applications do not copy these projections into their source declarations. */
export function queryTreeProjections(query: CheckedQuery): CheckedQuery[] {
if (!query.tree) return [];
const { tree: _tree, ...base } = query;
return query.tree.projections.flatMap((projection) => {
const selected = new Set<string>();
const walk = (fields: readonly QuerySelection[]) => {
for (const field of fields) {
if (field.interfaceRevisionId && field.memberId)
selected.add(JSON.stringify([field.interfaceRevisionId, field.memberId]));
walk(field.selection);
}
};
walk(projection.selection);
const effects = query.effects.filter(
(effect) =>
selected.has(JSON.stringify([effect.interfaceRevisionId, effect.memberId])) ||
effect.uses.some((use) => use !== "select"),
);
if (effects.some((effect) => effect.execution === "rpc-permitted"))
throw new QueryCompileError(
"QUERY_TREE_LOCAL_DEPENDENCY",
"A replica Query Tree requires locally executable snapshot projections",
);
return (["replica", "local"] as const).map((role): CheckedQuery => {
const id = treeProjectionQuery(query.declaration.id, projection.id, role);
const declaration: QueryDeclaration = {
...query.declaration,
id,
displayName: id,
root: projection.root,
placement:
role === "replica"
? { kind: "replica" }
: { kind: "local", sourceQueryId: treeProjectionQuery(query.declaration.id, projection.id, "replica") },
};
return {
...base,
declaration,
effects,
selection: projection.selection,
output: projection.output,
treeProjection: { tree: query.declaration.id, projection: projection.id, role },
definitionDigest: createHash("sha256")
.update(
canonicalJson({
parent: query.definitionDigest,
declaration,
selection: projection.selection,
output: projection.output,
}),
)
.digest("hex"),
};
});
});
}
+33
View File
@@ -94,6 +94,9 @@ export class QueryCompileError extends Error {
}
}
export interface CheckedQuery {
/** Checked decomposition; handles belong to a Client Session, never graph values. */
tree?: QueryTreePlan;
treeProjection?: { tree: string; projection: string; role: "replica" | "local" };
fragmentContracts?: Record<string, NonNullable<QuerySelection["fragmentSpread"]>>;
declaration: QueryDeclaration;
definitionDigest: string;
@@ -115,6 +118,12 @@ export type QueryArgument =
| { kind: "list"; values: QueryArgument[] }
| { kind: "object"; fields: Record<string, QueryArgument> };
export interface QuerySelection {
boundary?: {
name: string;
root: InterfaceRevisionId;
output: ValueType;
location?: QuerySourceLocation;
};
/** A checked occurrence marker, not an output field or storage capability. */
fragmentSpread?: {
mode?: "reference";
@@ -135,3 +144,27 @@ export interface QuerySelection {
relational?: import("./relational.js").QueryRelationalPlan;
predicate?: import("./relational.js").QueryPredicate;
}
export interface QueryTreeProjection {
id: string;
root: QueryRoot;
selection: QuerySelection[];
output: ValueType;
}
export interface QueryTreeNode {
id: string;
projection: string;
boundaries: { name: string; conditions: QuerySelection["conditions"]; node: string }[];
bindings: {
name: string;
projection: string;
/** [] denotes an occurrence in a bounded list; its entry key owns identity. */
targets: { path: string[]; node: string }[];
}[];
}
export interface QueryTreePlan {
root: string;
nodes: QueryTreeNode[];
projections: QueryTreeProjection[];
maxOccurrences: number;
}
+27 -2
View File
@@ -20,6 +20,9 @@ export interface ReactModulePreparation {
/** Public input type before workspace query lowering adds private projections. */
input?: ValueType;
children: ReactChildDeclaration[];
/** Binds roots/arguments only. The ordinary checked Query Tree owns every
* acquisition path; the React declaration does not repeat its descendants. */
trees?: Pick<QueryCompositionStep, "name" | "query" | "root" | "arguments">[];
}
export interface ReactPreparationArtifact {
/** Frontend queries belong to installed preparation, not the authored client API. */
@@ -51,7 +54,7 @@ export function presentationDeclarations(
};
for (const [name, value] of Object.entries(modules)) {
const declaration = object(value, `module ${name}`);
if (Object.keys(declaration).some((key) => !["inputs", "children"].includes(key)))
if (Object.keys(declaration).some((key) => !["inputs", "children", "trees"].includes(key)))
throw Error(`${name}: unknown preparation field`);
const module = pkg.modules?.find((entry) => entry.displayName === name);
if (!module) throw Error(`React declaration: unknown module ${name}`);
@@ -129,7 +132,29 @@ export function presentationDeclarations(
...(role ? { role } : {}),
};
});
artifact.modules.push({ module: module.id, composition, children: parsed });
const trees = declaration.trees ?? [];
if (!Array.isArray(trees) || trees.length > 16) throw Error(`${name}: trees must be a bounded array`);
const treeNames = new Set<string>();
const preparedTrees = trees.map((value) => {
const tree = object(value, `${name} tree`) as unknown as NonNullable<ReactModulePreparation["trees"]>[number];
if (
Object.keys(tree).some((key) => !["name", "query", "root", "arguments"].includes(key)) ||
!/^[A-Za-z_][A-Za-z0-9_]*$/.test(tree.name) ||
treeNames.has(tree.name)
)
throw Error(`${name}: tree requires a unique name and checked root/arguments`);
treeNames.add(tree.name);
const query = pkg.queries?.find((entry) => entry.displayName === tree.query);
if (!query || query.placement?.kind !== "replica")
throw Error(`${name}.${tree.name}: requires a replica Query Tree`);
return { ...tree, query: query.id };
});
artifact.modules.push({
module: module.id,
composition,
children: parsed,
...(preparedTrees.length ? { trees: preparedTrees } : {}),
});
}
for (const module of pkg.modules ?? []) {
const contract = interfaces.find((entry) => entry.revisionId === module.interfaceRevisionId);
+12 -1
View File
@@ -1,7 +1,7 @@
import type { WorkspaceRevision, ValueType } from "../capability-model/types.js";
import { moduleContract } from "../capability-model/modules.js";
import { canonicalJson } from "../capability-model/evolution.js";
import { queryBindingType } from "../query/composition.js";
import { queryBindingType, compileQueryComposition } from "../query/composition.js";
import { moduleTypeContext } from "../execution-world/module-contracts.js";
import { preparationArtifact, type ReactPreparationArtifact } from "./declarations.js";
import { expandPresentationQueries } from "./expansion.js";
@@ -25,6 +25,17 @@ export async function completeWorkspace(workspace: WorkspaceRevision): Promise<v
if (!composition || composition.output.kind !== "record")
throw Error(`Missing checked inputs for ${parent.module.displayName}`);
parent.preparation.input = structuredClone(composition.output);
const treeQueries = new Map((parent.pkg.checkedQueries ?? []).map((query) => [query.declaration.id, query]));
for (const tree of parent.preparation.trees ?? []) {
if (!treeQueries.get(tree.query)?.tree)
throw Error(`${parent.module.displayName}.${tree.name}: expected a checked Query Tree`);
// Reuse Camino's ordinary nominal/argument binder. Only input parameters
// are available; a tree cannot wait on a whole presentation result.
compileQueryComposition(
{ id: `react:tree:${parent.module.id}:${tree.name}`, input: composition.input, steps: [tree], maxQueries: 1 },
treeQueries,
);
}
const outputs = new Map(Object.entries(composition.output.fields));
const dependencies: string[] = [];
const children = parent.preparation.children.map((child) => {
+3
View File
@@ -118,6 +118,9 @@ export const loadQuixosLock = async (fileName: string): Promise<QuixosLockParseR
const absoluteRoot = path.resolve(fileName);
const repositoryRoot = path.dirname(absoluteRoot);
const rootName = path.basename(absoluteRoot);
const metadata = await lstat(absoluteRoot);
if (!metadata.isFile() || metadata.isSymbolicLink())
throw new Error("root locks must be ordinary files, not symbolic links");
const rootSource = await readFile(absoluteRoot, "utf8");
return await resolveQuixosLock(
rootSource,
+56
View File
@@ -4,6 +4,62 @@ import { mkdtemp, writeFile, symlink, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { CandidateMetadataBudget, readCandidateMetadata } from "../src/execution-world/metadata.js";
import { loadCheckedInstallationCandidate } from "../src/execution-world/candidate.js";
test(
"a real checked application can stage its replacement within the default metadata budget",
{
skip: !process.env.QX_CHECKED_CANDIDATE,
},
async () => {
const root = process.env.QX_CHECKED_CANDIDATE!;
const budget = new CandidateMetadataBudget();
const first = await loadCheckedInstallationCandidate(root, {}, { budget, owner: "reader" });
try {
const retained = budget.snapshot().bytes;
const second = await loadCheckedInstallationCandidate(root, {}, { budget, owner: "reader" });
try {
assert.equal(budget.snapshot().bytes, retained * 2);
assert.deepEqual(second.reference, first.reference, "Compaction preserves exact checked identity");
assert.equal(second.graphExecution.kernel, second.kernel, "Graph representation shares the validated program");
assert.equal(second.graphExecution.queries, second.queries);
// A deliberately tighter pool admits the serving installation but must
// reject replacement parsing without leaking a candidate or tree lease.
const tight = new CandidateMetadataBudget({
bytes: retained + 1,
ownerBytes: retained + 1,
candidates: 2,
});
const blocker = tight.acquire("reader");
blocker.charge(retained);
try {
await assert.rejects(
loadCheckedInstallationCandidate(root, {}, { budget: tight, owner: "reader" }),
/CANDIDATE_METADATA_LIMIT/,
);
assert.equal(tight.snapshot().bytes, retained);
assert.equal(tight.snapshot().candidates, 1);
} finally {
blocker.release();
}
const other = budget.acquire("other");
try {
assert.throws(() => other.charge(budget.limits.bytes), /CANDIDATE_METADATA_LIMIT/);
assert.equal(budget.snapshot().bytes, retained * 2, "Rejected admission must not leak charges");
} finally {
other.release();
}
} finally {
second.metadata.release();
}
assert.equal(budget.snapshot().bytes, retained, "The serving candidate survives failed or completed staging");
} finally {
first.metadata.release();
}
assert.equal(budget.snapshot().bytes, 0);
assert.equal(budget.snapshot().candidates, 0);
},
);
test("concurrent candidate reservations enforce aggregate and owner limits and release exactly once", () => {
const budget = new CandidateMetadataBudget({ bytes: 1000, ownerBytes: 600, candidates: 3 });
+22
View File
@@ -90,6 +90,28 @@ package Runtime id "package:runtime" revision "package:runtime@1" {
);
assert.equal(result.directResources.size, 1);
const resolveResource = async (source: { repository: string; commit: string }, kind: string) => ({
directory: directories.get(`${kind}\0${source.repository}\0${source.commit}`)!,
});
const bound = await Promise.all(
["workspace:first", "workspace:second"].map((workspaceId) =>
compileWorkspaceRepository({
rootDirectory: root,
resolveResource,
workspaceId,
sourceRootCommit: "5".repeat(40),
}),
),
);
assert.notEqual(bound[0]!.workspace.workspaceId, bound[1]!.workspace.workspaceId);
assert.notEqual(bound[0]!.workspace.id, bound[1]!.workspace.id);
for (const instance of bound) {
assert.equal(instance.workspace.sourceRootCommit, "5".repeat(40));
assert.deepEqual(instance.workspace.packageImports, result.workspace.packageImports);
assert.deepEqual(instance.workspace.interfaceImports, result.workspace.interfaceImports);
assert.deepEqual(instance.resources, result.resources, "binding does not fork resource identities");
}
const resource = await compileCapabilityResourceRepository({
rootDirectory: runtime,
kind: "package",
+8
View File
@@ -179,3 +179,11 @@ await writeFile(
"dist/portable-reader/variants/entries.ts",
"export type Entries = " + tsType(entries.valueType) + ";\n",
);
const { queryTreeFixture } = await import("./query-tree-fixture.js");
const { generatePackageQueryClientBindings } = await import("../src/execution-world/client-contracts.js");
const treeFixture = await queryTreeFixture();
await mkdir("dist/portable-reader/query-tree", { recursive: true });
await writeFile("dist/portable-reader/query-tree/queries.ts", generatePackageQueryClientBindings(treeFixture.pkg));
await writeFile("dist/portable-reader/query-tree/queries.json", JSON.stringify(compileQueryProgram(treeFixture.plan)));
await writeFile("dist/portable-reader/query-tree/kernel.json", JSON.stringify(compileKernelProgram(treeFixture.plan)));
+10 -2
View File
@@ -8,10 +8,18 @@ export const queryFixtureSource = {
repository: "https://query-fixture.example.test/source.git",
commit: "1".repeat(40),
};
export async function queryWorkspaceFixture(options: { live?: boolean } = {}) {
export async function queryWorkspaceFixture(options: { live?: boolean; snapshot?: boolean } = {}) {
const interfaces = new Map<string, InterfaceRevision>();
const sources: Record<string, string> = {};
function iface(name: string, members: string, parameters = "") {
if (options.snapshot) members = members.replaceAll(" ordered", "");
if (options.snapshot)
for (const member of ["name", "title", "done", "rank", "assignee", "items"])
for (const operation of ["get", "resolve"])
members = members.replace(
`${operation} id "${member}:${operation}";`,
`${operation} id "${member}:${operation}" execution snapshot;`,
);
const text = `${[...interfaces.keys()].map((name) => `import interface ${name};`).join("\n")}
interface ${name}${parameters} id "${name}" revision "${name}@1" {${members}}`;
sources[name] = text;
@@ -117,7 +125,7 @@ export async function queryWorkspaceFixture(options: { live?: boolean } = {}) {
${implementation("Task")} ${implementation("Reminder")}
conform Tasks as Collection<interface TaskFacts> id "tasks-collection" {
private edge Items id "items" {
atom Tasks projection items id "items:forward" many ordered;
atom Tasks projection items id "items:forward" many${options.snapshot ? "" : " ordered"};
interface TaskFacts projection collections id "items:inverse" many;
}
bind items.resolve to edge Items.items.resolve;
+54
View File
@@ -6,10 +6,64 @@ import {
compileExecutionPlan,
hexDigest,
inspectJavaScriptSurface,
inspectRestrictedImplementation,
restrictedSDKIdentity,
restrictedSDKPath,
type ArtifactImplementation,
reportExecutionPlacement,
type ExecutionInstallation,
} from "../src/execution-world/index.js";
test("restricted SDK identity ignores build comments and positions, preserving executable syntax", () => {
const source = 'export function value() { return ("hello"); }';
const x86 = "// /nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-core/dist/portable.js\n" + source;
const arm = "// /nix/store/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-core/dist/portable.js\n\n" + source;
assert.equal(restrictedSDKIdentity(x86), restrictedSDKIdentity(arm));
for (const changed of [
'export function value() { return ("goodbye"); }',
'export function value() { return\n("hello"); }',
'export function value() { fetch("hello"); }',
'import x from "node:fs"; ' + source,
'export function value() { return "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-core"; }',
])
assert.notEqual(restrictedSDKIdentity(source), restrictedSDKIdentity(changed));
assert.notEqual(restrictedSDKIdentity('"use strict";'), restrictedSDKIdentity('"use\\x20strict";'));
assert.notEqual(restrictedSDKIdentity('const value="// comment";'), restrictedSDKIdentity('const value="";'));
assert.throws(() => restrictedSDKIdentity("const ???"));
assert.throws(() => restrictedSDKIdentity(" ".repeat(2 * 1024 * 1024 + 1)), /ANALYSIS_LIMIT/);
});
test("restricted admission accepts the same SDK syntax and rejects changed SDK behavior", async () => {
const source = "export const registry = {};";
const sdk = 'export const value = "trusted";';
const implementation: ArtifactImplementation = {
id: "restricted",
target: "server",
compatibility: "quixos-restricted-module-v1",
path: "module.mjs",
ports: "quixos-transaction-ports-v1",
registryExport: "registry",
exports: ["value"],
contentDigest: "a".repeat(64),
files: [
{ path: "module.mjs", digest: "b".repeat(64), bytes: source.length, mediaType: "text/javascript" },
{ path: restrictedSDKPath, digest: "c".repeat(64), bytes: sdk.length, mediaType: "text/javascript" },
],
};
const check = (candidateSDK: string) => {
implementation.files[1]!.bytes = Buffer.byteLength(candidateSDK);
return inspectRestrictedImplementation(
"package:test@1",
implementation,
async (_revision, path) => Buffer.from(path === restrictedSDKPath ? candidateSDK : source),
restrictedSDKIdentity(sdk),
);
};
assert.deepEqual(await check("// a different build path\n" + sdk), []);
assert.equal((await check('export const value = "untrusted";'))[0]?.code, "RESTRICTED_SDK_UNSUPPORTED");
assert.equal((await check("export const ???"))[0]?.code, "RESTRICTED_SDK_UNSUPPORTED");
});
test("compiled-source inspection finds authority imports and ambient facilities without executing code", () => {
const surface = inspectJavaScriptSurface(
`
+54
View File
@@ -19,12 +19,16 @@ import {
expectedArtifactReceipt,
checkExecutionInstallation,
clientContractCatalog,
createClientContractCatalog,
CandidateMetadataBudget,
compileKernelProgram,
type ComponentArtifact,
type PackageArtifacts,
} from "../src/execution-world/index.js";
import { validateWorkspaceRevision } from "../src/capability-model/index.js";
import { presentationDeclarations, preparationArtifact } from "../src/react-build/declarations.js";
import { compileQueryComposition } from "../src/query/composition.js";
import type { InspectionKernel } from "../src/execution-world/inspection.js";
const source = { repository: "https://example.test/components.git", commit: "a".repeat(40) };
const contract = (name: string) => {
@@ -255,6 +259,56 @@ test("checked component-only artifacts install without a service and reject miss
() => clientContractCatalog(plan, { ...installation, plan: "bad" }, [pkg.revision]),
/CATALOG_PLAN_MISMATCH/,
);
const budget = new CandidateMetadataBudget();
const scope = budget.acquire("catalog-test");
const prepared = createClientContractCatalog(
plan,
installation,
scope,
compileKernelProgram(plan) as InspectionKernel,
);
try {
assert.throws(() => prepared.select(["absent"]), /UNINSTALLED_PACKAGE/);
assert.equal(scope.bytes, 0, "Unknown requests do not allocate compiler metadata");
assert.deepEqual(prepared.select([pkg.revision]), catalog);
const retained = scope.bytes;
assert.ok(retained > 0);
assert.ok(retained < 32 * 1024 * 1024, "Small catalogs retain a proportional metadata charge");
const full = prepared.select([pkg.revision]);
const empty = prepared.select([]);
assert.deepEqual(empty.components, []);
assert.deepEqual(empty.presentations, []);
assert.deepEqual(empty.moduleBindings, []);
assert.equal(empty.operations, full.operations, "Shared compiled metadata is reused");
assert.equal(Object.isFrozen(full.operations), true);
// Any plan access now would reveal a repeated digest/compile on the warm path.
const definition = plan.definition;
Object.defineProperty(plan, "definition", {
configurable: true,
get() {
throw Error("RECOMPILED_PLAN");
},
});
try {
assert.deepEqual(prepared.select([pkg.revision]), full);
assert.equal(scope.bytes, retained, "Package projections do not accumulate cache entries");
} finally {
Object.defineProperty(plan, "definition", { configurable: true, value: definition });
}
prepared.close();
assert.equal(scope.bytes, 0);
assert.throws(() => prepared.select([pkg.revision]), /CANDIDATE_METADATA_CLOSED/);
} finally {
prepared.close();
scope.release();
}
const tight = new CandidateMetadataBudget({ bytes: 1000, ownerBytes: 1000, candidates: 1 });
const tightScope = tight.acquire("catalog-test");
const refused = createClientContractCatalog(plan, installation, tightScope);
assert.throws(() => refused.select([pkg.revision]), /CANDIDATE_METADATA_LIMIT/);
assert.equal(tightScope.bytes, 0);
refused.close();
tightScope.release();
} finally {
await rm(root, { recursive: true, force: true });
}
+37
View File
@@ -0,0 +1,37 @@
import { queryWorkspaceFixture } from "./fixtures/query-workspace.js";
import { compileQuery } from "../src/query/compile.js";
import { queryTreeProjections } from "../src/query/tree.js";
import { linkQueries } from "../src/query/link.js";
import { compileExecutionPlan } from "../src/execution-world/plan.js";
export async function queryTreeFixture() {
const fixture = await queryWorkspaceFixture({ live: true, snapshot: true });
const declaration = {
...fixture.pkg.queries!.find((query) => query.id === "upcoming")!,
id: "tree",
displayName: "Tree",
operation: "Tree",
placement: { kind: "replica" as const },
fragments: [],
};
const checked = await compileQuery(
declaration,
fixture.interfaces,
async () => `query Tree($first:Int!, $details:Boolean!) {
root {items(first:$first) {entries {node {
...Heading @boundary(name:"basic") ...Details @boundary(name:"details") @include(if:$details)
assignee {...Person @boundary(name:"person")}
}}}}
} fragment Heading on TaskFacts {title} fragment Details on TaskFacts {rank done}
fragment Person on PersonFacts {name}`,
);
const projections = queryTreeProjections(checked);
fixture.pkg.queries = [declaration, ...projections.map((query) => query.declaration)];
fixture.pkg.checkedQueries = [checked, ...projections];
// Workspace imports retain the checked Package by value in the assembler.
const imported = fixture.workspace.packageImports.find((pkg) => pkg.revisionId === fixture.pkg.revisionId)!;
imported.queries = fixture.pkg.queries;
imported.checkedQueries = fixture.pkg.checkedQueries;
fixture.workspace.linkedQueries = linkQueries(fixture.workspace);
return { ...fixture, plan: compileExecutionPlan(fixture.workspace) };
}
+165
View File
@@ -0,0 +1,165 @@
import assert from "node:assert/strict";
import test from "node:test";
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
import { compileQuery } from "../src/query/compile.js";
import type { InterfaceRevision } from "../src/capability-model/types.js";
const source = { repository: "https://example.test/tree", commit: "a".repeat(40) };
function iface(text: string, dependencies: InterfaceRevision[] = []) {
const result = compileCapabilityResourceSource(text, {
source,
environment: {
interfaces: new Map(dependencies.map((value) => [value.displayName, value])),
interfaceClosure: dependencies,
},
});
assert.ok(result.ok, JSON.stringify(result.diagnostics));
if (result.resource.kind !== "interface") throw Error("Expected Interface");
return result.resource.revision;
}
const item = iface(`interface Item id "item" revision "item@1" {
queryable value title id "title" : string {get id "title:get" execution snapshot;}
queryable value body id "body" : string {get id "body:get" execution snapshot;}
}`);
const navigation = iface(
`import interface Item; interface Navigation id "nav" revision "nav@1" {
queryable value chrome id "chrome" : string {get id "chrome:get" execution snapshot;}
queryable relation document id "document" : optional-one interface Item {resolve id "document:resolve" execution snapshot;}
queryable relation items id "items" : many interface Item ordered {resolve id "items:resolve" execution snapshot;}
}`,
[item],
);
function declaration(root: InterfaceRevision = item) {
const result = compileCapabilityResourceSource(
`import interface ${root.displayName}; package Views id "views" revision "views@1" {
query Open id "open" root ${root.displayName} document "open.graphql" operation "Open" {replica; max rows 10;}
}`,
{ source, environment: { interfaces: new Map([[root.displayName, root]]), interfaceClosure: [root, item] } },
);
assert.ok(result.ok, JSON.stringify(result.diagnostics));
if (result.resource.kind !== "package") throw Error("Expected Package");
return result.resource.revision.queries![0]!;
}
const fragments = `fragment Heading on Item {title} fragment Body on Item {body}`;
test("boundaries remain complete typed projections; ordinary spreads stay inline", async () => {
const checked = await compileQuery(
declaration(),
[item],
async () => `query Open {root {...Heading ...Body @boundary(name:"body")}} ${fragments}`,
);
assert(checked.tree);
assert(!JSON.stringify(checked.output).includes('"body"'));
assert(!JSON.stringify(checked.selection).includes('"body"'));
const body = checked.tree.nodes.find((node) => node.id === "root/boundary/body")!;
assert(body);
assert(
JSON.stringify(checked.tree.projections.find((projection) => projection.id === body.projection)!.output).includes(
'"body"',
),
);
assert.equal(checked.tree.nodes[0]!.boundaries[0]!.name, "body");
const ordinary = await compileQuery(
declaration(),
[item],
async () => `query Open {root {...Heading ...Body}} ${fragments}`,
);
assert.equal(ordinary.tree, undefined);
assert(JSON.stringify(ordinary.output).includes('"body"'));
});
test("minimal nullable-root bindings exclude slow siblings and carry child handles", async () => {
const checked = await compileQuery(
declaration(navigation),
[item, navigation],
async () => `query Open {root {
...Chrome @boundary(name:"chrome") document {...Heading @boundary(name:"basic") ...Body @boundary(name:"body")}
}} fragment Chrome on Navigation {chrome} ${fragments}`,
);
const tree = checked.tree!;
const binding = tree.nodes[0]!.bindings[0]!;
assert.equal(binding.name, "document");
const projection = tree.projections.find((projection) => projection.id === binding.projection)!;
assert(!JSON.stringify(projection.selection).includes('"chrome"'));
assert(!JSON.stringify(projection.selection).includes('"title"'));
assert(!JSON.stringify(projection.selection).includes('"body"'));
assert.deepEqual(binding.targets[0]!.path, ["document"]);
assert.deepEqual(
tree.nodes.find((node) => node.id === binding.targets[0]!.node)!.boundaries.map((boundary) => boundary.name),
["basic", "body"],
);
});
test("bounded list bindings preserve entry identity without requesting details", async () => {
const checked = await compileQuery(
declaration(navigation),
[item, navigation],
async () => `query Open {root {
items(first: 3) {entries {node {...Body @boundary(name:"body")}}}
}} fragment Body on Item {body}`,
);
const tree = checked.tree!,
binding = tree.nodes[0]!.bindings[0]!;
assert.deepEqual(binding.targets[0]!.path, ["items", "entries", "[]", "node"]);
const projection = tree.projections.find((projection) => projection.id === binding.projection)!;
assert(JSON.stringify(projection.selection).includes('"name":"key"'));
assert(!JSON.stringify(projection.selection).includes('"name":"body"'));
assert(tree.maxOccurrences <= 256);
});
test("guards control boundary participation and invalid syntax fails checking", async () => {
const checked = await compileQuery(
declaration(),
[item],
async () => `query Open($show:Boolean!) {root {
...Body @boundary(name:"body") @include(if:$show)
}} fragment Body on Item {body}`,
);
assert.deepEqual(checked.tree!.nodes[0]!.boundaries[0]!.conditions, [
{ include: true, value: { kind: "variable", name: "show" } },
]);
for (const [text, error] of [
[`query Open {root {...Body @boundary(name:"")}} fragment Body on Item {body}`, /QUERY_BOUNDARY_NAME/],
[
`query Open($name:String!) {root {...Body @boundary(name:$name)}} fragment Body on Item {body}`,
/QUERY_BOUNDARY_NAME/,
],
[
`query Open {root {...Body @boundary(name:"x") ...Heading @boundary(name:"x")}} ${fragments}`,
/QUERY_BOUNDARY_DUPLICATE/,
],
[`query Open {root {body @boundary(name:"x")}}`, /QUERY_VALIDATION/],
[`query Open {root {...Cycle @boundary(name:"x")}} fragment Cycle on Item {...Cycle}`, /QUERY_VALIDATION/],
] as const)
await assert.rejects(
compileQuery(declaration(), [item], async () => text),
error,
);
});
test("equivalent repeated paths share bindings; aliases preserve different windows", async () => {
const checked = await compileQuery(
declaration(navigation),
[item, navigation],
async () => `query Open {root {
document {...Heading @boundary(name:"basic")} document {...Body @boundary(name:"body")}
recent:items(first:2) {entries {node {...Body @boundary(name:"body")}}}
earlier:items(first:3) {entries {node {...Body @boundary(name:"body")}}}
}} ${fragments}`,
);
assert.deepEqual(
checked.tree!.nodes[0]!.bindings.map((binding) => binding.name),
["document", "recent", "earlier"],
);
assert.deepEqual(
checked
.tree!.nodes.find((node) => node.id === "root/binding/document")!
.boundaries.map((boundary) => boundary.name),
["basic", "body"],
);
await assert.rejects(
compileQuery(
declaration(navigation),
[item, navigation],
async () => `query Open($show:Boolean!) {root {
document @include(if:$show) {...Heading @boundary(name:"basic")} document {...Body @boundary(name:"body")}
}} ${fragments}`,
),
/QUERY_TREE_BINDING_CONFLICT/,
);
});
+215
View File
@@ -0,0 +1,215 @@
import assert from "node:assert/strict";
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { mkdtemp, mkdir, writeFile, readFile, rm, symlink } from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import test from "node:test";
import { pathToFileURL } from "node:url";
import { materializeWorkspaceSources, workspaceSourcePin } from "../src/capability-language/source-closure.js";
import { prepareDevelopmentSources } from "../src/capability-language/development-sources.js";
import { backupWorkspaceSources, restoreWorkspaceSources } from "../src/capability-language/source-backup.js";
const exec = promisify(callback);
const lock = (resources: string) => `quixos-lock version 1 {
quixos source { repository "https://unavailable.test/toolchain.git"; commit "${"a".repeat(40)}"; }
${resources}
}`;
test("exact source closure recovers through explicitly selected bundles, preserves history and rejects edits", async (context) => {
const temporary = await mkdtemp(path.join(os.tmpdir(), "qx-closure-"));
context.after(() => rm(temporary, { recursive: true, force: true }));
const locations: Record<string, string> = {};
const repository = async (name: string, resources: string) => {
const directory = path.join(temporary, name);
await mkdir(directory);
const git = async (...args: string[]) => (await exec("git", args, { cwd: directory })).stdout.trim();
await git("init");
await writeFile(path.join(directory, "quixos.lock"), lock(""));
await git("add", ".");
const commit = async () => {
await git("-c", "user.name=Test", "-c", "user.email=test@example.test", "commit", "-am", "Source");
return await git("rev-parse", "HEAD");
};
const parent = await commit();
await writeFile(path.join(directory, "quixos.lock"), lock(resources));
await writeFile(path.join(directory, "marker"), name);
await git("add", ".");
const rev = await commit();
await git("tag", `quixos-reachability/${rev}`);
const bundle = path.join(temporary, `${name}.bundle`);
await git("bundle", "create", bundle, `refs/tags/quixos-reachability/${rev}`);
const source = { repository: `https://unavailable.test/${name}.git`, commit: rev };
locations[source.repository] = pathToFileURL(bundle).href;
return { ...source, parent };
};
const common = await repository("common", "");
const dependency = `interface Common source { repository "${common.repository}"; commit "${common.commit}"; }`;
const left = await repository("left", dependency),
right = await repository("right", dependency);
const root = await repository(
"root",
`
package Left source { repository "${left.repository}"; commit "${left.commit}"; }
package Right source { repository "${right.repository}"; commit "${right.commit}"; }`,
);
const directory = path.join(temporary, "checkout");
const closure = await materializeWorkspaceSources({ source: root, directory, locations, concurrency: 2 });
assert.deepEqual(
await materializeWorkspaceSources({ source: root, directory, repositoryPrefix: "https://unavailable.test/" }),
closure,
);
// Allow this root but not its dependencies: reject them before Git acquisition.
const scopedRoot = { ...root, repository: "https://unavailable.test/selected/root.git" };
await assert.rejects(
materializeWorkspaceSources({
source: scopedRoot,
directory: path.join(temporary, "scoped"),
locations: { ...locations, [scopedRoot.repository]: locations[root.repository] },
repositoryPrefix: "https://unavailable.test/selected/",
}),
/outside the acquisition boundary/,
);
await assert.rejects(
materializeWorkspaceSources({ source: root, directory, repositoryPrefix: "https://unavailable.test/root.git/" }),
/outside the acquisition boundary/,
);
assert.equal(closure.resources.length, 4, "diamond dependency is materialized once");
assert.deepEqual(JSON.parse(await readFile(path.join(directory, "sources.json"), "utf8")), closure);
const rootEntry = closure.resources.find((entry) => entry.kind === "workspace")!;
const rootDirectory = path.join(directory, rootEntry.directory);
const rootLock = path.join(rootDirectory, "quixos.lock");
const rootBody = await readFile(rootLock, "utf8");
const outsideLock = path.join(temporary, "outside.lock");
await writeFile(outsideLock, rootBody);
// Exercise the shared loader directly: committed root symlinks must not let
// privileged source inspection read outside the acquired repository.
const { loadQuixosLock } = await import("../src/resource-lock/index.js");
const linkedLock = path.join(temporary, "linked.lock");
await symlink(outsideLock, linkedLock);
await assert.rejects(loadQuixosLock(linkedLock), /root locks must be ordinary/);
const backup = path.join(temporary, "retained");
const manifest = await backupWorkspaceSources({ source: root, directory, output: backup });
assert.equal(manifest.repositories.length, 4);
const restored = await restoreWorkspaceSources({ backup, directory: path.join(temporary, "restored") });
assert.deepEqual(restored, closure, "Source-only recovery retains canonical identity without an available forge");
await assert.rejects(backupWorkspaceSources({ source: root, directory, output: backup }), /must not exist/);
const missing = {
...manifest,
repositories: manifest.repositories.filter((entry) => entry.repository !== common.repository),
};
await writeFile(path.join(backup, "backup.json"), JSON.stringify(missing));
await assert.rejects(
restoreWorkspaceSources({ backup, directory: path.join(temporary, "incomplete") }),
/omits required repository/,
);
await writeFile(path.join(backup, "backup.json"), JSON.stringify(manifest));
await writeFile(path.join(backup, manifest.repositories[0].bundle), "damaged backup");
await assert.rejects(
restoreWorkspaceSources({ backup, directory: path.join(temporary, "damaged") }),
/integrity mismatch/,
);
assert.equal((await exec("git", ["rev-parse", "HEAD^"], { cwd: rootDirectory })).stdout.trim(), root.parent);
assert.equal(
(await exec("git", ["remote", "get-url", "origin"], { cwd: rootDirectory })).stdout.trim(),
root.repository,
);
// Cached recovery does not need the primary or the backup.
assert.deepEqual(await materializeWorkspaceSources({ source: root, directory }), closure);
const pinFile = path.join(temporary, "source.json");
await writeFile(pinFile, JSON.stringify(root));
const cli = path.resolve(import.meta.dirname, "../src/capability-language/tool-cli.js");
const command = await exec(process.execPath, [cli, "source-checkout", pinFile, "--dir", directory]);
assert.deepEqual(JSON.parse(command.stdout), closure, "Protocol CLI preserves exact cached source identity");
const scoped = await exec(process.execPath, [
cli,
"source-checkout",
pinFile,
"--dir",
directory,
"--repository-prefix",
"https://unavailable.test/",
]);
assert.deepEqual(JSON.parse(scoped.stdout), closure);
const cliBackup = path.join(temporary, "cli-backup");
const backupCommand = await exec(process.execPath, [
cli,
"source-backup",
pinFile,
"--dir",
directory,
"--output",
cliBackup,
]);
assert.equal(JSON.parse(backupCommand.stdout).repositories.length, 4);
const restoreCommand = await exec(process.execPath, [
cli,
"source-restore",
cliBackup,
"--dir",
path.join(temporary, "cli-restore"),
]);
assert.deepEqual(JSON.parse(restoreCommand.stdout), closure);
const developmentCommand = await exec(process.execPath, [
cli,
"source-develop",
pinFile,
"--dir",
path.join(temporary, "cli-developer"),
"--cache",
directory,
]);
const developmentResult = JSON.parse(developmentCommand.stdout);
assert.equal(developmentResult.repositories, 4);
assert.deepEqual(developmentResult.source, closure.root);
const a = await prepareDevelopmentSources({
source: root,
cacheDirectory: directory,
directory: path.join(temporary, "a"),
});
const b = await prepareDevelopmentSources({
source: root,
cacheDirectory: directory,
directory: path.join(temporary, "b"),
});
assert.notEqual(a.workspaceId, b.workspaceId);
const aRoot = path.join(a.directory, "root"),
bRoot = path.join(b.directory, "root");
assert.equal((await exec("git", ["remote", "get-url", "origin"], { cwd: aRoot })).stdout.trim(), root.repository);
assert.equal(
await readFile(path.join(aRoot, "quixos.lock"), "utf8"),
await readFile(path.join(rootDirectory, "quixos.lock"), "utf8"),
);
await exec("git", ["cat-file", "-e", root.parent], { cwd: aRoot });
await writeFile(path.join(aRoot, "marker"), "Independent developer change");
assert.equal(await readFile(path.join(bRoot, "marker"), "utf8"), "root");
assert.equal(await readFile(path.join(rootDirectory, "marker"), "utf8"), "root");
await assert.rejects(
prepareDevelopmentSources({ source: root, cacheDirectory: directory, directory: a.directory }),
/never overwritten/,
);
await writeFile(path.join(rootDirectory, "marker"), "edited");
await assert.rejects(materializeWorkspaceSources({ source: root, directory, locations }), /modified/);
await assert.rejects(
materializeWorkspaceSources({
source: root,
directory: path.join(temporary, "bounded"),
locations,
maxRepositories: 2,
}),
/repository limit/,
);
await assert.rejects(
materializeWorkspaceSources({
source: root,
directory,
locations: { [root.repository]: "https://secret:password@example.test/root.git" },
}),
/transport substitution/,
);
});
test("Workspace pins reject floating revisions and embedded credentials", () => {
assert.throws(() => workspaceSourcePin({ repository: "https://example.test/root.git", commit: "main" }));
assert.throws(() =>
workspaceSourcePin({ repository: "https://user:password@example.test/root.git", commit: "a".repeat(40) }),
);
});
+46
View File
@@ -0,0 +1,46 @@
import assert from "node:assert/strict";
import test from "node:test";
import { mkdtemp, writeFile, readFile, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { workspaceSourceLocks } from "../src/capability-language/source-locks.js";
test("fork lock proposals follow declared fragments and ignore comments and unrelated lock files", async (context) => {
const directory = await mkdtemp(path.join(os.tmpdir(), "qx-lock-fork-"));
context.after(() => rm(directory, { recursive: true, force: true }));
const from = { repository: "https://source.test/app/runtime.git", commit: "b".repeat(40) };
const to = { repository: "https://source.test/fork/runtime.git", commit: "c".repeat(40) };
const root = `quixos-lock version 1 {
quixos source { repository "https://source.test/quixos.git"; commit "${"a".repeat(40)}"; }
import "resources.lock";
// package Fake source { repository "https://unrelated.test/secret.git"; commit "${"d".repeat(40)}"; }
}`;
const fragment = `quixos-lock fragment version 1 {
// Preserve this note: 雨 📚
package Runtime source { repository "${from.repository}"; commit "${from.commit}"; }
}`;
await writeFile(path.join(directory, "quixos.lock"), root);
await writeFile(path.join(directory, "resources.lock"), fragment);
await writeFile(path.join(directory, "unrelated.lock"), "This is not a Quixos lock.");
const result = await workspaceSourceLocks(directory, [{ from, to }]);
assert.equal(result.lock.resources.length, 1);
assert.deepEqual(result.lock.resources[0].source, { resolver: "git", ...to });
assert.equal(result.files.find((file) => file.path === "quixos.lock")!.source, root);
assert.match(result.files.find((file) => file.path === "resources.lock")!.source, /Preserve this note: 雨 📚/);
assert.deepEqual(result.files.map((file) => file.path).sort(), ["quixos.lock", "resources.lock"]);
assert.equal(
await readFile(path.join(directory, "resources.lock"), "utf8"),
fragment,
"Proposing a fork does not edit selected source",
);
await assert.rejects(
workspaceSourceLocks(directory, [
{ from, to },
{ from, to },
]),
/Duplicate/,
);
await assert.rejects(workspaceSourceLocks(directory, [{ from, to: { ...to, commit: "main" } }]), /exact commit/);
await writeFile(path.join(directory, "quixos.lock"), root.replace('"resources.lock"', '"../outside.lock"'));
await assert.rejects(workspaceSourceLocks(directory), /Invalid source lock/);
});