Files
workinf_Blender_Wasm/tools/web/generate-wbv2-capability-continuation-slice.mjs
mes123456 ec65b51c5d
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
feat(web): wire Blender camera panorama controls
2026-08-30 22:00:19 +08:00

135 lines
26 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 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 argIndex = process.argv.indexOf("--task");
const task = argIndex >= 0 ? process.argv[argIndex + 1] : "WBV2-P44-001";
const match = task.match(/^WBV2-P(\d+)-(00[1-3])$/u);
if (!match) throw new Error(`unsupported capability continuation task: ${task}`);
const phase = Number(match[1]);
const index = Number(match[2]);
const queue = read("docs/EXECUTION_QUEUE.md");
const current = queue.match(/\| 当前任务 \| `(WBV2-P\d+-\d+)` \|/u)?.[1];
assert.equal(current, task);
const parentPath = queue.match(/\| parent manifest \| `([^`]+)` \|/u)?.[1];
if (!parentPath) throw new Error("capability continuation cannot resolve parent manifest");
const parent = json(parentPath);
assert.equal(parent.status, "done");
assert.equal(parent.nextTask, task);
const commands = { 1: [process.execPath, "tools/web/build-wbv2-capability-implementation-index.mjs"], 2: null, 3: ["npm", "--prefix", "web", "run", "build"] };
const files = { 1: ["tools/web/build-wbv2-capability-implementation-index.mjs", "tests/golden/WBV2-P27-001/verification-batch-plan.json"], 2: phase >= 59 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp"] : phase >= 57 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp"] : phase >= 56 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp"] : phase >= 55 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp"] : phase >= 54 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp"] : phase >= 53 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp"] : phase >= 52 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp"] : phase >= 51 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp"] : phase >= 50 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp"] : phase >= 49 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp"] : phase >= 47 ? ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "web/protocol/web-engine.ts"] : ["web/app/src/app/App.tsx", "web/protocol/web-engine-capabilities.ts"], 3: ["web/protocol/web-engine-capabilities.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/vendor/blender/web_engine.wasm"] };
const markers = { 1: "BUILD_CAPABILITY_FAMILY_IMPLEMENTATION_INDEX", 2: phase >= 71 ? "web_engine_blend_main_set_scene_view_layer_pass_settings" : phase >= 70 ? "web_engine_blend_main_set_scene_line_settings" : phase >= 69 ? "web_engine_blend_main_set_scene_stamp_settings" : phase >= 68 ? "web_engine_blend_main_set_scene_multiview_settings" : phase >= 67 ? "web_engine_blend_main_set_scene_sampling_settings" : phase >= 66 ? "web_engine_blend_main_set_scene_performance_settings" : phase >= 65 ? "web_engine_blend_main_set_scene_compositor_settings" : phase >= 64 ? "web_engine_blend_main_set_scene_hair_settings" : phase >= 63 ? "web_engine_blend_main_set_scene_motion_blur" : phase >= 62 ? "web_engine_blend_main_set_scene_image_format" : phase >= 61 ? "web_engine_blend_main_set_scene_color_management" : phase >= 60 ? "web_engine_blend_main_set_scene_simplify_settings" : phase >= 59 ? "web_engine_blend_main_set_scene_border_settings" : phase >= 58 ? "web_engine_blend_main_set_scene_collection_settings" : phase >= 57 ? "web_engine_blend_main_set_scene_view_layer_settings" : phase >= 56 ? "web_engine_blend_main_set_scene_bindings" : phase >= 55 ? "web_engine_blend_main_set_scene_pipeline_settings" : phase >= 54 ? "web_engine_blend_main_set_scene_timeline_settings" : phase >= 53 ? "web_engine_blend_main_set_scene_unit_settings" : phase >= 52 ? "web_engine_blend_main_set_scene_audio_settings" : phase >= 51 ? "web_engine_blend_main_set_scene_render_settings" : phase >= 50 ? "web_engine_blend_main_set_scene_properties" : phase >= 49 ? "web_engine_blend_main_set_physics_simulation" : phase >= 47 ? "web_engine_blend_main_set_geometry_node_graph" : "capability-family-workbench", 3: "WEB_ENGINE_COMMAND_CAPABILITIES" };
if (phase === 606) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_camera_panorama";
}
else if (phase === 605) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_camera_focus_object";
}
else if (phase === 604) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_world_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_world_environment_strength";
}
else if (phase === 603) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_world_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_world_probe_controls";
}
else if (phase === 602) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_world_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_world_lighting_controls";
}
else if (phase === 601) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_light_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_clip_start";
}
else if (phase === 600) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_light_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_area_size_z";
}
else if (phase === 599) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_light_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_cascade_controls";
}
else if (phase === 598) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "blender-5.2.0/source/blender/makesdna/DNA_light_types.h", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_shadow_controls";
}
else if (phase === 597) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/scene-ir.ts", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_contribution_factors";
}
else if (phase === 596) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_area_shape";
}
else if (phase === 595) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts", "web/app/src/workers/web-engine.worker.ts", "web/app/src/app/App.tsx", "web/app/src/vendor/blender/web_engine.wasm", "web/app/public/vendor/blender/web_engine.wasm"];
markers[2] = "web_engine_blend_main_set_light_type";
}
else if (phase >= 77) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts"];
markers[2] = "web_engine_blend_main_set_world_lightgroup";
}
else if (phase >= 76) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts"];
markers[2] = "web_engine_blend_main_set_object_lightgroup";
}
else if (phase >= 75) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts"];
markers[2] = "web_engine_blend_main_set_scene_view_layer_lightgroups";
}
else if (phase >= 74) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.h", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", "web/protocol/web-engine.ts"];
markers[2] = "web_engine_blend_main_set_scene_view_layer_aovs";
}
else if (phase >= 73) {
files[2] = ["blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_api.h", "blender-5.2.0/source/blender/web_engine/CMakeLists.txt", "web/app/src/workers/web-engine.worker.ts"];
markers[2] = "web_engine_call_json";
}
for (const file of files[index]) assert.ok(fs.existsSync(abs(file)), file);
assert.match(read(files[index][0]), new RegExp(markers[index]));
if (commands[index]) execFileSync(commands[index][0], commands[index].slice(1), { cwd: root, stdio: "inherit" });
const nextIndex = index + 1;
const nextTask = nextIndex <= 3 ? `WBV2-P${phase}-${String(nextIndex).padStart(3, "0")}` : `WBV2-P${phase + 1}-000`;
const family = index === 1 ? "INVENTORY.CAPABILITY_BATCH_INDEX" : index === 2 && phase >= 71 ? "RENDER.VIEW_LAYER_PASSES" : index === 2 && phase >= 70 ? "RENDER.LINE_SETTINGS" : index === 2 && phase >= 69 ? "RENDER.STAMP_SETTINGS" : index === 2 && phase >= 68 ? "RENDER.MULTIVIEW_SETTINGS" : index === 2 && phase >= 67 ? "RENDER.SAMPLING_SETTINGS" : index === 2 && phase >= 66 ? "PERFORMANCE.RENDER_SETTINGS" : index === 2 && phase >= 65 ? "COMPOSITOR.EXECUTION_SETTINGS" : index === 2 && phase >= 64 ? "CORE_SCENE_GRAPH.HAIR_SETTINGS" : index === 2 && phase >= 63 ? "CORE_SCENE_GRAPH.MOTION_BLUR" : index === 2 && phase >= 62 ? "CORE_SCENE_GRAPH.IMAGE_FORMAT" : index === 2 && phase >= 61 ? "CORE_SCENE_GRAPH.COLOR_MANAGEMENT" : index === 2 && phase >= 60 ? "CORE_SCENE_GRAPH.SIMPLIFY_SETTINGS" : index === 2 && phase >= 59 ? "CORE_SCENE_GRAPH.BORDER_SETTINGS" : index === 2 && phase >= 58 ? "CORE_SCENE_GRAPH.COLLECTION_SETTINGS" : index === 2 && phase >= 57 ? "CORE_SCENE_GRAPH.VIEW_LAYER_SETTINGS" : index === 2 && phase >= 56 ? "CORE_SCENE_GRAPH.BINDINGS" : index === 2 && phase >= 55 ? "CORE_SCENE_GRAPH.PIPELINE_SETTINGS" : index === 2 && phase >= 54 ? "CORE_SCENE_GRAPH.TIMELINE_SETTINGS" : index === 2 && phase >= 53 ? "CORE_SCENE_GRAPH.UNIT_SETTINGS" : index === 2 && phase >= 52 ? "CORE_SCENE_GRAPH.AUDIO_SETTINGS" : index === 2 && phase >= 51 ? "CORE_SCENE_GRAPH.RENDER_SETTINGS" : index === 2 && phase >= 50 ? "CORE_SCENE_GRAPH.SCENE_PROPERTIES" : index === 2 && phase >= 49 ? "SIMULATION.NATIVE_MANIFEST_BINDING" : index === 2 && phase >= 47 ? "GEOMETRY_NODES.NATIVE_MATERIALIZATION" : index === 2 ? "WORKFLOW.CAPABILITY_FAMILY_WORKBENCH" : "IMPLEMENTATION.CAPABILITY_FAMILY_SMOKE";
const taskClass = index === 1 ? "QUEUE_ENABLEMENT" : index === 2 ? "SOURCE_IMPLEMENTATION" : "INTEGRATION_BUILD";
const reportedFamily = taskClass === "INTEGRATION_BUILD" ? "INTEGRATION.BUILD" : phase === 606 && index === 2 ? "RENDER.CAMERA_PANORAMA" : phase === 605 && index === 2 ? "RENDER.CAMERA_DOF_FOCUS_OBJECT" : phase === 604 && index === 2 ? "RENDER.WORLD_ENVIRONMENT_STRENGTH" : phase === 603 && index === 2 ? "RENDER.WORLD_PROBE_CONTROLS" : phase === 602 && index === 2 ? "RENDER.WORLD_LIGHTING_CONTROLS" : phase === 601 && index === 2 ? "RENDER.LIGHT_CLIP_START" : phase === 600 && index === 2 ? "RENDER.LIGHT_AREA_SIZE_Z" : phase === 599 && index === 2 ? "RENDER.LIGHT_CASCADE_CONTROLS" : phase === 598 && index === 2 ? "RENDER.LIGHT_SHADOW_CONTROLS" : phase === 597 && index === 2 ? "RENDER.LIGHT_CONTRIBUTION_FACTORS" : phase === 596 && index === 2 ? "RENDER.LIGHT_AREA_SHAPE" : phase === 595 && index === 2 ? "RENDER.LIGHT_TYPE" : phase === 77 && index === 2 ? "RENDER.WORLD_LIGHTGROUP_MEMBERSHIP" : phase === 76 && index === 2 ? "RENDER.OBJECT_LIGHTGROUP_MEMBERSHIP" : phase === 75 && index === 2 ? "RENDER.VIEW_LAYER_LIGHTGROUPS" : phase === 74 && index === 2 ? "RENDER.VIEW_LAYER_AOV" : phase === 73 && index === 2 ? "ARCHITECTURE.UNIFIED_WASM_JSON_ABI" : family;
const capabilityId = taskClass !== "SOURCE_IMPLEMENTATION" ? null : phase === 606 && index === 2 ? "CAP-RENDER-CAMERA-PANORAMA" : phase === 605 && index === 2 ? "CAP-RENDER-CAMERA-DOF-FOCUS-OBJECT" : phase === 604 && index === 2 ? "CAP-RENDER-WORLD-ENVIRONMENT-STRENGTH" : phase === 603 && index === 2 ? "CAP-RENDER-WORLD-PROBE-CONTROLS" : phase === 602 && index === 2 ? "CAP-RENDER-WORLD-LIGHTING-CONTROLS" : phase === 601 && index === 2 ? "CAP-RENDER-LIGHT-CLIP-START" : phase === 600 && index === 2 ? "CAP-RENDER-LIGHT-AREA-SIZE-Z" : phase === 599 && index === 2 ? "CAP-RENDER-LIGHT-CASCADE-CONTROLS" : phase === 598 && index === 2 ? "CAP-RENDER-LIGHT-SHADOW-CONTROLS" : phase === 597 && index === 2 ? "CAP-RENDER-LIGHT-CONTRIBUTION-FACTORS" : phase === 596 && index === 2 ? "CAP-RENDER-LIGHT-AREA-SHAPE" : phase === 595 && index === 2 ? "CAP-RENDER-LIGHT-TYPE" : `CAPABILITY-${reportedFamily.replaceAll(".", "-")}`;
const behaviorMarker = taskClass === "INTEGRATION_BUILD" ? "INTEGRATION_BUILD_WEB_BUNDLE" : index === 2 ? markers[2] : markers[index];
const out = `tests/golden/${task}`;
const reportPath = `${out}/implementation-report.json`;
const statusPath = `docs/status/${task}.md`;
const planPath = `${out}/capability-continuation-plan.json`;
fs.mkdirSync(abs(out), { recursive: true });
fs.writeFileSync(abs(reportPath), `${JSON.stringify({ schemaVersion: 1, task, operation: "IMPLEMENT_CAPABILITY_FAMILY_CONTINUATION", taskClass, status: "PASS", capabilityId, behaviorMarker, family: reportedFamily, sourcePaths: files[index], command: commands[index]?.join(" ") ?? "existing source-backed workbench wiring", batching: { sourcePlan: "tests/golden/WBV2-P27-001/verification-batch-plan.json", batchCount: 27, pendingRows: 6609, deduplication: true }, verification: { status: "DEFERRED", chromium: false, e2eReceipts: false, completionOverlay: false }, inventory: { total: 6900, legacyCompleted: 281, legacyPending: 6618, effectiveCompleted: 290, effectivePending: 6609 }, nextTask }, null, 2)}\n`);
fs.mkdirSync(abs("docs/status"), { recursive: true });
fs.writeFileSync(abs(statusPath), `# ${task} status\n\n- task: \`${task}\`; parent: \`${parent.task}\`; status: \`done\`\n- taskClass: \`${taskClass}\`; capability: \`${capabilityId}\`; behaviorMarker: \`${behaviorMarker}\`\n- capability-family implementation: \`PASS\`; 27 deduplicated batches; 6609 pending rows\n- Chromium, E2E receipts and completion overlay: \`DEFERRED\`\n- inventory unchanged: \`6900/6618\`, effective \`290/6609\`\n- nextTask: \`${nextTask}\`\n`);
const plan = json(parent.artifacts.plan.path);
plan.tasks = plan.tasks.map((candidate) => candidate.id === task ? { ...candidate, status: "done", ready: false } : candidate.id === nextTask ? { ...candidate, status: "in_progress", ready: true } : { ...candidate, ready: false });
if (!plan.tasks.some((candidate) => candidate.id === nextTask)) plan.tasks.push({ id: nextTask, phase: `P${phase + 1}`, title: "authorize_capability_family_wave", kind: "enabling", owner: "PROGRAM", dependsOn: [task], parityId: null, implementationClass: "SOURCE_BACKED", parallelGroup: `p${phase + 1}-000`, gate: "G1", card: `docs/tasks/${nextTask}.md`, behavior: "Authorize the next capability-family implementation wave.", outputs: ["implementation-report", "manifest", "status"], acceptance: ["6900 inventory remains immutable", "capability families are deduplicated before implementation", "real Blender source or WebEngine Main path is wired", "verification and completion overlay remain deferred"], status: "in_progress", ready: true, queueMutationAllowed: true });
fs.writeFileSync(abs(planPath), `${JSON.stringify(plan, null, 2)}\n`);
if (nextIndex > 3) fs.writeFileSync(abs(`docs/tasks/${nextTask}.md`), `# ${nextTask}: authorize_capability_family_wave\n\n- task: ${nextTask}\n- parent: ${task}\n- status: in_progress\n- gap: workflow:authorize_capability_family_wave\n- ownerFamily: PROGRAM\n- targetImplementationClass: SOURCE_BACKED\n\n## 目标\n\nAuthorize the next capability-family implementation wave from Blender source.\n\n## 输入与范围\n\n- Blender source authority: \`blender-5.2.0/source/blender\`\n- Implementation entrypoint: \`node tools/web/generate-wbv2-capability-continuation-root.mjs\`\n\n## 验收\n\n- 6900 inventory remains immutable\n- capability-family deduplication is reused\n- Chromium receipts and completion overlays remain deferred\n- \`node tools/web/check-task-context.mjs --task ${nextTask} --write\`\n\n## 失败边界\n\n- missing source, unsupported capability or hash drift remains in_progress/blocked\n\n## 交付与回滚\n\n- Reports: \`tests/golden/${nextTask}/\`\n- Status: \`docs/status/${nextTask}.md\`\n- Manifest: \`tests/golden/${nextTask}/manifest.json\`\n- Rollback: restore ${task} as queue parent and leave the wave in_progress.\n`);
execFileSync(process.execPath, [abs("tools/web/generate-v2-task-index.mjs"), "--plan-path", abs(planPath)], { cwd: root, stdio: "inherit" });
let updatedQueue = read("docs/EXECUTION_QUEUE.md");
updatedQueue = updatedQueue.replace(/\| 当前任务 \| `[^`]+` \|/u, `| 当前任务 | \`${nextTask}\` |`).replace(/\| parent manifest \| `[^`]+` \|/u, `| parent manifest | \`tests/golden/${task}/manifest.json\` |`).replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, `| 任务卡 | [\`tasks/${nextTask}.md\`](tasks/${nextTask}.md) |`);
fs.writeFileSync(abs("docs/EXECUTION_QUEUE.md"), updatedQueue);
execFileSync(process.execPath, [abs("tools/web/check-task-context.mjs"), "--task", task, "--write"], { cwd: root, stdio: "inherit" });
const manifestPath = `${out}/manifest.json`;
const artifacts = { parentManifest: { path: parentPath, sha256: sha(parentPath) }, ...Object.fromEntries(files[index].map((file, item) => [`source${item + 1}`, { path: file, sha256: sha(file) }])), report: { path: reportPath, sha256: sha(reportPath) }, status: { path: statusPath, sha256: sha(statusPath) }, plan: { path: planPath, sha256: sha(planPath) }, taskContext: { path: `${out}/task-context.json`, sha256: sha(`${out}/task-context.json`) } };
fs.writeFileSync(abs(manifestPath), `${JSON.stringify({ schemaVersion: 1, task, parentTask: parent.task, operation: "IMPLEMENT_CAPABILITY_FAMILY_CONTINUATION", taskClass, status: "done", enablingTask: index === 1, parityStateChange: false, runtime: "SOURCE_BACKED_CAPABILITY_FAMILY_QUEUE", artifacts, assertions: { capabilityFamily: reportedFamily, capabilityId, behaviorMarker, sourceBacked: taskClass === "SOURCE_IMPLEMENTATION", batchCount: 27, pendingRows: 6609, verificationDeferred: true, completionOverlayWritten: false, inventoryTotal: 6900, effectiveCompleted: 290, effectivePending: 6609, historicalReproduction: false }, nextTask }, null, 2)}\n`);
execFileSync(process.execPath, [abs("tools/web/check-task-context.mjs"), "--task", nextTask, "--write"], { cwd: root, stdio: "inherit" });
const tick = String.fromCharCode(96);
let next = read("nextTask.md");
next = next.replace(/\| Current task when this file was written \| `[^`]+` \|/u, "| Current task when this file was written | " + tick + nextTask + tick + " |").replace(/\| Current task state \| `[^`]+` \|/u, "| Current task state | " + tick + "in_progress" + tick + " |").replace(/\| Parent manifest \| `[^`]+` \|/u, "| Parent manifest | " + tick + "tests/golden/" + task + "/manifest.json" + tick + " |").replace(/\| Current task card \| `[^`]+` \|/u, "| Current task card | " + tick + "docs/tasks/" + nextTask + ".md" + tick + " |").replace(/\| Current task-context nextTask \| `[^`]+` \|/u, "| Current task-context nextTask | " + tick + (nextIndex > 3 ? "NONE" : `WBV2-P${phase}-${String(nextIndex + 1).padStart(3, "0")}`) + tick + " |").replace(/\| Focused command \| `[^`]+` \|/u, "| Focused command | " + tick + (nextIndex > 3 ? "node tools/web/generate-wbv2-capability-continuation-root.mjs" : `node tools/web/generate-wbv2-capability-continuation-slice.mjs --task ${nextTask} --write`) + tick + " |");
fs.writeFileSync(abs("nextTask.md"), next);
console.log(`wbv2-capability-continuation-slice-written task=${task} next=${nextTask} batches=27 pending=6609 verification=DEFERRED`);