Files
workinf_Blender_Wasm/tools/web/capture-corrective-baseline.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

91 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)), "../..");
const read = (relative) => fs.readFileSync(path.join(root, relative), "utf8");
const sha256 = (relative) => crypto.createHash("sha256").update(read(relative)).digest("hex");
const parseJson = (relative) => JSON.parse(read(relative));
const relative = (absolute) => path.relative(root, absolute).replaceAll(path.sep, "/");
function argument(name) {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] : undefined;
}
function queueCurrentTask() {
const source = read("docs/EXECUTION_QUEUE.md");
return source.match(/\|\s*当前任务\s*\|\s*`([^`]+)`/u)?.[1]
?? source.match(/当前任务\s*[:]\s*`([^`]+)`/u)?.[1];
}
function countLegacyEvidence() {
const rootPath = path.join(root, "tests/golden");
return fs.readdirSync(rootPath, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && /^M16-GAP-\d{5}$/u.test(entry.name))
.length;
}
const task = argument("--task") ?? queueCurrentTask();
const output = argument("--output");
if (!task || !output) {
throw new Error("usage: node tools/web/capture-corrective-baseline.mjs --task M16-GAP-xxxxx --output path");
}
const queue = read("docs/EXECUTION_QUEUE.md");
const parentManifestPath = queue.match(/\|\s*parent manifest\s*\|\s*`([^`]+)`/iu)?.[1];
if (!parentManifestPath) throw new Error("EXECUTION_QUEUE.md does not declare parent manifest");
const parentManifest = parseJson(parentManifestPath);
if (queueCurrentTask() !== task) throw new Error(`queue current task mismatch: ${queueCurrentTask()} != ${task}`);
if (parentManifest.nextTask !== task) throw new Error(`parent nextTask mismatch: ${parentManifest.nextTask} != ${task}`);
const parentStatusPath = `docs/status/${parentManifest.task}.md`;
if (!fs.existsSync(path.join(root, parentStatusPath))) throw new Error(`missing parent status: ${parentStatusPath}`);
const plan = parseJson("tests/golden/M15-03A/next-task-plan.json");
const catalog = read("tests/golden/M15-03A/task-catalog.jsonl");
const activeIds = catalog.split("\n").filter(Boolean).map((line) => JSON.parse(line))
.filter((entry) => entry.state === "active").map((entry) => entry.id);
const sourcePaths = [
"docs/EXECUTION_QUEUE.md",
parentManifestPath,
parentStatusPath,
"tests/golden/M15-03A/completed-gap-tasks.json",
"tests/golden/M15-03A/task-catalog.jsonl",
"tests/golden/M15-03A/task-index.json",
"tests/golden/M15-03A/next-task-plan.json",
];
const baseline = {
schemaVersion: 1,
task,
queue: {
currentTask: queueCurrentTask(),
parentManifest: parentManifestPath,
parentNextTask: parentManifest.nextTask,
},
generatedState: {
catalogActiveTask: activeIds.length === 1 ? activeIds[0] : null,
catalogActiveIds: activeIds,
planFirstTask: plan.firstTask,
planNextTask: plan.nextTask,
completionCount: parseJson("tests/golden/M15-03A/completed-gap-tasks.json").length,
legacyEvidenceDirectoryCount: countLegacyEvidence(),
},
sourceHashes: Object.fromEntries(sourcePaths.map((sourcePath) => [sourcePath, sha256(sourcePath)])),
invariants: {
queueMutationAllowed: false,
parentPointerMatchesCurrent: parentManifest.nextTask === task,
exactlyOneCatalogActive: activeIds.length === 1,
catalogActiveMatchesPlan: activeIds[0] === plan.firstTask,
},
};
if (!baseline.invariants.parentPointerMatchesCurrent || !baseline.invariants.exactlyOneCatalogActive || !baseline.invariants.catalogActiveMatchesPlan) {
throw new Error(`generated state is inconsistent: ${JSON.stringify(baseline.invariants)}`);
}
const outputPath = path.resolve(root, output);
if (!outputPath.startsWith(`${root}${path.sep}`)) throw new Error("baseline output must stay inside the repository");
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, `${JSON.stringify(baseline, null, 2)}\n`);
process.stdout.write(`corrective-baseline-ok task=${task} active=${activeIds[0]} parentNext=${parentManifest.nextTask} output=${relative(outputPath)}\n`);