Files
workinf_Blender_Wasm/tools/web/authorize-wbv2-p1141.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

83 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-P1140-001";
const rootTask = "WBV2-P1141-000";
const moduleTask = "WBV2-P1141-001";
const moduleId = "ACTION_POSE_MARKERS_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-action-pose-markers-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=5359 npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-action-pose-markers-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-ACTION-POSE-MARKERS-READER", "CAP-ACTION-POSE-MARKERS-RECOVERY", "CAP-ACTION-POSE-MARKERS-FAIL-CLOSED"], sourcePaths,
reuseReceipts: [{ manifest: "tests/golden/WBV2-P1140-001/manifest.json", report: "tests/golden/WBV2-P1140-001/module-receipt.json" }],
positive: ["ActionPoseMarkers Main reader exposes marker names and frames", "three Blender pose markers remain ordered", "save/reopen preserves marker metadata"],
negative: ["malformed blend is rejected without Main mutation", "missing animation target is fail-closed", "non-finite marker frame is not synthesized"],
recovery: ["undo", "redo", "save/reopen", "worker termination and deterministic marker state"], commands, fullFamilyParity: false,
};
const rootRecord = { id: rootTask, phase: "1141", title: "authorize_action_pose_markers_module", kind: "enabling", owner: "ANIMATION", dependsOn: [parentTask], parityId: null, implementationClass: "QUEUE_ONLY", parallelGroup: "module-root-1141", gate: "MODULE", card: `docs/tasks/${rootTask}.md`, behavior: "Authorize a source-backed ActionPoseMarkers reader and recovery module after P1140.", outputs: ["successor authorization", "module plan", "manifest", "status"], acceptance: ["P1140 evidence remains hash-bound", "ActionPoseMarkers 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: "1141", title: "verify_action_pose_markers_reader_module", kind: "module", owner: "ANIMATION", dependsOn: [rootTask], parityId: moduleId, implementationClass: "LOCAL_EXACT", parallelGroup: "module-action_pose_markers_reader", gate: "MODULE", card: `docs/tasks/${moduleTask}.md`, behavior: "Verify ActionPoseMarkers metadata 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 ActionPoseMarkers 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-p1141.mjs\`\n\n## 目标\n\n授权一个 source-backed ActionPoseMarkers reader/recovery 模块。\n\n## 输入与范围\n\n- RNA: \`blender-5.2.0/source/blender/makesrna/intern/rna_action.cc\` (ActionPoseMarkers)\n- Reader: \`blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp\`\n- Chromium: \`web/tests/e2e/wbv2-action-pose-markers-production.spec.ts\`\n\n## 验收\n\n- P1140 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- 交付 P1141 manifests/status/receipt;回滚恢复 P1140 终态指针。\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- P1140 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 ActionPoseMarkers reader\n\n- task: ${moduleTask}\n- parent: ${rootTask}\n- status: in_progress\n- gap: workflow:action_pose_markers_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 的 ActionPoseMarkers 元数据在 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_action.cc\` (ActionPoseMarkers)\n- Fixture: \`tests/files/web/generated/M16-GAP-00161-action-pose-markers.blend\`\n- Production test: \`web/tests/e2e/wbv2-action-pose-markers-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`);