Files
workinf_Blender_Wasm/tools/web/authorize-wbv2-p1142.mjs
mes123456 f3e21c6b98
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
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

77 lines
11 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 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 abs = (file) => path.join(root, file);
const read = (file) => fs.readFileSync(abs(file), "utf8");
const json = (file) => JSON.parse(read(file));
const sha = (file) => crypto.createHash("sha256").update(fs.readFileSync(abs(file))).digest("hex");
const writeJson = (file, value) => { fs.mkdirSync(path.dirname(abs(file)), { recursive: true }); fs.writeFileSync(abs(file), `${JSON.stringify(value, null, 2)}\n`); };
const writeText = (file, value) => { fs.mkdirSync(path.dirname(abs(file)), { recursive: true }); fs.writeFileSync(abs(file), value); };
const run = (entry, ...args) => execFileSync(process.execPath, [abs(entry), ...args], { cwd: root, stdio: "inherit" });
const parentTask = "WBV2-P1141-001";
const rootTask = "WBV2-P1142-000";
const moduleTask = "WBV2-P1142-001";
const moduleId = "FCURVE_SAMPLE_READER";
const planPath = "tests/golden/WBV2-P1100-000/module-execution-plan.json";
const parentManifestPath = `tests/golden/${parentTask}/manifest.json`;
const dynamicPath = "tests/golden/WBV2/dynamic-inventory.json";
const sourcePaths = [
"blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp",
"web/protocol/scene-ir.ts",
"web/app/src/workers/web-engine.worker.ts",
"web/app/src/engine-client/WebEngineClient.ts",
"web/tests/e2e/wbv2-fcurve-sample-production.spec.ts",
"web/app/src/vendor/blender/web_engine.wasm",
];
const commands = [
"source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine -- -j8 && cp build_web_blender6/bin/web_engine.wasm web/app/src/vendor/blender/web_engine.wasm && cp build_web_blender6/bin/web_engine.wasm web/app/public/vendor/blender/web_engine.wasm",
"npm --prefix web run typecheck",
"npm --prefix web run build",
"WEB_TEST_PORT=5365 npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-fcurve-sample-production.spec.ts",
];
const queuePath = "docs/EXECUTION_QUEUE.md";
const queueTask = read(queuePath).match(/\| 当前任务 \| `([^`]+)` \|/u)?.[1];
assert.ok([parentTask, rootTask, moduleTask].includes(queueTask));
const parent = json(parentManifestPath);
assert.equal(parent.status, "done");
assert.ok(parent.nextTask === null || parent.nextTask === rootTask);
const plan = json(planPath);
assert.ok(["COMPLETED_MODULE_EXECUTION", "ACTIVE_MODULE_EXECUTION_QUEUE"].includes(plan.status));
const dynamic = json(dynamicPath);
const dynamicSummary = { path: dynamicPath, sourceTreeSha256: dynamic.authority.sourceTreeSha256, unitCount: dynamic.summary.unitCount, planningEstimate: dynamic.policy.planningEstimate, hardTarget: dynamic.policy.hardTarget, verifiedUnits: dynamic.coverage.verifiedUnits, unverifiedUnits: dynamic.coverage.unverifiedUnits };
const module = { moduleId, sourceBacked: true, capabilities: ["CAP-FCURVE-SAMPLE-READER", "CAP-FCURVE-SAMPLE-RECOVERY", "CAP-FCURVE-SAMPLE-FAIL-CLOSED"], sourcePaths, reuseReceipts: [{ manifest: "tests/golden/WBV2-P1141-001/manifest.json", report: "tests/golden/WBV2-P1141-001/module-receipt.json" }], positive: ["FCurveSample Main reader exposes sampled source and values", "four native FPoint samples remain ordered", "save/reopen preserves sampled data"], negative: ["malformed blend is rejected without Main mutation", "missing sampled payload is fail-closed", "non-finite sampled value is not synthesized"], recovery: ["undo", "redo", "save/reopen", "worker termination and deterministic sampled state"], commands, fullFamilyParity: false };
const rootRecord = { id: rootTask, phase: "1142", title: "authorize_fcurve_sample_reader_module", kind: "enabling", owner: "ANIMATION", dependsOn: [parentTask], parityId: null, implementationClass: "QUEUE_ONLY", parallelGroup: "module-root-1142", gate: "MODULE", card: `docs/tasks/${rootTask}.md`, behavior: "Authorize a source-backed FCurveSample reader and recovery module after P1141.", outputs: ["successor authorization", "module plan", "manifest", "status"], acceptance: ["P1141 evidence remains hash-bound", "FCurveSample has explicit Blender source mapping", "inventory remains source-derived", "one source-backed module is active"], commands: ["node tools/web/check-execution-control-plane.mjs"], status: "done", ready: false, queueMutationAllowed: true };
const moduleRecord = { id: moduleTask, phase: "1142", title: "verify_fcurve_sample_reader_module", kind: "module", owner: "ANIMATION", dependsOn: [rootTask], parityId: moduleId, implementationClass: "LOCAL_EXACT", parallelGroup: "module-fcurve_sample_reader", gate: "MODULE", card: `docs/tasks/${moduleTask}.md`, behavior: "Verify FCurveSample data through Blender Main reader, protocol and Chromium recovery.", outputs: ["module receipt", "negative matrix", "manifest", "status"], acceptance: ["source and protocol hashes are bound", "positive Main reader is proven", "negative no-mutation and recovery are proven", "Chromium and save/reopen evidence are exact", "full-family parity remains explicit"], status: "in_progress", ready: true, queueMutationAllowed: true, module };
plan.status = "ACTIVE_MODULE_EXECUTION_QUEUE";
if (!plan.modules.some((entry) => entry.id === moduleId)) plan.modules.push({ id: moduleId, task: moduleTask, owner: "ANIMATION", sourceBacked: true, capabilities: module.capabilities, reusedReceipts: module.reuseReceipts, fullFamilyParity: false });
if (!plan.tasks.some((entry) => entry.id === rootTask)) plan.tasks.push(rootRecord);
if (!plan.tasks.some((entry) => entry.id === moduleTask)) plan.tasks.push(moduleRecord);
writeJson(planPath, plan);
run("tools/web/generate-v2-task-index.mjs", "--plan-path", abs(planPath));
parent.nextTask = rootTask;
parent.continuation = { authorized: true, root: rootTask, scope: moduleId, priorModule: parentTask };
parent.artifacts.plan.sha256 = sha(planPath);
writeJson(parentManifestPath, parent);
writeText(`docs/tasks/${rootTask}.md`, `# ${rootTask}: authorize FCurveSample reader\n\n- task: ${rootTask}\n- parent: ${parentTask}\n- status: done\n- ownerFamily: ANIMATION\n- targetImplementationClass: QUEUE_ONLY\n- Implementation entrypoint: \`node tools/web/authorize-wbv2-p1142.mjs\`\n\n## 目标\n\n授权一个 source-backed FCurveSample reader/recovery 模块。\n\n## 输入与范围\n\n- RNA: \`blender-5.2.0/source/blender/makesrna/intern/rna_fcurve.cc\` (FCurveSample)\n- Reader: \`blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp\`\n- Chromium: \`web/tests/e2e/wbv2-fcurve-sample-production.spec.ts\`\n\n## 验收\n\n- P1141 evidence stays hash-bound; one source-backed module active; malformed or hash-drift input remains \`in_progress\`.\n- \`node tools/web/check-execution-control-plane.mjs\`\n\n## 交付与回滚\n\n- 交付 P1142 manifests/status/receipt;回滚恢复 P1141 终态指针。\n`);
writeText(`docs/status/${rootTask}.md`, `# ${rootTask} status\n\n- task: \`${rootTask}\`; parent: \`${parentTask}\`; status: \`done\`\n- operation: \`AUTHORIZE_SOURCE_BACKED_MODULE\`; module: \`${moduleId}\`\n- P1141 evidence reused; historical 6900-entry inventory remains immutable\n- dynamic source inventory: \`${dynamicSummary.unitCount}\` units, verified \`${dynamicSummary.verifiedUnits}\`, unverified \`${dynamicSummary.unverifiedUnits}\`, planning estimate \`${dynamicSummary.planningEstimate}\` (non-binding)\n- nextTask: \`${moduleTask}\`\n`);
let updatedQueue = read(queuePath).replace(/\| 当前任务 \| `[^`]+` \|/u, `| 当前任务 | \`${rootTask}\` |`).replace(/\| parent manifest \| `[^`]+` \|/u, `| parent manifest | \`${parentManifestPath}\` |`).replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, `| 任务卡 | [\`tasks/${rootTask}.md\`](tasks/${rootTask}.md) |`);
writeText(queuePath, updatedQueue);
run("tools/web/check-task-context.mjs", "--task", rootTask, "--write");
const rootReportPath = `tests/golden/${rootTask}/successor-authorization-report.json`;
const rootStatusPath = `docs/status/${rootTask}.md`;
const rootContextPath = `tests/golden/${rootTask}/task-context.json`;
writeJson(rootReportPath, { schemaVersion: 1, task: rootTask, operation: "AUTHORIZE_SOURCE_BACKED_MODULE", status: "PASS", parent: { task: parentTask, manifest: parentManifestPath, manifestSha256: sha(parentManifestPath) }, successor: { task: moduleTask, moduleId, sourceBacked: true, noRepeatedImplementation: true }, inventory: { ...plan.inventory, boundCaseDelta: 0 }, dynamicInventory: { ...dynamicSummary, boundUnitDelta: 0 }, nextTask: moduleTask });
writeJson(`tests/golden/${rootTask}/manifest.json`, { schemaVersion: 1, task: rootTask, parentTask, operation: "AUTHORIZE_SOURCE_BACKED_MODULE", status: "done", enablingTask: true, parityStateChange: false, runtime: "MODULE_EXECUTION_QUEUE_BRIDGE", artifacts: { parentManifest: { path: parentManifestPath, sha256: sha(parentManifestPath) }, plan: { path: planPath, sha256: sha(planPath) }, report: { path: rootReportPath, sha256: sha(rootReportPath) }, status: { path: rootStatusPath, sha256: sha(rootStatusPath) }, taskContext: { path: rootContextPath, sha256: sha(rootContextPath) } }, assertions: { sourceBackedSuccessor: true, priorEvidencePreserved: true, inventoryTotal: 6900, effectiveCompleted: 290, effectivePending: 6609, boundCaseDelta: 0, dynamicInventory: dynamicSummary, fullHistoricalReproduction: false }, nextTask: moduleTask });
updatedQueue = read(queuePath).replace(/\| 当前任务 \| `[^`]+` \|/u, `| 当前任务 | \`${moduleTask}\` |`).replace(/\| parent manifest \| `[^`]+` \|/u, `| parent manifest | \`tests/golden/${rootTask}/manifest.json\` |`).replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, `| 任务卡 | [\`tasks/${moduleTask}.md\`](tasks/${moduleTask}.md) |`);
writeText(queuePath, updatedQueue);
writeText(`docs/tasks/${moduleTask}.md`, `# ${moduleTask}: verify FCurveSample reader\n\n- task: ${moduleTask}\n- parent: ${rootTask}\n- status: in_progress\n- gap: workflow:fcurve_sample_reader\n- ownerFamily: ANIMATION\n- targetImplementationClass: LOCAL_EXACT\n- moduleId: ${moduleId}\n- Implementation entrypoint: \`node tools/web/run-wbv2-module-task.mjs --task ${moduleTask} --write\`\n\n## 目标\n\n验证 Blender Main 的 FCurveSample sampled 数据在 Chromium recovery/save-reopen 中保持一致。\n\n## 输入与范围\n\n${sourcePaths.map((file) => `- \`${file}\``).join("\n")}\n- RNA: \`blender-5.2.0/source/blender/makesrna/intern/rna_fcurve.cc\` (FCurveSample)\n- Fixture: \`tests/files/web/generated/M16-GAP-00161-action-fcurve-samples.blend\`\n- Production test: \`web/tests/e2e/wbv2-fcurve-sample-production.spec.ts\`\n\n## 验收\n\n- Positive: ${module.positive.join(", ")}\n- Negative: ${module.negative.join(", ")}\n- Recovery: ${module.recovery.join(", ")}\n- Commands:\n${commands.map((command) => `- \`${command}\``).join("\n")}\n- JS simulation is forbidden; all behavior must come from Blender Main/WASM.\n\n## 交付与回滚\n\n- Receipt: \`tests/golden/${moduleTask}/module-receipt.json\`; Manifest: \`tests/golden/${moduleTask}/manifest.json\`; Status: \`docs/status/${moduleTask}.md\`.\n`);
run("tools/web/check-task-context.mjs", "--task", moduleTask, "--write");
run("tools/web/check-execution-control-plane.mjs");
process.stdout.write(`wbv2-successor-authorized root=${rootTask} next=${moduleTask} module=${moduleId}\n`);