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
Timothy J. Aveni ffd728b061 Keep pin-upgraded workbench paths relative for relocation 2026-10-08 07:59:49 -07:00
Quixos Subtree Publisher e78b7adf28 Publish quixos-protocol from c4319441699b7b8c5af7e70dade6e4ad8cccddea 2026-10-08 14:59:49 +00:00
Timothy J. Aveni a935273913 Use execution workspace review semantics during bottom-up pin upgrades 2026-10-08 07:43:32 -07:00
Quixos Subtree Publisher e162dc3013 Publish quixos-protocol from e4075003e669be0e307ec8c0585cccb1bd08276a 2026-10-08 14:43:32 +00:00
Quixos Subtree Publisher e65ddad798 Publish quixos-protocol from 932fe7714761fb8b5db4bd6401357cdff762a428 2026-10-08 05:08:28 +00:00
Timothy J. Aveni 31a48e746d Admit checked restricted artifacts without individual code approvals 2026-10-07 21:43:44 -07:00
Quixos Subtree Publisher f9a8d64564 Publish quixos-protocol from abc91ab1cbbd61ffd7f0a49dbc5768598ac8745c 2026-10-08 00:03:01 +00:00
Timothy J. Aveni 61534d3fb5 Keep checked browser runtime versions independent of authority upgrades 2026-10-07 17:03:01 -07:00
Timothy J. Aveni 391f02f78a Pool host-approved restricted code with scoped and fair graph execution 2026-10-07 16:47:19 -07:00
Timothy J. Aveni c8c56d8ea7 Bound checked candidate metadata and restricted invocation resources 2026-10-07 15:51:22 -07:00
Quixos Subtree Publisher 0443992dd4 Publish quixos-protocol from 452f3c48aadceaf08e56ae9c226e98a7f9df4900 2026-10-07 13:30:33 +00:00
Timothy J. Aveni 9248332da6 Allow explicitly exported read-only edge dependency ports 2026-10-07 06:30:33 -07:00
48 changed files with 2627 additions and 189 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
"sourceCommit": "dd7527da2714f3b0941ed908357fd82e98f45bc4",
"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 });
}
}
+45 -7
View File
@@ -15,7 +15,12 @@ import {
} from "./assembly.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
import { snapshotCommit, buildImmutableCandidate } from "./checked-build.js";
import { planEvolution, type WorkspaceRevision, type EvolutionReview } from "../capability-model/index.js";
import {
planEvolution,
reviewSemanticMajors,
type WorkspaceRevision,
type EvolutionReview,
} from "../capability-model/index.js";
import { withFileLock } from "./file-lock.js";
const execFile = promisify(callback);
type Source = { repository: string; commit: string };
@@ -187,6 +192,34 @@ export type UpgradeEffects = {
snapshot(root: string): Promise<string>;
publish(root: string, commit: string): Promise<void>;
};
/** Review a successfully built immutable candidate using the same boundary as
* ordinary authoring checks. Execution storage compatibility is checked against
* the current graph at activation; the legacy evolution model is not its schema.
*/
export async function reviewUpgradeCandidate({
artifact,
baseline,
candidate,
reviews = [],
}: {
artifact: string;
baseline: WorkspaceRevision | null;
candidate: WorkspaceRevision;
reviews?: EvolutionReview[];
}) {
let execution = false;
try {
await fs.access(path.join(artifact, "execution-plan.json"));
execution = true;
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
return execution
? { filename: "semantic-review.json", review: reviewSemanticMajors(baseline, candidate, reviews) }
: { filename: "evolution.json", review: planEvolution(baseline, candidate, { reviews }) };
}
const effects: UpgradeEffects = {
async check(node, root, output, spec) {
if (node.kind === "workspace" && !spec.baseline && !spec.bootstrap)
@@ -206,10 +239,15 @@ const effects: UpgradeEffects = {
? (JSON.parse(await fs.readFile(spec.baseline, "utf8")) as WorkspaceRevision)
: null;
const reviews = spec.reviews ? (JSON.parse(await fs.readFile(spec.reviews, "utf8")) as EvolutionReview[]) : [];
const evolution = planEvolution(baseline, JSON.parse(candidate), { reviews });
await fs.writeFile(path.join(output, "evolution.json"), JSON.stringify(evolution, null, 2));
if (evolution.blockers.length)
throw new Error(`Refactor required in ${node.directory}: ${evolution.blockers.join("; ")}`);
const { filename, review } = await reviewUpgradeCandidate({
artifact,
baseline,
candidate: JSON.parse(candidate),
reviews,
});
await fs.writeFile(path.join(output, filename), JSON.stringify(review, null, 2));
if (review.blockers.length)
throw new Error(`Refactor required in ${node.directory}: ${review.blockers.join("; ")}`);
}
},
async snapshot(root) {
@@ -443,13 +481,13 @@ export const applyPinUpgrades = async (
quixos: compiled.lock.quixos,
directResources: [...compiled.directResources.entries()].map(([bindingKey, node]) => {
const [kind, binding] = bindingKey.split("\0");
return { kind, binding, resourceKey: node.key, directory: node.directory };
return { kind, binding, resourceKey: node.key, directory: path.relative(plan.workbench, node.directory) };
}),
resources: [...resources.values()].map((node) => ({
key: node.key,
kind: node.kind,
source: node.source,
directory: node.directory,
directory: path.relative(plan.workbench, node.directory),
...(node.resource.kind === "build"
? {}
: {
+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) => {
+6 -1
View File
@@ -1526,7 +1526,6 @@ const validateBoundDependencies = (
);
break;
}
validateAttachmentAccess(issues, attachment, params.conformance, `${path}.binding.edgeTypeId`);
const projection = edgeProjection(attachment.attachment, edgeBinding.projectionId);
if (!projection) {
issue(
@@ -1537,6 +1536,12 @@ const validateBoundDependencies = (
);
break;
}
// Exporting traversal never exports writes or subscription ownership.
const publicRead =
projection.endpoint.publicTraversal &&
requirement.primitives.length > 0 &&
requirement.primitives.every((primitive) => primitive === "resolve");
if (!publicRead) validateAttachmentAccess(issues, attachment, params.conformance, `${path}.binding.edgeTypeId`);
if (projection.target.keyType && requirement.primitives.includes("connect"))
issue(
issues,
+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
+14 -3
View File
@@ -11,7 +11,7 @@ export interface ArtifactFile {
export interface ArtifactImplementation {
id: string;
target: string;
compatibility: "quixos-service-v1" | "quixos-js-module-v1";
compatibility: "quixos-service-v1" | "quixos-js-module-v1" | "quixos-restricted-module-v1";
path: string;
ports: "quixos-transaction-ports-v1";
registryExport?: string;
@@ -225,7 +225,11 @@ export function parseArtifactGroup(value: unknown): ArtifactGroup {
["registryExport"],
path,
);
if (entry.compatibility !== "quixos-service-v1" && entry.compatibility !== "quixos-js-module-v1")
if (
!["quixos-service-v1", "quixos-js-module-v1", "quixos-restricted-module-v1"].includes(
String(entry.compatibility),
)
)
fail(path, "Unsupported artifact ABI");
if (entry.ports !== "quixos-transaction-ports-v1") fail(path, "Unsupported transaction port ABI");
if (
@@ -233,7 +237,9 @@ export function parseArtifactGroup(value: unknown): ArtifactGroup {
(entry.target !== "server" || entry.registryExport !== undefined)
)
fail(path, "Service must target server and has no JS registry export");
if (entry.compatibility === "quixos-js-module-v1") text(entry.registryExport, `${path}.registryExport`);
if (entry.compatibility !== "quixos-service-v1") text(entry.registryExport, `${path}.registryExport`);
if (entry.compatibility === "quixos-restricted-module-v1" && entry.target !== "server")
fail(path, "Restricted modules must target server");
const exports = array(entry.exports, `${path}.exports`).map((value) => text(value, `${path}.exports`));
unique(exports, `${path}.exports`);
const files = parseArtifactFiles(entry.files, `${path}.files`);
@@ -300,6 +306,11 @@ export function checkPackageCoverage(
for (const id of implementation.exports) {
const entry = expected.exports.find((e) => e.id === id);
if (!entry) fail(implementation.id, `Unknown export ${id}`);
if (
implementation.compatibility === "quixos-restricted-module-v1" &&
(entry!.execution.kind === "remote" || !entry!.execution.targets.includes("server"))
)
fail(implementation.id, `Export ${id} does not promise restricted server computation`);
if (
implementation.target !== "server" &&
(!entry?.execution ||
+166 -107
View File
@@ -1,18 +1,34 @@
import { graphExecutionRepresentation } from "./graph-execution.js";
import { open, lstat } from "node:fs/promises";
import { constants } from "node:fs";
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,
type CandidateMetadataBudget,
type CandidateMetadataScope,
} from "./metadata.js";
const rootPattern = /^\/nix\/store\/[a-z0-9]{32}-[^/\s]+$/;
async function readJson(root: string, file: string): Promise<any> {
const handle = await open(join(root, file), constants.O_RDONLY | constants.O_NOFOLLOW);
async function readJson(root: string, file: string, scope: CandidateMetadataScope) {
// 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 {
const metadata = await handle.stat();
if (!metadata.isFile() || metadata.size > 32 * 1024 * 1024) throw Error(`Invalid checked candidate file: ${file}`);
return JSON.parse(await handle.readFile("utf8"));
return {
value: JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.bytes)),
releaseTree: input.releaseTree,
};
} catch (error) {
input.releaseTree();
throw error;
} finally {
await handle.close();
input.releaseInput();
}
}
/** Load the checked Nix output, preserving its original compilation and provenance.
@@ -22,113 +38,156 @@ async function readJson(root: string, file: string): Promise<any> {
export async function loadCheckedInstallationCandidate(
root: string,
selections: Readonly<Record<string, PackageInstallationSelection>> = {},
resources: { budget?: CandidateMetadataBudget; owner?: string } = {},
) {
if (!rootPattern.test(root) || !(await lstat(root)).isDirectory())
throw Error("Candidate must be an immutable Nix output directory");
const [candidate, plan, artifacts, source, entryRequest, kernel, queries, graph] = await Promise.all([
readJson(root, "candidate.json"),
readJson(root, "execution-plan.json"),
readJson(root, "execution-artifacts.json"),
readJson(root, "source.json"),
readJson(root, "entry.json"),
readJson(root, "kernel.json"),
readJson(root, "queries.json"),
readJson(root, "graph-execution.json"),
]);
const { executionContracts: _legacyContracts, ...workspace } = candidate;
if (
plan?.definition?.codec !== "camino-execution-plan-v1" ||
hexDigest(plan.definition) !== plan.digest ||
hexDigest(workspace) !== hexDigest(plan.definition.workspace)
)
throw new ExecutionPlanError(
"CANDIDATE_PLAN_MISMATCH",
root,
"Stored plan must match its digest and exact workspace source contract",
);
if (
source.kind !== "workspace" ||
source.commit !== plan.definition.workspace.sourceRootCommit ||
typeof source.repository !== "string"
)
throw new ExecutionPlanError("CANDIDATE_SOURCE_MISMATCH", root, "Expected exact workspace source provenance");
const repository = new URL(source.repository);
if (
!["https:", "ssh:"].includes(repository.protocol) ||
repository.username ||
repository.password ||
repository.hash ||
repository.search
)
throw new ExecutionPlanError("CANDIDATE_SOURCE_INVALID", root, "Expected a source URL without credentials");
if (
artifacts.codec !== "camino-checked-execution-v2" ||
typeof artifacts.generator !== "string" ||
!/^[0-9a-f]{64}$/.test(artifacts.generator) ||
!artifacts.roots ||
typeof artifacts.roots !== "object" ||
Array.isArray(artifacts.roots)
)
throw new ExecutionPlanError("CANDIDATE_ARTIFACTS_INVALID", root, "Expected checked package roots");
for (const entry of Object.values(artifacts.roots))
if (typeof entry !== "string" || !rootPattern.test(entry))
const scope = (resources.budget ?? candidateMetadataBudget).acquire(resources.owner ?? root);
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 [
"execution-plan.json",
"execution-artifacts.json",
"source.json",
"entry.json",
"kernel.json",
"queries.json",
]) {
const parsed = await readJson(root, file, scope);
values.push(parsed.value);
}
const [plan, artifacts, source, entryRequest, kernel, queries] = values;
if (
plan?.definition?.codec !== "camino-execution-plan-v1" ||
hexDigest(plan.definition) !== plan.digest ||
workspaceDigest !== hexDigest(plan.definition.workspace)
)
throw new ExecutionPlanError(
"CANDIDATE_ARTIFACTS_INVALID",
"CANDIDATE_PLAN_MISMATCH",
root,
"Package artifacts must be immutable Nix roots",
"Stored plan must match its digest and exact workspace source contract",
);
const installation = await loadExecutionInstallation(plan, artifacts.roots, selections);
const entry = compileWorkspaceEntry(plan, entryRequest);
for (const [name, program, codec] of [
["kernel", kernel, "camino-kernel-program-v1"],
["queries", queries, "camino-query-program-v1"],
] as const)
if (program?.codec !== codec || program.plan !== kernel.plan || hexDigest(program) !== artifacts.programs?.[name])
throw new ExecutionPlanError(
"CANDIDATE_PROGRAM_MISMATCH",
name,
"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",
if (
source.kind !== "workspace" ||
source.commit !== plan.definition.workspace.sourceRootCommit ||
typeof source.repository !== "string"
)
throw new ExecutionPlanError("CANDIDATE_SOURCE_MISMATCH", root, "Expected exact workspace source provenance");
const repository = new URL(source.repository);
if (
!["https:", "ssh:"].includes(repository.protocol) ||
repository.username ||
repository.password ||
repository.hash ||
repository.search
)
throw new ExecutionPlanError("CANDIDATE_SOURCE_INVALID", root, "Expected a source URL without credentials");
if (
artifacts.codec !== "camino-checked-execution-v2" ||
typeof artifacts.generator !== "string" ||
!/^[0-9a-f]{64}$/.test(artifacts.generator) ||
!artifacts.roots ||
typeof artifacts.roots !== "object" ||
Array.isArray(artifacts.roots)
)
throw new ExecutionPlanError("CANDIDATE_ARTIFACTS_INVALID", root, "Expected checked package roots");
for (const entry of Object.values(artifacts.roots))
if (typeof entry !== "string" || !rootPattern.test(entry))
throw new ExecutionPlanError(
"CANDIDATE_ARTIFACTS_INVALID",
root,
"Package artifacts must be immutable Nix roots",
);
const installation = await loadExecutionInstallation(plan, artifacts.roots, selections, scope);
const entry = compileWorkspaceEntry(plan, entryRequest);
for (const [name, program, codec] of [
["kernel", kernel, "camino-kernel-program-v1"],
["queries", queries, "camino-query-program-v1"],
] as const)
if (program?.codec !== codec || program.plan !== kernel.plan || hexDigest(program) !== artifacts.programs?.[name])
throw new ExecutionPlanError(
"CANDIDATE_PROGRAM_MISMATCH",
name,
"Stored interpreter input must match the checked build and plan",
);
const expectedGraph = graphExecutionRepresentation(plan, installation, kernel, queries);
// 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: 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) => ({
revision: pkg.revision,
source: pkg.source,
})),
};
const reference = {
root,
"Stored graph execution differs from checked inputs",
);
const provenance = {
workspaceRevision: 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) => ({
revision: pkg.revision,
source: pkg.source,
})),
};
const reference = {
root,
digest: hexDigest({
plan: plan.digest,
world: installation.world,
digest: hexDigest({
plan: plan.digest,
world: installation.world,
provenance,
roots: artifacts.roots,
entry,
programs: artifacts.programs,
}),
};
const catalog = createClientContractCatalog(plan, installation, scope, kernel);
return {
reference,
workspace: plan.definition.workspace.workspaceId,
world: hexDigest(expectedGraph),
plan: kernel.plan as string,
graphExecution: expectedGraph,
provenance,
roots: artifacts.roots,
entry,
programs: artifacts.programs,
}),
};
return {
reference,
workspace: plan.definition.workspace.workspaceId,
world: hexDigest(graph),
plan: kernel.plan as string,
graphExecution: graph,
provenance,
entry,
kernel,
queries,
installation,
executionPlan: plan as ExecutionPlan,
};
kernel,
queries,
installation,
executionPlan: plan as ExecutionPlan,
clientContractCatalog: catalog.select,
metadata: {
get bytes() {
return scope.bytes;
},
release: () => {
catalog.close();
scope.release();
},
},
};
} catch (error) {
scope.release();
throw error;
}
}
export function compileWorkspaceEntry(plan: ExecutionPlan, entryRequest: unknown): { operation: string } | null {
+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") +
+25 -11
View File
@@ -13,6 +13,7 @@ import {
type CheckedWorld,
} from "./artifacts.js";
import { type ExecutionPlan, ExecutionPlanError } from "./plan.js";
import { readCandidateMetadata, type CandidateMetadataScope } from "./metadata.js";
const maxFileBytes = 256 * 1024 * 1024;
const metadataLimit = 16 * 1024 * 1024;
@@ -21,6 +22,18 @@ const fail = (where: string, message: string): never => {
};
/** Roots must be trusted immutable output directories. Refuse symlinks, including
* interior directory links. Hash verification is still required before serving bytes. */
async function artifactFile(root: string, relative: string): Promise<string> {
artifactPath(relative);
const components = relative.split("/");
let resolved = root;
for (let index = 0; index < components.length; index++) {
resolved = path.join(resolved, components[index]!);
const stat = await lstat(resolved);
if (stat.isSymbolicLink() || (index < components.length - 1 ? !stat.isDirectory() : !stat.isFile()))
fail(relative, "Only real directories and regular files are allowed");
}
return resolved;
}
export async function artifactFileReader(roots: Readonly<Record<string, string>>): Promise<ArtifactReader> {
const directories = new Map<string, string>();
for (const [revision, root] of Object.entries(roots)) {
@@ -33,16 +46,8 @@ export async function artifactFileReader(roots: Readonly<Record<string, string>>
return async (revision, relative, limit) => {
const root = directories.get(revision);
if (!root) fail(revision, "Unknown package output");
artifactPath(relative);
if (!Number.isSafeInteger(limit) || limit < 0 || limit > maxFileBytes) fail(relative, "Invalid file byte limit");
const components = relative.split("/");
let resolved = root!;
for (let index = 0; index < components.length; index++) {
resolved = path.join(resolved, components[index]!);
const stat = await lstat(resolved);
if (stat.isSymbolicLink() || (index < components.length - 1 ? !stat.isDirectory() : !stat.isFile()))
fail(relative, "Only real directories and regular files are allowed");
}
const resolved = await artifactFile(root!, relative);
const handle = await open(resolved, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
try {
const stat = await handle.stat();
@@ -72,6 +77,7 @@ export interface LoadedArtifactWorld {
export async function loadArtifactWorld(
plan: ExecutionPlan,
roots: Readonly<Record<string, string>>,
metadata?: CandidateMetadataScope,
): Promise<LoadedArtifactWorld> {
const read = await artifactFileReader(roots),
manifests: PackageArtifacts[] = [];
@@ -79,8 +85,16 @@ export async function loadArtifactWorld(
if (JSON.stringify(Object.keys(roots).sort()) !== JSON.stringify(expected))
fail("world", "Output roots must exactly match the installed plan");
for (const revision of expected) {
const bytes = await read(revision, "share/quixos/package-artifacts.json", metadataLimit);
const manifest = parsePackageArtifacts(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)));
const file = "share/quixos/package-artifacts.json";
const input = metadata
? await readCandidateMetadata(await artifactFile(roots[revision]!, file), metadata, metadataLimit)
: { bytes: await read(revision, file, metadataLimit), releaseInput: () => {} };
let manifest: PackageArtifacts;
try {
manifest = parsePackageArtifacts(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.bytes)));
} finally {
input.releaseInput();
}
if (manifest.packageRevisionId !== revision) fail(revision, "Output belongs to another package revision");
manifests.push(manifest);
}
+1
View File
@@ -17,6 +17,7 @@ export * from "./queries.js";
export * from "./query-document.js";
export { generatePortableClientBindings } from "../bindings/portable-client.js";
export * from "./candidate.js";
export * from "./metadata.js";
export { reviewSemanticMajors } from "../capability-model/evolution.js";
+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[] = [];
+90 -4
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";
@@ -11,6 +11,7 @@ import {
type PublicQueryContract,
} from "./client-contracts.js";
import { resultInterfaces } from "./projections.js";
import type { CandidateMetadataScope } from "./metadata.js";
/** Activation is assigned by graph authority, never by a package artifact. */
export interface ActiveExecutionIdentity {
@@ -44,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(
@@ -68,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(
@@ -97,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 };
@@ -112,6 +183,8 @@ export interface ExecutionInstallation {
readonly root: string;
readonly service?: ArtifactImplementation;
readonly module?: ArtifactImplementation;
/** Checked candidate only; the qualified host verifies its import surface and SDK. */
readonly restricted?: ArtifactImplementation;
readonly browserImplementation?: ArtifactImplementation;
readonly browserURL?: string;
readonly browserRegistry?: string;
@@ -159,8 +232,9 @@ export async function loadExecutionInstallation(
plan: ExecutionPlan,
roots: Readonly<Record<string, string>>,
selections: Readonly<Record<string, PackageInstallationSelection>> = {},
metadata?: CandidateMetadataScope,
): Promise<ExecutionInstallation> {
return assembleExecutionInstallation(plan, roots, await loadArtifactWorld(plan, roots), selections);
return assembleExecutionInstallation(plan, roots, await loadArtifactWorld(plan, roots, metadata), selections);
}
function assembleExecutionInstallation(
plan: ExecutionPlan,
@@ -249,6 +323,11 @@ function assembleExecutionInstallation(
exports: exports.map((entry) => entry.id),
})
: undefined;
const restrictedCandidates = manifest.implementations.filter(
(entry) => entry.compatibility === "quixos-restricted-module-v1",
);
if (restrictedCandidates.length > 1) fail(revision, "Ambiguous restricted module candidates");
const restricted = restrictedCandidates[0];
const browserExports = exports.filter(
(entry) =>
entry.execution.kind !== "remote" &&
@@ -256,7 +335,13 @@ function assembleExecutionInstallation(
);
if (!browserExports.length) {
if (selection.browser) fail(revision, "Package has no browser exports to select");
packages.push({ revision, root, ...(service ? { service } : {}), ...(module ? { module } : {}) });
packages.push({
revision,
root,
...(service ? { service } : {}),
...(module ? { module } : {}),
...(restricted ? { restricted } : {}),
});
continue;
}
const browser = selectArtifactImplementation(manifest, {
@@ -287,6 +372,7 @@ function assembleExecutionInstallation(
root,
...(service ? { service } : {}),
...(module ? { module } : {}),
...(restricted ? { restricted } : {}),
browserImplementation: browser,
browserURL: contentArtifactUrl(entry),
browserRegistry: browser.registryExport,
+138
View File
@@ -0,0 +1,138 @@
import { constants } from "node:fs";
import { open } from "node:fs/promises";
/** Admission estimates, not a hard V8 heap limit. A process/cgroup ceiling is
* still required. Charge input, parse trees and derived indexes before reading
* or parsing. Each live candidate retains its reservation until explicit close. */
export class CandidateMetadataBudget {
readonly limits: Readonly<{ bytes: number; ownerBytes: number; candidates: number }>;
private bytes = 0;
private scopes = 0;
private readonly owners = new Map<string, number>();
// 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");
this.limits = Object.freeze({ ...limits });
}
snapshot() {
return { bytes: this.bytes, candidates: this.scopes, owners: this.owners.size, limits: this.limits };
}
acquire(owner: string) {
if (!owner || owner.length > 4096) throw Error("INVALID_CANDIDATE_METADATA_OWNER");
if (this.scopes >= this.limits.candidates) throw Error("CANDIDATE_METADATA_LIMIT");
this.scopes++;
let bytes = 0,
closed = false;
const charge = (amount: number) => {
if (closed) throw Error("CANDIDATE_METADATA_CLOSED");
if (!Number.isSafeInteger(amount) || amount < 0) throw Error("INVALID_CANDIDATE_METADATA_CHARGE");
const owned = this.owners.get(owner) ?? 0;
if (this.bytes + amount > this.limits.bytes || owned + amount > this.limits.ownerBytes)
throw Error("CANDIDATE_METADATA_LIMIT");
bytes += amount;
this.bytes += amount;
this.owners.set(owner, owned + amount);
};
const subtract = (amount: number) => {
bytes -= amount;
this.bytes -= amount;
const remaining = (this.owners.get(owner) ?? 0) - amount;
if (remaining) this.owners.set(owner, remaining);
else this.owners.delete(owner);
};
return {
get bytes() {
return bytes;
},
charge,
/** Opaque, idempotent transient reservation; callers cannot subtract an
* arbitrary amount or release another candidate's charges. */
reserve: (amount: number) => {
charge(amount);
let released = false;
return () => {
if (closed || released) return;
released = true;
subtract(amount);
};
},
release: () => {
if (closed) return;
closed = true;
subtract(bytes);
this.scopes--;
},
};
}
}
export type CandidateMetadataScope = ReturnType<CandidateMetadataBudget["acquire"]>;
export const candidateMetadataBudget = new CandidateMetadataBudget();
// Estimate JSON allocation without constructing its tree. Object/array headers,
// value slots and strings get explicit weights, with a multiplier for checked
// indexes/derived representations. Indentation must not multiply tree charges.
function jsonAllocation(bytes: Uint8Array): number {
let estimate = 0;
for (let i = 0; i < bytes.length; i++) {
const c = bytes[i]!;
if (c === 123) estimate += 64;
else if (c === 91) estimate += 32;
else if (c === 34) {
const start = ++i;
while (i < bytes.length && bytes[i] !== 34) {
if (bytes[i] === 92) i++;
i++;
}
estimate += 32 + 2 * (i - start);
} else if (c === 45 || (c >= 48 && c <= 57) || c === 116 || c === 102 || c === 110) {
estimate += 16;
while (i + 1 < bytes.length) {
const next = bytes[i + 1]!;
if (next === 9 || next === 10 || next === 13 || next === 32 || next === 44 || next === 93 || next === 125)
break;
i++;
}
}
}
return estimate * 2;
}
/** Immutable regular files only. Fixed allocation plus an extra byte catches
* growth; readFile() would allocate before the admission check can intervene. */
export async function readCandidateMetadata(
path: string,
scope: CandidateMetadataScope,
limit: number,
): Promise<{ bytes: Uint8Array; releaseInput: () => void; releaseTree: () => void }> {
const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
let releaseInput: (() => void) | undefined;
try {
const stat = await handle.stat();
if (!stat.isFile() || !Number.isSafeInteger(stat.size) || stat.size > limit)
throw Error("CANDIDATE_METADATA_FILE_LIMIT");
// Reserve the input buffer and UTF-16 decoding before allocating either.
releaseInput = scope.reserve((stat.size + 1) * 3);
const bytes = Buffer.alloc(stat.size + 1);
let offset = 0;
while (offset < bytes.length) {
const read = await handle.read(bytes, offset, bytes.length - offset, offset);
if (!read.bytesRead) break;
offset += read.bytesRead;
}
if (offset !== stat.size) throw Error("CANDIDATE_METADATA_CHANGED");
const content = bytes.subarray(0, offset);
// No JSON.parse/decoded tree exists until its shape-based charge succeeds.
const releaseTree = scope.reserve(jsonAllocation(content));
return { bytes: content, releaseInput, releaseTree };
} catch (error) {
releaseInput?.();
throw error;
} finally {
await handle.close();
}
}
+83 -6
View File
@@ -23,6 +23,75 @@ export interface ImportFinding {
readonly detail: string;
readonly line: number | null;
}
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,
qualifiedSDKIdentity?: string,
): Promise<ImportFinding[]> {
const entry = implementation.files.find((file) => file.path === implementation.path);
const sdk = implementation.files.find((file) => file.path === restrictedSDKPath);
if (
implementation.compatibility !== "quixos-restricted-module-v1" ||
implementation.target !== "server" ||
implementation.files.length !== 2 ||
!entry ||
!sdk ||
entry === sdk ||
entry.mediaType !== "text/javascript" ||
sdk.mediaType !== "text/javascript" ||
!/^[$A-Z_a-z][$\w]*$/.test(implementation.registryExport ?? "")
)
return [{ code: "RESTRICTED_CLOSURE", detail: "Expected one closed module and the restricted SDK", 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,
]);
}
const nativeImports = new Set(builtinModules.flatMap((name) => [name, name.replace(/^node:/, "")]));
const ambient = new Set([
"process",
@@ -136,9 +205,8 @@ export function inspectJavaScriptSurface(source: string, checkedSDKImports: read
return findings;
}
/** Derived metadata only. Browser/server portability and available targets say
* nothing about sandbox eligibility. No existing package artifact has a
* qualified restricted runtime, so it conservatively requires its VM. */
/** Derived metadata only. A build report can describe a closed candidate but
* cannot qualify the host's engine, SDK, OS sandbox or resource controller. */
export async function reportExecutionPlacement(
plan: ExecutionPlan,
installation: ExecutionInstallation,
@@ -155,11 +223,13 @@ export async function reportExecutionPlacement(
const packages = await Promise.all(
installation.packages.map(async (pkg) => {
const implementations: { id: string; digest: string; compatibility: string; findings: ImportFinding[] }[] = [];
for (const implementation of [pkg.service, pkg.module].filter(
for (const implementation of [pkg.service, pkg.module, pkg.restricted].filter(
(entry): entry is ArtifactImplementation => !!entry,
)) {
let findings: ImportFinding[];
if (implementation.compatibility === "quixos-js-module-v1") {
if (implementation.compatibility === "quixos-restricted-module-v1")
findings = await inspectRestrictedImplementation(pkg.revision, implementation, read);
else if (implementation.compatibility === "quixos-js-module-v1") {
const file = implementation.files.find((file) => file.path === implementation.path)!;
if (!file || file.bytes > 1024 * 1024)
findings = [
@@ -191,7 +261,14 @@ export async function reportExecutionPlacement(
revision: pkg.revision,
requiresVM: implementations.length > 0,
restrictedEligible: false as const,
reason: "NO_QUALIFIED_RESTRICTED_RUNTIME",
reason: pkg.restricted ? "HOST_PROFILE_QUALIFICATION_REQUIRED" : "NO_RESTRICTED_IMPLEMENTATION",
restrictedCandidate: pkg.restricted
? {
digest: pkg.restricted.contentDigest,
exports: pkg.restricted.exports,
compatible: implementations.find((entry) => entry.id === pkg.restricted!.id)!.findings.length === 0,
}
: null,
implementations,
};
}),
+2
View File
@@ -29,6 +29,8 @@ export interface PackageInvocation {
/** Capability arguments/receiver use the versioned managed-reference codec. */
input: unknown;
receiver: unknown | null;
/** Host-owned remaining root resources, separate from authored input. */
budget?: { root: string; depth: number; calls: number; deadlineMs: number };
}
export type InvocationReply =
| { abi: typeof transactionPortAbi; invocation: string; status: "returned"; output: unknown }
+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,
+160
View File
@@ -0,0 +1,160 @@
import test from "node:test";
import assert from "node:assert/strict";
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 });
const first = budget.acquire("a"),
second = budget.acquire("a"),
third = budget.acquire("b");
assert.throws(() => budget.acquire("c"), /CANDIDATE_METADATA_LIMIT/);
first.charge(400);
second.charge(200);
third.charge(400);
assert.throws(() => second.charge(1), /CANDIDATE_METADATA_LIMIT/);
assert.throws(() => third.charge(1), /CANDIDATE_METADATA_LIMIT/);
assert.equal(budget.snapshot().bytes, 1000);
second.release();
second.release();
assert.equal(budget.snapshot().bytes, 800);
assert.throws(() => second.charge(1), /CANDIDATE_METADATA_CLOSED/);
first.release();
third.release();
assert.deepEqual(budget.snapshot(), { bytes: 0, candidates: 0, owners: 0, limits: budget.limits });
});
test("metadata files reserve parse/index space before allocation and refuse oversized or linked inputs", async () => {
const root = await mkdtemp(join(tmpdir(), "qx-metadata-budget-"));
const budget = new CandidateMetadataBudget({ bytes: 450, ownerBytes: 450, candidates: 2 });
const scope = budget.acquire("workspace");
try {
const file = join(root, "metadata.json");
await writeFile(file, '{"ok":true}');
const input = await readCandidateMetadata(file, scope, 100);
assert.equal(new TextDecoder().decode(input.bytes), '{"ok":true}');
const withInput = scope.bytes;
input.releaseInput();
input.releaseInput();
const first = scope.bytes;
assert.equal(withInput - first, (Buffer.byteLength('{"ok":true}') + 1) * 3);
assert.ok(first > 12 * 3, "Decoded tree and derived metadata are reserved before parsing");
await assert.rejects(readCandidateMetadata(file, scope, 100), /CANDIDATE_METADATA_LIMIT/);
assert.equal(scope.bytes, first, "Rejected tree allocation releases its transient input buffer");
await assert.rejects(readCandidateMetadata(file, scope, 5), /CANDIDATE_METADATA_FILE_LIMIT/);
await symlink(file, join(root, "linked"));
await assert.rejects(readCandidateMetadata(join(root, "linked"), scope, 100), { code: "ELOOP" });
await assert.rejects(readCandidateMetadata(root, scope, 100), /CANDIDATE_METADATA_FILE_LIMIT/);
assert.equal(scope.bytes, first);
input.releaseTree();
input.releaseTree();
assert.equal(scope.bytes, 0, "Discarded validation trees can release independently of the candidate");
} finally {
scope.release();
await rm(root, { recursive: true, force: true });
}
assert.equal(budget.snapshot().bytes, 0);
});
test("JSON structure accounting is independent of indentation and covers strings, values and dense nodes", async () => {
const root = await mkdtemp(join(tmpdir(), "qx-metadata-shape-"));
const budget = new CandidateMetadataBudget();
try {
for (const value of [{}, [null, true, false, 1, -3.5e8], { 'a"b': '雪\\"', nodes: [{}, {}, {}] }]) {
const measured = [];
for (const spaces of [0, 4]) {
const file = join(root, "metadata.json"),
text = JSON.stringify(value, null, spaces);
await writeFile(file, text);
const scope = budget.acquire("test");
try {
const input = await readCandidateMetadata(file, scope, 10000);
assert.deepEqual(JSON.parse(new TextDecoder().decode(input.bytes)), value);
input.releaseInput();
measured.push(scope.bytes);
} finally {
scope.release();
}
}
assert.equal(measured[0], measured[1], "Whitespace changes input storage, not the allocated JSON shape");
assert.ok(measured[0] >= 128);
}
} finally {
await rm(root, { recursive: true, force: true });
}
assert.equal(budget.snapshot().bytes, 0);
});
test("transient leases release exactly once, including after their parent closes", () => {
const budget = new CandidateMetadataBudget({ bytes: 1000, ownerBytes: 600, candidates: 3 });
const scope = budget.acquire("a");
scope.charge(200);
const release = scope.reserve(300);
assert.throws(() => scope.reserve(101), /CANDIDATE_METADATA_LIMIT/);
release();
release();
assert.equal(scope.bytes, 200);
const afterClose = scope.reserve(100);
scope.release();
afterClose();
assert.equal(scope.bytes, 0);
assert.equal(budget.snapshot().bytes, 0);
});
+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",
+57
View File
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import { test } from "node:test";
import {
compileWorkspaceRevision,
capabilityId,
computeCapabilityClosure,
resolveOperationPlan,
validateWorkspaceRevision,
@@ -197,6 +198,62 @@ test("public traversal permits a native inverse read without exporting mutation
expectIssue(workspace, "private-attachment-access");
});
test("public traversal exports only the selected read-only package edge port", () => {
const workspace = makeValidCapabilityWorkspace();
const exportDefinition = workspace.packageImports
.flatMap((pkg) => pkg.exports)
.find((entry) => entry.id === fixtureId.summaryGetExport)!;
const port = {
id: capabilityId.dependencyPort("port:summary:owner"),
displayName: "owner",
requirement: {
kind: "edge" as const,
target: { kind: "interface" as const, interfaceRevisionId: fixtureId.namedV1 },
cardinality: "exactly-one" as const,
primitives: ["resolve" as const],
},
};
exportDefinition.dependencyPorts.push(port);
const binding = conformance(workspace, fixtureId.projectSummaryConformance).operationBindings[0].binding;
assert.ok(binding.kind === "package");
binding.dependencies.push({
portId: port.id,
binding: {
kind: "edge",
edgeTypeId: fixtureId.projectOwner,
projectionId: fixtureId.projectOwnerProjection,
},
});
const edge = conformance(workspace, fixtureId.projectOwnedConformance).privateAttachments.find(
(entry) => entry.id === fixtureId.projectOwner,
);
assert.ok(edge?.kind === "edge");
const endpoint = edge.endpoints.find((entry) => entry.projectionId === fixtureId.projectOwnerProjection)!;
expectIssue(workspace, "private-attachment-access");
// Publicity of the opposite projection does not grant this receiver access.
edge.endpoints.find((entry) => entry !== endpoint)!.publicTraversal = true;
expectIssue(workspace, "private-attachment-access");
endpoint.publicTraversal = true;
assert.deepEqual(validateWorkspaceRevision(workspace), []);
const compiled = compileWorkspaceRevision(workspace);
assert.ok(compiled.ok);
const plan = resolveOperationPlan(compiled.plan, fixtureId.project, fixtureId.summaryV1, fixtureId.summaryGet);
assert.ok(plan?.kind === "package");
assert.deepEqual(plan.dependencies.find((entry) => entry.port.id === port.id)?.port.requirement, port.requirement);
for (const primitive of ["connect", "disconnect", "watch-start", "watch-stop"] as const) {
const mutated = structuredClone(workspace);
const requirement = mutated.packageImports
.flatMap((pkg) => pkg.exports)
.find((entry) => entry.id === fixtureId.summaryGetExport)!
.dependencyPorts.find((entry) => entry.id === port.id)!.requirement;
assert.ok(requirement.kind === "edge");
requirement.primitives.push(primitive);
expectIssue(mutated, "private-attachment-access");
}
});
test("native state and edge providers must match operation shape", () => {
const stateWorkspace = makeValidCapabilityWorkspace();
const state = conformance(stateWorkspace, fixtureId.projectNamedConformance).operationBindings[0]!.binding;
+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;
+97 -1
View File
@@ -5,9 +5,90 @@ import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { execFile as callback } from "node:child_process";
import { planPinUpgrades, applyPinUpgrades, type UpgradeEffects } from "../src/capability-language/pin-upgrades.js";
import {
planPinUpgrades,
applyPinUpgrades,
discoverUpgradeSpec,
reviewUpgradeCandidate,
type UpgradeEffects,
} from "../src/capability-language/pin-upgrades.js";
import { capabilityId } from "../src/capability-model/index.js";
import { makeValidCapabilityWorkspace } from "./fixtures/capability-model.js";
const execFile = promisify(callback);
test("execution pin upgrades accept unchanged shared attachments without legacy migration planning", async (context) => {
const artifact = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-execution-review-"));
context.after(() => fs.rm(artifact, { recursive: true, force: true }));
await fs.writeFile(path.join(artifact, "execution-plan.json"), "{}");
const baseline = makeValidCapabilityWorkspace();
assert.ok(baseline.sharedAttachments.length);
const candidate = structuredClone(baseline);
candidate.sourceRootCommit = "f".repeat(40);
const { filename, review } = await reviewUpgradeCandidate({ artifact, baseline, candidate });
assert.equal(filename, "semantic-review.json");
assert.equal(review.checkerVersion, "quixos-semantic-review-v1");
assert.deepEqual(review.blockers, []);
assert.equal("migrationRequired" in review, false);
assert.equal("runtimeActions" in review, false);
});
test("execution pin upgrades require exact semantic-major receipts and reject foreign workspaces", async (context) => {
const artifact = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-semantic-review-"));
context.after(() => fs.rm(artifact, { recursive: true, force: true }));
await fs.writeFile(path.join(artifact, "execution-plan.json"), "{}");
const baseline = makeValidCapabilityWorkspace();
baseline.conformances[0]!.id = capabilityId.conformance("conformance:project:named");
const candidate = structuredClone(baseline);
candidate.conformances[0]!.semanticMajor = 2;
const { review } = await reviewUpgradeCandidate({ artifact, baseline, candidate });
assert.equal(review.reviews.length, 1);
assert.equal(review.reviews[0]!.accepted, false);
assert.match(review.blockers[0]!, /Semantic-major review required/);
const receipt = {
requirementDigest: review.reviews[0]!.requirementDigest,
decision: "accepted-unchanged" as const,
rationale: "Reviewed the consumer against the provider's unchanged label behavior",
agentId: "upgrade-fixture-agent",
};
assert.deepEqual(
(await reviewUpgradeCandidate({ artifact, baseline, candidate, reviews: [receipt] })).review.blockers,
[],
);
candidate.packageImports[0]!.source.commit = "f".repeat(40);
assert.equal(
(await reviewUpgradeCandidate({ artifact, baseline, candidate, reviews: [receipt] })).review.reviews[0]!.accepted,
false,
);
candidate.workspaceId = capabilityId.workspace("workspace:foreign");
await assert.rejects(() => reviewUpgradeCandidate({ artifact, baseline, candidate }), /different workspace/);
});
test("nonexecution pin upgrades retain their evolution checks", async (context) => {
const artifact = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-evolution-review-"));
context.after(() => fs.rm(artifact, { recursive: true, force: true }));
const baseline = makeValidCapabilityWorkspace();
const { filename, review } = await reviewUpgradeCandidate({
artifact,
baseline,
candidate: structuredClone(baseline),
});
assert.equal(filename, "evolution.json");
assert.equal(review.checkerVersion, "quixos-evolution-v1");
assert.ok(review.blockers.some((message) => message.includes("legacy workspace-shared attachments")));
assert.equal("migrationRequired" in review, true);
});
test("unexpected artifact access failures do not select nonexecution checks", async (context) => {
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-review-failure-"));
context.after(() => fs.rm(directory, { recursive: true, force: true }));
const artifact = path.join(directory, "not-a-directory");
await fs.writeFile(artifact, "not an artifact");
const candidate = makeValidCapabilityWorkspace();
await assert.rejects(() => reviewUpgradeCandidate({ artifact, baseline: candidate, candidate }), {
code: "ENOTDIR",
});
});
test(
"real jj snapshots and immutable Git publication propagate a changed interface into the root",
{ skip: !process.env.QX_CHECK_GENERATOR },
@@ -73,6 +154,21 @@ test(
await run(workbench, "git", ["--git-dir", path.join(workbench, "remotes/named.git"), "show-ref"]),
new RegExp(commit),
);
assert.equal(graph.resources[0].directory, "resources/Named");
assert.equal(graph.directResources[0].directory, "resources/Named");
// A retained workbench can move without keeping its original absolute path
// alive. Recovery journals remain bound to their original writer/root.
const relocated = `${workbench}-relocated`;
context.after(() => fs.rm(relocated, { recursive: true, force: true }));
await fs.cp(workbench, relocated, { recursive: true });
const discovered = await discoverUpgradeSpec(relocated);
assert.equal(discovered.nodes[1]!.directory, "resources/Named");
const relocatedPlan = await planPinUpgrades(relocated, { ...discovered, bootstrap: true });
assert.equal(relocatedPlan.workbench, relocated);
assert.deepEqual(
relocatedPlan.nodes.map((node) => node.directory),
["resources/Named", "root"],
);
},
);
+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/);
});