Make workspace authoring converge through immutable Nix candidates

Coordinate registered resource edits bottom-up into retained exact remote sources.
Use one Nix-owned source graph for provisional checking, template publication,
explicit baseline upgrades and host activation; retain independent runtime pins.

Add scoped contract inspection, historical recovery, derived worklists, crash-safe
locks, named dependency adoption and plain-QX structural editing. Repair TODO
ownership and template instantiation, and document the supported agent workflow.

Validated with protocol and command suites, real jj/Nix convergence and cache
checks, TS/React installed-command acceptance, and fresh TODO first-edit acceptance.
No live deployment or public publication performed. Props projection generation
and a one-command rich feature generator remain explicitly outside this delivery.
This commit is contained in:
Timothy J. Aveni
2026-09-14 12:25:47 -07:00
parent fae4e48f72
commit 01ca965c7f
29 changed files with 1103 additions and 75 deletions
+33
View File
@@ -1,10 +1,12 @@
import fs from "node:fs/promises";
import path from "node:path";
import {execFile as callback, spawn} from "node:child_process";
import { createWriteStream } from "node:fs";
import {promisify} from "node:util";
import {fileURLToPath} from "node:url";
const execFile = promisify(callback);
const environment = () => ({...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", GIT_TERMINAL_PROMPT: "0"});
export const checkerIdentity = () => process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
/** Checking snapshots jj, but never publishes or activates the working copy. */
export async function snapshotCommit(root: string): Promise<string> {
@@ -60,3 +62,34 @@ export async function buildCheckedPackage(source: string, schema: string, packag
});
});
}
/** Exact remote source DAG; the Nix checker owns schema construction and all
* nested fetches. Keep build noise in a named log, with a bounded failure tail. */
export async function buildImmutableCandidate(source: { repository: string; commit: string }, kind: "workspace" | "interface" | "package", logFile: string, contractOnly = false): Promise<string> {
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);
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error("Candidate origin must be credential-free HTTPS");
const generator = process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
if (!/^\/nix\/store\/[^/]+$/.test(generator)) throw new Error("Use the installed Quixos checker");
const builder = path.join(generator, "share/checked-candidate.nix");
await fs.access(builder);
const log = createWriteStream(logFile, { flags: "wx", mode: 0o600 });
await new Promise<void>((resolve, reject) => { log.once("open", () => resolve()); log.once("error", reject); });
return await new Promise((resolve, reject) => {
let output = "", tail = "", failure: Error | undefined;
const child = spawn("nix", ["build", "--impure", "--file", builder,
"--argstr", "repository", source.repository, "--argstr", "commit", source.commit,
"--argstr", "kind", kind, "--argstr", "generator", generator,
"--arg", "contractOnly", contractOnly ? "true" : "false",
"--no-link", "--print-out-paths", "-L"], { env: environment(), stdio: ["ignore", "pipe", "pipe"] });
log.on("error", error => { failure = error; child.kill(); });
child.stdout.on("data", chunk => { output += chunk; });
child.stderr.on("data", chunk => { log.write(chunk); tail = (tail + String(chunk)).slice(-6000); });
child.on("error", error => { failure = error; });
child.on("close", code => log.end(() => {
if (failure) reject(failure);
else if (code !== 0 || !/^\/nix\/store\/[a-z0-9]{32}-[^\s/]+$/.test(output.trim())) reject(new Error(`Candidate Nix check failed (${code}). Full log: ${logFile}\n${tail}`));
else resolve(output.trim());
}));
});
}