From f69c81f607bf7578a52adfa9843d3d0a53cc4ed5 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Mon, 21 Sep 2026 00:57:50 -0700 Subject: [PATCH] Check execution workspace candidates with pinned artifact builders --- flake.nix | 1 + nix/check-execution.mjs | 28 +++++++++ nix/checked-candidate.nix | 71 ++++++++++++++++------ src/capability-language/authoring-check.ts | 9 ++- 4 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 nix/check-execution.mjs diff --git a/flake.nix b/flake.nix index f2fcab4..f805081 100644 --- a/flake.nix +++ b/flake.nix @@ -77,6 +77,7 @@ mkdir -p "$out" install -Dm644 nix/checked-package.nix "$out/share/checked-package.nix" install -Dm644 nix/checked-artifacts.nix "$out/share/checked-artifacts.nix" + install -Dm644 nix/check-execution.mjs "$out/share/check-execution.mjs" substitute nix/checked-candidate.nix "$out/share/checked-candidate.nix" \ --replace-fail '@nixpkgs@' '${pkgs.path}' cp --reflink=auto --recursive grammar "$out/grammar" diff --git a/nix/check-execution.mjs b/nix/check-execution.mjs new file mode 100644 index 0000000..af90822 --- /dev/null +++ b/nix/check-execution.mjs @@ -0,0 +1,28 @@ +// Validate compiler-owned plans and immutable artifact receipts before publishing check outputs. +import { readFile, writeFile } from "node:fs/promises"; +import { createHash } from "node:crypto"; +import { pathToFileURL } from "node:url"; +import { join } from "node:path"; +const [compilerPath, planPath, checksPath, output] = process.argv.slice(2); +const compiler = await import(pathToFileURL(compilerPath)); +const plan = JSON.parse(await readFile(planPath, "utf8")); +const checks = JSON.parse(await readFile(checksPath, "utf8")); +const roots = Object.create(null); +for (const check of checks) { + if (Object.hasOwn(roots, check.packageRevisionId)) throw Error("Duplicate checked package revision"); + roots[check.packageRevisionId] = check.artifactPath; +} +const checked = await compiler.loadCheckedArtifactWorld( + plan, + roots, + createHash("sha256") + .update(await readFile(compilerPath)) + .digest("hex"), +); +for (const [file, value] of [ + ["execution-world.json", checked.world], + ["kernel.json", compiler.compileKernelProgram(plan)], + ["queries.json", compiler.compileQueryProgram(plan)], + ["execution-artifacts.json", { codec: "camino-checked-execution-v1", roots, manifests: checked.manifests }], +]) + await writeFile(join(output, file), JSON.stringify(value) + "\n"); diff --git a/nix/checked-candidate.nix b/nix/checked-candidate.nix index fabe5b9..7fe5a4e 100644 --- a/nix/checked-candidate.nix +++ b/nix/checked-candidate.nix @@ -37,8 +37,18 @@ let entry: load (ancestors ++ [ (sourceKey source) ]) (entry.source // { inherit (entry) kind; }) ) lock.resources; in - source // { inherit directory children; }; + source // { inherit directory children lock; }; root = load [ ] { inherit kind repository commit; }; + configuration = + if builtins.pathExists (root.directory + "/quixos.check.json") then + builtins.fromJSON (builtins.readFile (root.directory + "/quixos.check.json")) + else + { }; + execution = (configuration.backend or "typescript") == "execution"; + runtime = builtins.getFlake ( + "git+${root.lock.quixos.repository}?rev=${root.lock.quixos.commit}&ref=refs/tags/quixos-reachability/${root.lock.quixos.commit}" + ); + runtimePackages = runtime.packages.${system}; flatten = node: [ node ] ++ pkgs.lib.concatMap flatten node.children; nodes = builtins.attrValues ( builtins.listToAttrs ( @@ -70,7 +80,8 @@ let ${protocol}/bin/quixos-workspace-compile --root ${node.directory} \ --source-root-commit ${pkgs.lib.escapeShellArg node.commit} \ --checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots} \ - --graph-out "$out/graph.json" --schemas-out "$out/package-schemas.json" > "$out/candidate.json" + --graph-out "$out/graph.json" --schemas-out "$out/package-schemas.json" \ + ${pkgs.lib.optionalString execution ''--execution-plan-out "$out/execution-plan.json"''} > "$out/candidate.json" '' else '' @@ -92,21 +103,37 @@ let "git+${node.repository}?rev=${node.commit}&ref=refs/tags/quixos-reachability/${node.commit}" ); checked = - package.quixosPackages.${system}.checkedServer - or (throw "Package ${node.repository} lacks checkedServer; use the supported package scaffold."); - artifact = checked { - schema = - if kind == "workspace" then - pkgs.writeText "package-specialization-schema.json" ( - builtins.toJSON - (builtins.fromJSON (builtins.readFile "${contract}/package-schemas.json")) - .${candidate.revision.revisionId} - ) - else - "${compiled}/bindings.json"; - generator = protocol; - packageRevisionId = candidate.revision.revisionId; - }; + if execution then + package.quixosPackages.${system}.checkedArtifacts + or (throw "Package ${node.repository} lacks checkedArtifacts; execution workspaces cannot import legacy servers.") + else + package.quixosPackages.${system}.checkedServer + or (throw "Package ${node.repository} lacks checkedServer; use the supported package scaffold."); + artifact = checked ( + if execution then + { + plan = "${contract}/execution-plan.json"; + generator = protocol; + core = runtimePackages.camino-replica-core; + sharedRuntime = runtimePackages.camino-shared-runtime; + typescript = runtimePackages.camino-replica-core.dependencies; + packageRevisionId = candidate.revision.revisionId; + } + else + { + schema = + if kind == "workspace" then + pkgs.writeText "package-specialization-schema.json" ( + builtins.toJSON + (builtins.fromJSON (builtins.readFile "${contract}/package-schemas.json")) + .${candidate.revision.revisionId} + ) + else + "${compiled}/bindings.json"; + generator = protocol; + packageRevisionId = candidate.revision.revisionId; + } + ); in { packageRevisionId = candidate.revision.revisionId; @@ -124,11 +151,21 @@ let ) ); in +assert pkgs.lib.assertMsg (!execution || kind == "workspace") + "Execution packages must be checked in their complete workspace; run qx-workspace check from the workspace root."; pkgs.runCommand (if contractOnly then "qx-contract" else "qx-checked-candidate") { } '' mkdir -p "$out" cp ${contract}/candidate.json "$out/candidate.json" cp ${contract}/graph.json "$out/graph.json" cp ${manifest} "$out/checks.json" + ${pkgs.lib.optionalString execution '' + cp ${contract}/execution-plan.json "$out/execution-plan.json" + ${pkgs.lib.optionalString (!contractOnly) '' + ${pkgs.nodejs_24}/bin/node ${./check-execution.mjs} \ + ${protocol}/libexec/quixos-protocol/execution-world.mjs \ + "$out/execution-plan.json" "$out/checks.json" "$out" + ''} + ''} ${pkgs.lib.optionalString ( kind != "workspace" ) ''cp ${contract}/bindings.json "$out/bindings.json"''} diff --git a/src/capability-language/authoring-check.ts b/src/capability-language/authoring-check.ts index eb0a94a..8c696ad 100644 --- a/src/capability-language/authoring-check.ts +++ b/src/capability-language/authoring-check.ts @@ -34,6 +34,7 @@ export async function checkAuthoring( phase: string; output: string; compilation?: "passed"; + runtime?: "execution"; activationReadiness?: "preserve" | "migration-required" | "blocked"; migrationRequired?: string[]; } = { @@ -98,7 +99,13 @@ export async function checkAuthoring( const candidateText = await fs.readFile(path.join(report.artifactPath, "candidate.json"), "utf8"); await fs.writeFile(path.join(output, "candidate.json"), candidateText); report.compilation = "passed"; - if (resource.kind === "workspace" && !options.contractOnly) { + try { + await fs.access(path.join(report.artifactPath, "execution-plan.json")); + report.runtime = "execution"; + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } + if (resource.kind === "workspace" && !options.contractOnly && report.runtime !== "execution") { report.phase = "evolution"; await progress(); let baseline = options.baseline;