Files
workinf_Blender_Wasm/tools/web/write-corrective-handoff.mjs
mes123456 a081c68c87
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions
Reorganize Blender Web execution around capabilities
2026-08-24 18:37:46 -04:00

75 lines
4.2 KiB
JavaScript

import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
function arg(name) {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] : undefined;
}
const task = arg("--task");
const status = arg("--status");
const gate = arg("--gate");
const next = arg("--next");
const evidenceRootArg = arg("--evidence-root");
const sourceHashesArg = arg("--source-hashes");
const commandExitCodesArg = arg("--command-exit-codes");
const resolutionArg = arg("--resolution");
const queueMutationArg = arg("--queue-mutation");
const queueMutation = queueMutationArg === undefined ? false : queueMutationArg === "true";
if (!task || !status || !gate || !next || !evidenceRootArg) {
throw new Error("usage: node tools/web/write-corrective-handoff.mjs --task C0-xxx --status done --gate G0 --next C0-xxx --evidence-root tests/golden/corrective/C0-xxx [--queue-mutation true]");
}
const evidenceRoot = path.resolve(root, evidenceRootArg);
if (!evidenceRoot.startsWith(`${root}${path.sep}`)) throw new Error("evidence root must stay inside the repository");
if (!fs.existsSync(evidenceRoot) || !fs.statSync(evidenceRoot).isDirectory()) throw new Error(`missing evidence root: ${evidenceRootArg}`);
const relative = (file) => path.relative(root, file).replaceAll(path.sep, "/");
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
const reports = ["dry-run.json", "write.json"]
.map((name) => path.join(evidenceRoot, name))
.filter((file) => fs.existsSync(file))
.map((file) => JSON.parse(fs.readFileSync(file, "utf8")));
const resolution = resolutionArg && fs.existsSync(path.resolve(root, resolutionArg)) ? JSON.parse(fs.readFileSync(path.resolve(root, resolutionArg), "utf8")) : null;
const inputDigests = [...new Set([...reports.map((report) => report.inputDigest), resolution?.inputDigest].filter(Boolean))];
if (inputDigests.length > 1 && !resolution) throw new Error("dry-run and write inputDigest differ");
const sourceHashValue = sourceHashesArg && fs.existsSync(path.resolve(root, sourceHashesArg)) ? JSON.parse(fs.readFileSync(path.resolve(root, sourceHashesArg), "utf8")) : resolution?.sourceHashes ?? reports.find((report) => report.sourceHashes)?.sourceHashes ?? null;
const sourceHashes = sourceHashValue?.sourceHashes ?? sourceHashValue;
const commandExitValue = commandExitCodesArg && fs.existsSync(path.resolve(root, commandExitCodesArg)) ? JSON.parse(fs.readFileSync(path.resolve(root, commandExitCodesArg), "utf8")) : resolution?.commandExitCodes ?? null;
const commandExitCodes = commandExitValue?.commandExitCodes ?? commandExitValue;
const artifacts = [];
const stack = [evidenceRoot];
while (stack.length) {
const directory = stack.pop();
for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
const file = path.join(directory, entry.name);
if (entry.isDirectory()) stack.push(file);
else if (entry.isFile() && entry.name !== "manifest.json" && entry.name !== "handoff.json") {
artifacts.push({ path: relative(file), sha256: sha256(file), bytes: fs.statSync(file).size });
}
}
}
artifacts.sort((a, b) => a.path.localeCompare(b.path));
const handoff = {
schemaVersion: 1,
task,
status,
gate,
queueMutation,
evidenceRoot: relative(evidenceRoot),
artifactCount: artifacts.length,
artifacts,
...(resolution?.inputDigest ? { inputDigest: resolution.inputDigest } : inputDigests.length === 1 ? { inputDigest: inputDigests[0] } : {}),
...(sourceHashes ? { sourceHashes } : {}),
...(commandExitCodes ? { commandExitCodes } : {}),
...(resolution ? { resolution: { status: resolution.status, writeOutcome: resolution.writeOutcome, authorization: resolution.authorization, candidate: resolution.candidate ?? null } } : {}),
nextCorrectiveTask: next,
};
fs.writeFileSync(path.join(evidenceRoot, "handoff.json"), `${JSON.stringify(handoff, null, 2)}\n`);
fs.writeFileSync(path.join(evidenceRoot, "manifest.json"), `${JSON.stringify(handoff, null, 2)}\n`);
process.stdout.write(`corrective-handoff-ok task=${task} status=${status} gate=${gate} artifacts=${artifacts.length} next=${next}\n`);