Files
workinf_Blender_Wasm/tools/web/generate-wbv2-material-animation-receipt.mjs
mes123456 2abe7cc220
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance WBV2 domain parity through P12
2026-08-28 06:38:27 +08:00

107 lines
10 KiB
JavaScript

import assert from "node:assert/strict";
import crypto from "node:crypto";
import fs from "node:fs";
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 write = process.argv.includes("--write");
const absolute = (file) => path.join(root, file);
const read = (file) => fs.readFileSync(absolute(file), "utf8");
const readJson = (file) => JSON.parse(read(file));
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(absolute(file))).digest("hex");
const outputRoot = "tests/golden/WBV2-P12-003";
const productionPath = `${outputRoot}/material-animation-production.json`;
const secondProductionPath = `${outputRoot}/material-animation-production.second.json`;
const reportPath = `${outputRoot}/material-animation-receipt-report.json`;
const statusPath = "docs/status/WBV2-P12-003.md";
const planPath = `${outputRoot}/domain-expansion-task-plan.json`;
const manifestPath = `${outputRoot}/manifest.json`;
const parentPath = "tests/golden/WBV2-P12-002/manifest.json";
const materialManifestPath = "tests/golden/WBV2-P3-001/manifest.json";
const materialReportPath = "tests/golden/WBV2-P3-001/material-report.json";
const animationManifestPath = "tests/golden/WBV2-P3-002/manifest.json";
const animationReportPath = "tests/golden/WBV2-P3-002/animation-report.json";
const parent = readJson(parentPath);
assert.equal(parent.task, "WBV2-P12-002");
assert.equal(parent.status, "done");
assert.equal(parent.nextTask, "WBV2-P12-003");
for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path);
for (const [manifestPathValue, reportPathValue] of [[materialManifestPath, materialReportPath], [animationManifestPath, animationReportPath]]) {
const manifest = readJson(manifestPathValue);
const report = readJson(reportPathValue);
assert.equal(manifest.status, "done");
assert.equal(report.desktop.status, "EXACT");
assert.equal(report.wasm.status, "EXACT");
assert.equal(report.saveReopen ?? report.wasm.saveReopen, "EXACT");
const bound = Object.values(manifest.artifacts ?? {}).find((artifact) => artifact.path === reportPathValue);
assert.ok(bound);
assert.equal(bound.sha256, sha256(reportPathValue));
}
assert.equal(sha256(productionPath), sha256(secondProductionPath));
const production = readJson(productionPath);
assert.equal(production.task, "WBV2-P12-003");
assert.equal(production.browser, "chromium");
assert.equal(production.queueMutation, false);
assert.equal(production.material.sourceUnsupportedNode, true);
assert.equal(production.material.finalRevision, production.material.baseRevision + 4);
assert.equal(production.material.activeUVMap, "WBV2P3001SecondaryUV");
assert.equal(production.material.invalidUV.code, "INVALID_ARGUMENT");
assert.equal(production.material.revisionAfterFailure, production.material.finalRevision);
assert.equal(production.material.saveReopenExact, true);
assert.equal(production.animation.insertedRevision, production.animation.baseRevision + 1);
assert.equal(production.animation.finalRevision, production.animation.baseRevision + 2);
assert.deepEqual(production.animation.locationFrames, [1, 10, 20, 25]);
assert.equal(production.animation.interpolation, "LINEAR");
assert.equal(production.animation.staleFrame.code, "INVALID_ARGUMENT");
assert.equal(production.animation.revisionAfterFailure, production.animation.finalRevision);
assert.equal(production.animation.saveReopenExact, true);
assert.ok(production.material.savedByteLength > 0);
assert.ok(production.animation.savedByteLength > 0);
const receipt = {
schemaVersion: 1,
task: "WBV2-P12-003",
operation: "MATERIAL_ANIMATION_ENGINE_RECEIPT",
status: "PASS",
browser: "chromium",
queueMutation: false,
source: { material: { task: "WBV2-P3-001", manifest: materialManifestPath, manifestSha256: sha256(materialManifestPath), report: materialReportPath, reportSha256: sha256(materialReportPath) }, animation: { task: "WBV2-P3-002", manifest: animationManifestPath, manifestSha256: sha256(animationManifestPath), report: animationReportPath, reportSha256: sha256(animationReportPath) }, reusedReadOnly: true },
production: { report: productionPath, reportSha256: sha256(productionPath), secondReport: secondProductionPath, secondReportSha256: sha256(secondProductionPath), deterministic: true, material: { materialId: production.material.materialId, meshId: production.material.meshId, revisions: [production.material.baseRevision, production.material.finalRevision], activeUVMap: production.material.activeUVMap, baseColor: production.material.baseColor, roughness: production.material.roughness, metallic: production.material.metallic, unsupportedNodeRetained: production.material.sourceUnsupportedNode, invalidUVRejected: true, savedByteLength: production.material.savedByteLength, saveReopen: "EXACT" }, animation: { animationId: production.animation.animationId, revisions: [production.animation.baseRevision, production.animation.insertedRevision, production.animation.finalRevision], channelCount: production.animation.channelCount, locationFrames: production.animation.locationFrames, interpolation: production.animation.interpolation, staleFrameRejected: true, savedByteLength: production.animation.savedByteLength, saveReopen: "EXACT" } },
assertions: { materialMainChanged: true, animationMainChanged: true, sourceHashesBound: true, unsupportedGraphExplicit: true, staleFrameRejected: true, saveReopenExact: true, historicalReproduction: false },
nextTask: "WBV2-P12-004",
};
if (!write) { process.stdout.write("wbv2-material-animation-receipt-ok task=WBV2-P12-003 material=EXACT animation=EXACT saveReopen=EXACT deterministic=true next=WBV2-P12-004\n"); process.exit(0); }
fs.mkdirSync(absolute(outputRoot), { recursive: true });
fs.writeFileSync(absolute(reportPath), `${JSON.stringify(receipt, null, 2)}\n`);
fs.writeFileSync(absolute(statusPath), `# WBV2-P12-003 status\n\n- task: \`WBV2-P12-003\`; parent: \`WBV2-P12-002\`; status: \`done\`\n- material/UV: \`4\` Main mutations; Principled values and UV map persisted\n- animation/TRS: keyframe frame \`25\` + interpolation \`LINEAR\`; persisted\n- save/reopen: \`EXACT\`; invalid UV and stale frame rejected\n- existing P3 source receipts reused read-only; no duplicate historical run\n- nextTask: \`WBV2-P12-004\`\n\nThe material and animation workflows are now bound to the compiled WebEngine WASM Main in Chromium. Unsupported material nodes remain explicit, and stale or malformed authoring commands do not mutate Main.\n`);
const previousPlan = readJson("tests/golden/WBV2-P12-002/domain-expansion-task-plan.json");
const nextPlan = structuredClone(previousPlan);
nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P12-002/domain-expansion-task-plan.json", sha256: sha256("tests/golden/WBV2-P12-002/domain-expansion-task-plan.json") };
nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P12-003" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P12-004" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false });
fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`);
const nextCardPath = "docs/tasks/WBV2-P12-004.md";
fs.writeFileSync(absolute(nextCardPath), read(nextCardPath).replace("- status: planned", "- status: in_progress"));
execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", absolute(planPath)], { cwd: root, stdio: "inherit" });
execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P12-003", "--write"], { cwd: root, stdio: "inherit" });
const artifactPaths = { parentManifest: parentPath, materialManifest: materialManifestPath, materialReport: materialReportPath, animationManifest: animationManifestPath, animationReport: animationReportPath, production: productionPath, productionSecond: secondProductionPath, e2e: "web/tests/e2e/wbv2-material-animation-production.spec.ts", client: "web/app/src/engine-client/WebEngineClient.ts", nativeApi: "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", generator: "tools/web/generate-wbv2-material-animation-receipt.mjs", report: reportPath, status: statusPath, parityPlan: planPath, taskContext: `${outputRoot}/task-context.json` };
const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }]));
const manifest = { schemaVersion: 1, task: "WBV2-P12-003", parentTask: "WBV2-P12-002", operation: "MATERIAL_ANIMATION_ENGINE_RECEIPT", status: "done", enablingTask: false, parityStateChange: true, runtime: "PLAYWRIGHT_CHROMIUM_WEBENGINE_WASM", artifacts, assertions: { materialMutations: 4, animationMutations: 2, saveReopen: "EXACT", negativeCases: 2, unsupportedGraphExplicit: true, deterministic: true, historicalReproduction: false }, nextTask: "WBV2-P12-004" };
fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`);
let queue = read("docs/EXECUTION_QUEUE.md");
queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P12-004` |")
.replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P12-003/manifest.json` |")
.replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P12-004.md`](tasks/WBV2-P12-004.md) |");
fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue);
let nextTaskFile = read("nextTask.md");
nextTaskFile = nextTaskFile.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | `WBV2-P12-004` |")
.replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | `tests/golden/WBV2-P12-003/manifest.json` |")
.replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | `docs/tasks/WBV2-P12-004.md` |")
.replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | `WBV2-P12-005` |")
.replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | `npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-viewport-nonmesh-production.spec.ts` |");
fs.writeFileSync(absolute("nextTask.md"), nextTaskFile);
process.stdout.write(`wbv2-material-animation-receipt-written task=WBV2-P12-003 material=EXACT animation=EXACT saveReopen=EXACT next=WBV2-P12-004 artifacts=${Object.keys(artifacts).length}\n`);