Files
workinf_Blender_Wasm/my-tools/activate_full_feature_gap_phase.py
mes123456 66d3f4e0f4
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): complete portable Blender feature gaps
2026-09-06 19:33:50 +08:00

227 lines
17 KiB
Python

#!/usr/bin/env python3
"""Activate the next implementation-first Web feature gap phase."""
from __future__ import annotations
import hashlib
import json
import re
import subprocess
from pathlib import Path
from wbv2_runtime import CONTROL_ROOT, INDEX, QUEUE, ROOT
CURRENT = "WBV2-P1421-001"
ROOT_TASK = "WBV2-P1422-000"
TASK = "WBV2-P1422-001"
def read_json(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))
def write_json(path: Path, value: object) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def write_text(path: Path, value: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(value, encoding="utf-8")
def sha(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def rel(path: Path) -> str:
return path.relative_to(ROOT).as_posix()
TASKS = [
{
"id": "OBJECT-EDITOR-COVERAGE", "module": "OBJECT", "title": "Complete object editor authoring workflows",
"commands": ["clearObjectTransform", "createObjectData", "setObjectSelection", "setCollectionVisibility"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/scene-ir.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "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"],
"implementation": "Map the remaining object editor workflows to native Main transactions: clear location/rotation/scale, create camera/light/empty/armature data, selection/visibility and collection operations. Preserve browser-native viewport context; do not emulate desktop screens.",
},
{
"id": "MESH-EDITOR-COVERAGE", "module": "MESH", "title": "Complete mesh and UV editor authoring workflows",
"commands": ["meshSubdivide", "meshTriangulate", "meshRecalculateNormals", "meshBridgeLoops", "meshDeleteElements", "meshUVProject"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/mesh-authoring-commands.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Extend the existing native mesh writer for subdivide, triangulate, normal recalculation, bridge loops, delete variants and common UV projection/seam workflows, with typed selection and no JavaScript mesh simulation.",
},
{
"id": "CURVE-SURFACE-COVERAGE", "module": "NONMESH", "title": "Complete curve, surface and point-data authoring",
"commands": ["setCurveBevel", "setCurveTaper", "convertCurveType", "setSurfaceResolution", "setMetaballElements"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/nonmesh-geometry-commands.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Complete user-reachable curve/surface/point-cloud workflows using Blender Main data, including bevel/taper/resolution/type conversion and remaining metaball/curve properties.",
},
{
"id": "ARMATURE-EDITOR-COVERAGE", "module": "ANIMATION", "title": "Complete armature edit and constraint workflows",
"commands": ["armatureEditOperations", "setBoneRoll", "setBoneSymmetry", "setBoneParenting", "setPoseConstraint"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/scene-ir.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "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"],
"implementation": "Extend the existing armature hierarchy writer with edit operations, roll/symmetry/parenting and pose constraints through native Main.",
},
{
"id": "ANIMATION-FCURVE-DRIVER-COVERAGE", "module": "ANIMATION", "title": "Complete FCurve, driver and keying workflows",
"commands": ["setFCurveKeyframes", "setDriverExpression", "setActionGroups", "setKeyingSet", "bakeAnimation"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/animation-render-commands.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Add typed FCurve point/driver/group/keying-set and bounded bake workflows to the existing Action/Main path.",
},
{
"id": "SEQUENCER-COMPOSITOR-COVERAGE", "module": "MEDIA", "title": "Complete Sequencer effects and compositor authoring",
"commands": ["setSequencerStripModifiers", "setSequencerTransitions", "setCompositorNodeGraph", "setSequencerColorBalance"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/sequencer.ts", "web/protocol/compositor-commands.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Extend full-strip editing with effects/transitions and expose compositor graph editing through native Main graph transactions.",
},
{
"id": "PHYSICS-SIMULATION-COVERAGE", "module": "SIMULATION", "title": "Complete physics and cache authoring workflows",
"commands": ["setRigidBodySettings", "setClothSettings", "setFluidSettings", "setParticleCache", "setSimulationPlayback"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/physics-simulation.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Add typed native Main settings and cache/playback controls for rigid body, cloth, fluid, soft body and particle workflows, with explicit unavailable-resource errors.",
},
{
"id": "TRACKING-MASK-COVERAGE", "module": "ASSET_IO", "title": "Complete movie tracking and mask workflows",
"commands": ["setTrackingMarkers", "setTrackingSolve", "setMaskSpline", "setMaskPoint"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/tracking-mask.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Expose browser media-backed tracking markers, solve settings and mask spline/point editing through native Main or the existing server job boundary.",
},
{
"id": "GREASE-PENCIL-COVERAGE", "module": "PAINT", "title": "Complete Grease Pencil draw/edit/paint workflows",
"commands": ["setGreasePencilDrawing", "setGreasePencilMaterial", "setGreasePencilSelection", "setGreasePencilInterpolate"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/grease-pencil.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Extend existing layer/frame/stroke support to drawing edit, material, selection, interpolation and browser pointer workflows.",
},
{
"id": "RENDER-CAMERA-LIGHT-COVERAGE", "module": "RENDER", "title": "Complete render, camera, light and pass workflows",
"commands": ["renderFrame", "renderAnimation", "setRenderPasses", "setCameraView", "setLightLinking"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/render-routing.ts", "web/protocol/render-capabilities.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Complete user-visible render execution/routing, camera view controls, advanced light linking and render pass settings through native or explicit server routes.",
},
{
"id": "ASSET-LIBRARY-COVERAGE", "module": "ASSET_IO", "title": "Complete asset browser and library workflows",
"commands": ["assetMark", "assetCatalog", "libraryLink", "libraryOverride", "libraryReload"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/asset-library-io.ts", "web/protocol/library-main-append.ts", "web/app/src/app/App.tsx", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp"],
"implementation": "Expose browser-native asset catalog, link/append/override/reload workflows and preserve path/security rules.",
},
{
"id": "RNA-DATABLOCK-REFLECTION", "module": "RUNTIME", "title": "Complete generic RNA data-block reflection and editing",
"commands": ["listRNADataBlocks", "readRNAProperties", "setRNAProperty", "listRNACollections"],
"ownedPaths": ["web/protocol/web-engine.ts", "web/protocol/scene-ir.ts", "web/app/src/app/App.tsx", "web/app/src/engine-client/WebEngineClient.ts", "web/app/src/workers/web-engine.worker.ts", "blender-5.2.0/source/blender/web_engine/web_engine_api.cpp", "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"],
"implementation": "Add source-backed bounded RNA reflection for the remaining user-editable data-block properties. Reject unsupported pointers, context-only properties and unsafe arbitrary execution.",
},
]
def main() -> int:
backlog_path = CONTROL_ROOT / "web-product-backlog.json"
backlog = read_json(backlog_path)
existing = {task.get("id") for task in backlog.get("tasks", [])}
for task in TASKS:
task.setdefault("attempts", 0)
task["status"] = "pending"
task["resultPath"] = f"tests/golden/WBV2/product-migration/{task['id']}-result.json"
if task["id"] not in existing:
backlog.setdefault("tasks", []).append(task)
else:
for current in backlog["tasks"]:
if current.get("id") == task["id"]:
current.update(task)
module_tasks = {}
for task in backlog["tasks"]:
module_tasks.setdefault(task.get("module"), []).append(task["id"])
for module in backlog.get("workflowModules", []):
extra = module_tasks.get(module.get("id"), [])
if extra:
module["taskIds"] = list(dict.fromkeys([*module.get("taskIds", []), *extra]))
module["status"] = "partial"
backlog["previousQueueTask"] = CURRENT
backlog["rootTask"] = ROOT_TASK
backlog["queueTask"] = TASK
backlog["strategy"] = "implementation_first_full_feature_gaps"
backlog["gapInventory"] = "docs/WEB_FULL_FEATURE_GAP_INVENTORY.md"
write_json(backlog_path, backlog)
current_manifest_path = ROOT / "tests/golden" / CURRENT / "manifest.json"
current_manifest = read_json(current_manifest_path)
current_manifest["nextTask"] = ROOT_TASK
write_json(current_manifest_path, current_manifest)
current_status_path = ROOT / "docs/status" / f"{CURRENT}.md"
if current_status_path.is_file():
status = re.sub(r"- nextTask: `[^`]+`", f"- nextTask: `{ROOT_TASK}`", current_status_path.read_text(encoding="utf-8"))
write_text(current_status_path, status)
if isinstance(current_manifest.get("artifacts", {}).get("status"), dict):
current_manifest["artifacts"]["status"]["sha256"] = sha(current_status_path)
write_json(current_manifest_path, current_manifest)
root_dir = ROOT / "tests/golden" / ROOT_TASK
task_dir = ROOT / "tests/golden" / TASK
plan_path = root_dir / "product-implementation-plan.json"
base_plan = read_json(ROOT / "tests/golden/WBV2-P1421-000/product-implementation-plan.json")
root_record = {"id": ROOT_TASK, "phase": "1422", "title": "authorize full Web feature gap implementation", "kind": "enabling", "owner": "PROGRAM", "dependsOn": [CURRENT], "implementationClass": "QUEUE_ONLY", "card": f"docs/tasks/{ROOT_TASK}.md", "commands": [], "status": "done", "ready": False, "queueMutationAllowed": True}
task_record = {"id": TASK, "phase": "1422", "title": "complete full Web feature gap implementation", "kind": "product-module", "owner": "WEB_PRODUCT", "dependsOn": [ROOT_TASK], "implementationClass": "PRODUCT_WORKFLOW", "card": f"docs/tasks/{TASK}.md", "commands": ["python3 my-tools/run_web_product_migration.py"], "status": "in_progress", "ready": True, "queueMutationAllowed": True, "productBacklog": "tests/golden/WBV2/web-product-backlog.json", "module": {"sourcePaths": list(dict.fromkeys(p for task in TASKS for p in task["ownedPaths"]))}}
plan = {**base_plan, "planId": "BLENDER_WEB_FULL_FEATURE_IMPLEMENTATION_P1422", "status": "ACTIVE_FULL_FEATURE_GAP_IMPLEMENTATION", "tasks": [*base_plan.get("tasks", []), root_record, task_record]}
write_json(plan_path, plan)
write_text(ROOT / "docs/tasks" / f"{ROOT_TASK}.md", f"# {ROOT_TASK}: authorize full Web feature gap implementation\n\n- task: {ROOT_TASK}\n- parent: {CURRENT}\n- status: done\n- nextTask: {TASK}\n- gap inventory: `docs/WEB_FULL_FEATURE_GAP_INVENTORY.md`\n")
write_text(ROOT / "docs/tasks" / f"{TASK}.md", f"""# {TASK}: complete full Web feature gap implementation
- task: {TASK}
- parent: {ROOT_TASK}
- status: in_progress
- targetImplementationClass: PRODUCT_WORKFLOW
- implementation entrypoint: `python3 my-tools/run_web_product_migration.py`
- node tools/web/print-task-context.mjs
- scope: `docs/WEB_FULL_FEATURE_GAP_INVENTORY.md` and pending tasks in `tests/golden/WBV2/web-product-backlog.json`
- verification: deferred to user operation; do not run full validators in implementation turns
## 目标
Implement every pending user-reachable Web feature gap in the backlog through Blender Main/WASM routes, preserving browser-native equivalents for desktop-only UI.
## 输入与范围
- `docs/WEB_FULL_FEATURE_GAP_INVENTORY.md`
- `tests/golden/WBV2/web-product-backlog.json`
- owned source/protocol/Worker/UI paths listed by each pending task
## 验收
- one bounded implementation task per Codex turn
- existing dirty changes are preserved
- no JavaScript simulation replaces Blender Main behavior
- verification remains deferred until user operation
## 失败边界
- failed turns remain pending and are retried
- unsupported desktop-only or unavailable-resource paths fail closed
- no task advances from a missing result artifact or unrelated file change
## 交付与回滚
- result artifacts: `tests/golden/WBV2/product-migration/*-result.json`
- aggregate report: `tests/golden/{TASK}/product-implementation-report.json`
- rollback: stop the product runner and retain the last completed bounded task
""")
root_report = root_dir / "full-feature-gap-authorization.json"
write_json(root_report, {"schemaVersion": 1, "task": ROOT_TASK, "operation": "AUTHORIZE_FULL_FEATURE_GAP_IMPLEMENTATION", "status": "PASS", "gapInventory": "docs/WEB_FULL_FEATURE_GAP_INVENTORY.md", "pendingTaskCount": len(TASKS), "nextTask": TASK})
root_status = ROOT / "docs/status" / f"{ROOT_TASK}.md"
write_text(root_status, f"# {ROOT_TASK} status\n\n- status: `done`\n- gap inventory: `docs/WEB_FULL_FEATURE_GAP_INVENTORY.md`\n- nextTask: `{TASK}`\n")
root_manifest = root_dir / "manifest.json"
write_json(root_manifest, {"schemaVersion": 1, "task": ROOT_TASK, "parentTask": CURRENT, "operation": "AUTHORIZE_FULL_FEATURE_GAP_IMPLEMENTATION", "status": "done", "nextTask": TASK, "artifacts": {"parentManifest": {"path": f"tests/golden/{CURRENT}/manifest.json", "sha256": sha(current_manifest_path)}, "backlog": {"path": str(backlog_path.relative_to(ROOT)), "sha256": sha(backlog_path)}, "plan": {"path": str(plan_path.relative_to(ROOT)), "sha256": sha(plan_path)}, "inventory": {"path": "docs/WEB_FULL_FEATURE_GAP_INVENTORY.md", "sha256": sha(ROOT / "docs/WEB_FULL_FEATURE_GAP_INVENTORY.md")}}})
queue = QUEUE.read_text(encoding="utf-8")
queue = re.sub(r"\| 当前任务 \| `[^`]+` \|", f"| 当前任务 | `{TASK}` |", queue, count=1)
queue = re.sub(r"\| parent manifest \| `[^`]+` \|", f"| parent manifest | `tests/golden/{ROOT_TASK}/manifest.json` |", queue, count=1, flags=re.IGNORECASE)
queue = re.sub(r"\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|", f"| 任务卡 | [`tasks/{TASK}.md`](tasks/{TASK}.md) |", queue, count=1)
write_text(QUEUE, queue)
subprocess.run(["node", "tools/web/generate-v2-task-index.mjs", "--plan-path", str(plan_path.relative_to(ROOT))], cwd=ROOT, check=True)
subprocess.run(["node", "tools/web/check-task-context.mjs", "--task", TASK, "--write"], cwd=ROOT, check=True)
print(f"full-feature-gap-phase-activated task={TASK} pending={len(TASKS)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())