Reorganize Blender Web execution around capabilities
This commit is contained in:
89
tools/web/check-corrective-resign.mjs
Normal file
89
tools/web/check-corrective-resign.mjs
Normal file
@@ -0,0 +1,89 @@
|
||||
import assert from "node:assert/strict";
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const arg = (name) => { const index = process.argv.indexOf(name); return index >= 0 ? process.argv[index + 1] : undefined; };
|
||||
const staged = path.resolve(root, arg("--staged-root") ?? "tests/golden/corrective/C0-002/staged");
|
||||
const reportPath = path.resolve(root, arg("--report") ?? "tests/golden/corrective/C0-002/resign-check.json");
|
||||
const read = (file) => fs.readFileSync(file, "utf8");
|
||||
const json = (file) => JSON.parse(read(file));
|
||||
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
||||
const relative = (file) => path.relative(root, file).replaceAll(path.sep, "/");
|
||||
const check = [];
|
||||
const ok = (name, value, detail = {}) => { assert.ok(value, name); check.push({ name, status: "PASS", ...detail }); };
|
||||
|
||||
const registryPath = path.join(staged, "completed-gap-tasks.candidate.json");
|
||||
const planPath = path.join(staged, "next-task-plan.candidate.json");
|
||||
const indexPath = path.join(staged, "task-index", "task-index.json");
|
||||
const catalogPath = path.join(staged, "task-index", "task-catalog.jsonl");
|
||||
const snapshotPath = path.join(staged, "source-snapshot", "snapshot-manifest.json");
|
||||
const registry = json(registryPath);
|
||||
const plan = json(planPath);
|
||||
const index = json(indexPath);
|
||||
const snapshot = json(snapshotPath);
|
||||
ok("registry-count", registry.length === 273, { value: registry.length });
|
||||
ok("registry-unique", new Set(registry).size === registry.length);
|
||||
ok("plan-shape", plan.tasks.length === 6900 && plan.summary.completed === 273 && plan.summary.active === 1 && plan.firstTask === "M16-GAP-00274");
|
||||
ok("plan-source-hash", plan.sources.completions.sha256 === sha256(registryPath));
|
||||
ok("index-plan-hash", index.source.sha256 === sha256(planPath));
|
||||
ok("index-catalog-hash", index.catalog.sha256 === sha256(catalogPath));
|
||||
ok("index-active", index.activeTask === plan.firstTask);
|
||||
ok("snapshot-nonempty", snapshot.files.length > 0, { value: snapshot.files.length });
|
||||
|
||||
const snapshotByPath = new Map();
|
||||
for (const file of snapshot.files) {
|
||||
const source = path.join(root, file.path);
|
||||
const copy = path.join(root, file.snapshotPath);
|
||||
assert.equal(sha256(source), file.sha256, file.path);
|
||||
assert.equal(sha256(copy), file.sha256, file.snapshotPath);
|
||||
snapshotByPath.set(file.path, file.sha256);
|
||||
}
|
||||
check.push({ name: "snapshot-hashes", status: "PASS", files: snapshot.files.length, bytes: snapshot.files.reduce((sum, file) => sum + file.bytes, 0) });
|
||||
|
||||
const manifestRoot = path.join(staged, "resigned-manifests");
|
||||
const manifestTasks = fs.readdirSync(manifestRoot).filter((entry) => /^M(?:15|16)-/u.test(entry)).sort();
|
||||
const m16Tasks = manifestTasks.filter((entry) => /^M16-GAP-\d{5}$/u.test(entry));
|
||||
ok("resigned-manifest-count", m16Tasks.length === 274, { value: m16Tasks.length });
|
||||
let doneCount = 0;
|
||||
for (const task of m16Tasks) {
|
||||
const manifest = json(path.join(manifestRoot, task, "manifest.json"));
|
||||
if (manifest.status === "done") doneCount += 1;
|
||||
for (const artifact of Object.values(manifest.artifacts ?? {})) {
|
||||
if (!artifact?.path || !artifact?.sha256) continue;
|
||||
assert.equal(snapshotByPath.get(artifact.path), artifact.sha256, `${task}:${artifact.path}`);
|
||||
}
|
||||
}
|
||||
ok("resigned-manifest-done-count", doneCount === 273, { value: doneCount });
|
||||
const rootParent = json(path.join(manifestRoot, "M15-03E", "manifest.json"));
|
||||
ok("root-parent-repaired", rootParent.nextTask === "M16-GAP-00001");
|
||||
|
||||
const contextRoot = path.join(staged, "resigned-context");
|
||||
const contexts = fs.readdirSync(contextRoot).filter((entry) => /^M16-GAP-\d{5}$/u.test(entry)).sort();
|
||||
ok("repaired-context-count", contexts.length === 24, { value: contexts.length });
|
||||
for (const task of contexts) {
|
||||
const context = json(path.join(contextRoot, task, "task-context.json"));
|
||||
assert.equal(context.task, task);
|
||||
assert.equal(context.parentTask, json(path.join(manifestRoot, task, "manifest.json")).parentTask);
|
||||
const parent = json(path.join(manifestRoot, context.parentTask, "manifest.json"));
|
||||
assert.equal(parent.nextTask, task, `${task}:parent-nextTask`);
|
||||
}
|
||||
check.push({ name: "repaired-context-links", status: "PASS", files: contexts.length });
|
||||
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "corrective-resign-check-"));
|
||||
const regenerated = path.join(tempRoot, "plan.json");
|
||||
try {
|
||||
execFileSync(process.execPath, [path.join(root, "tools/web/generate-blender-next-task-plan.mjs"), regenerated, "--completion-path", registryPath], { cwd: root, stdio: "ignore" });
|
||||
ok("candidate-plan-deterministic", fs.readFileSync(regenerated).equals(fs.readFileSync(planPath)));
|
||||
} finally {
|
||||
fs.rmSync(tempRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const result = { schemaVersion: 1, operation: "CORRECTIVE_STATE_RECONCILIATION_RESIGN_CHECK", status: "PASS", queueMutation: false, stagedRoot: relative(staged), checks: check, artifactCount: snapshot.files.length, planTaskCount: plan.tasks.length, completedCount: doneCount, activeTask: plan.firstTask };
|
||||
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
|
||||
fs.writeFileSync(reportPath, `${JSON.stringify(result, null, 2)}\n`);
|
||||
process.stdout.write(`corrective-resign-check-ok files=${snapshot.files.length} manifests=${m16Tasks.length} contexts=${contexts.length} active=${plan.firstTask}\n`);
|
||||
Reference in New Issue
Block a user