feat(web): complete Blender route runtime execution
This commit is contained in:
149
my-tools/activate_server_operator_runtime_phase.py
Normal file
149
my-tools/activate_server_operator_runtime_phase.py
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Activate the final server-operator and browser-equivalent runtime closure."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from wbv2_runtime import CONTROL_ROOT, QUEUE, ROOT
|
||||
|
||||
CURRENT, ROOT_TASK, TASK = "WBV2-P1423-001", "WBV2-P1424-000", "WBV2-P1424-001"
|
||||
|
||||
|
||||
def read(path: Path) -> dict:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def write(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 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()
|
||||
|
||||
|
||||
TASKS = [
|
||||
{
|
||||
"id": "SERVER-OPERATOR-HTTP-PROVIDER", "module": "RUNTIME", "title": "Connect server-native routes to an executable HTTP provider",
|
||||
"commands": ["submitServerOperatorJob", "cancelServerOperatorJob", "readServerOperatorResult"],
|
||||
"ownedPaths": ["web/protocol/server-operator-job.ts", "web/app/src/engine-client/WebEngineClient.ts", "web/app/src/app/App.tsx", "web/app/vite.config.ts", "tools/web/server-operator-job-server.mjs", "tools/web/server-operator-job.py", "tools/web/server-job-process.mjs"],
|
||||
"implementation": "Replace the QUEUED-only browser stub with a real same-origin/configurable HTTP provider. Save the current Main to .blend, submit a bounded binary envelope, execute Blender background in an isolated workspace, save and hash the mutated .blend, return it, reopen it in WebEngine, and support cancellation/result reads. Preserve source/build/property/request/result hash bindings and resource budgets.",
|
||||
},
|
||||
{
|
||||
"id": "SERVER-OPERATOR-SCHEMA-PALETTE", "module": "WORKSPACE", "title": "Load native operator RNA schemas on demand in F3",
|
||||
"commands": ["describeServerOperator", "invokeFeatureRoute"],
|
||||
"ownedPaths": ["web/protocol/server-operator-job.ts", "web/protocol/blender-operator.ts", "web/app/src/engine-client/WebEngineClient.ts", "web/app/src/app/App.tsx", "tools/web/server-operator-job-server.mjs", "tools/web/server-operator-job.py"],
|
||||
"implementation": "Query Blender background RNA metadata for a selected SERVER_NATIVE operator, cache the typed boolean/number/enum/string schema by Blender build, render the existing compact property form, and submit a real server job. Unsupported pointer/collection/context properties remain blocked.",
|
||||
},
|
||||
{
|
||||
"id": "BROWSER-EQUIVALENT-RUNTIME", "module": "WORKSPACE", "title": "Make browser-equivalent feature routes actionable",
|
||||
"commands": ["invokeBrowserEquivalent", "featureRouteRuntimeAudit"],
|
||||
"ownedPaths": ["web/protocol/blender-operator.ts", "web/app/src/app/App.tsx", "web/app/src/capabilities/blender-feature-route-catalog.json", "tools/web/generate-blender-feature-route-catalog.mjs", "tools/web/check-blender-feature-route-catalog.mjs", "tools/web/check-web-engine-command-registry.mjs", "docs/WEB_FULL_FEATURE_GAP_INVENTORY.md", "my-tools/show_blender_web_scheduler.py"],
|
||||
"implementation": "Attach explicit browser action groups to BROWSER_EQUIVALENT routes and dispatch them to existing open/save/workspace/selection/view/editor workflows from F3. Publish a static runtime audit proving every WEB_MAIN command is registered, every SERVER_NATIVE entry has the HTTP route, and every BROWSER_EQUIVALENT entry has an actionable browser group; keep behavioral evidence separate.",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
backlog_path = CONTROL_ROOT / "web-product-backlog.json"
|
||||
backlog = read(backlog_path)
|
||||
existing = {item.get("id") for item in backlog.get("tasks", [])}
|
||||
for task in TASKS:
|
||||
item = {**task, "attempts": 0, "status": "pending", "resultPath": f"tests/golden/WBV2/product-migration/{task['id']}-result.json"}
|
||||
if task["id"] not in existing:
|
||||
backlog["tasks"].append(item)
|
||||
else:
|
||||
next(current for current in backlog["tasks"] if current.get("id") == task["id"]).update(item)
|
||||
grouped = {}
|
||||
for item in backlog["tasks"]:
|
||||
grouped.setdefault(item.get("module"), []).append(item["id"])
|
||||
for module in backlog["workflowModules"]:
|
||||
if grouped.get(module["id"]):
|
||||
module["taskIds"] = list(dict.fromkeys([*module.get("taskIds", []), *grouped[module["id"]]]))
|
||||
module["status"] = "partial"
|
||||
backlog.update({"previousQueueTask": CURRENT, "rootTask": ROOT_TASK, "queueTask": TASK, "strategy": "implementation_first_runtime_closure"})
|
||||
write(backlog_path, backlog)
|
||||
|
||||
current_manifest_path = ROOT / "tests/golden" / CURRENT / "manifest.json"
|
||||
current_manifest = read(current_manifest_path)
|
||||
current_manifest["nextTask"] = ROOT_TASK
|
||||
current_status = ROOT / "docs/status" / f"{CURRENT}.md"
|
||||
if current_status.is_file():
|
||||
status = re.sub(r"- nextTask: `[^`]+`", f"- nextTask: `{ROOT_TASK}`", current_status.read_text(encoding="utf-8"))
|
||||
text(current_status, status)
|
||||
if isinstance(current_manifest.get("artifacts", {}).get("status"), dict):
|
||||
current_manifest["artifacts"]["status"]["sha256"] = sha(current_status)
|
||||
write(current_manifest_path, current_manifest)
|
||||
|
||||
root_dir = ROOT / "tests/golden" / ROOT_TASK
|
||||
plan_path = root_dir / "runtime-closure-plan.json"
|
||||
base_plan = read(ROOT / "tests/golden/WBV2-P1423-000/operator-route-implementation-plan.json")
|
||||
root_record = {"id": ROOT_TASK, "phase": "1424", "title": "authorize server operator runtime closure", "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": "1424", "title": "complete server operator runtime closure", "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(path for task in TASKS for path in task["ownedPaths"]))}}
|
||||
write(plan_path, {**base_plan, "planId": "BLENDER_WEB_RUNTIME_CLOSURE_P1424", "status": "ACTIVE_RUNTIME_CLOSURE", "tasks": [*base_plan.get("tasks", []), root_record, task_record]})
|
||||
text(ROOT / "docs/tasks" / f"{ROOT_TASK}.md", f"# {ROOT_TASK}: authorize server operator runtime closure\n\n- task: {ROOT_TASK}\n- parent: {CURRENT}\n- status: done\n- nextTask: {TASK}\n")
|
||||
text(ROOT / "docs/tasks" / f"{TASK}.md", f"""# {TASK}: complete server operator runtime closure
|
||||
|
||||
- task: {TASK}
|
||||
- parent: {ROOT_TASK}
|
||||
- status: in_progress
|
||||
- targetImplementationClass: PRODUCT_WORKFLOW
|
||||
- node tools/web/print-task-context.mjs
|
||||
|
||||
## 目标
|
||||
|
||||
Close the audited gap between classified feature routes and real browser/server execution.
|
||||
|
||||
## 输入与范围
|
||||
|
||||
- `tests/golden/WBV2/web-product-backlog.json`
|
||||
- `web/app/src/capabilities/blender-feature-route-catalog.json`
|
||||
- each pending task's owned paths
|
||||
|
||||
## 验收
|
||||
|
||||
- SERVER_NATIVE performs a real bounded HTTP Blender background job and returns a mutated blend
|
||||
- F3 loads typed operator properties on demand
|
||||
- browser-equivalent routes invoke existing browser workflows
|
||||
- verification remains incremental and behavioral evidence stays explicit
|
||||
|
||||
## 失败边界
|
||||
|
||||
- missing provider, malformed envelope, hash mismatch, timeout, cancellation and context-only operators fail closed
|
||||
- failed turns remain pending and never publish false completion
|
||||
|
||||
## 交付与回滚
|
||||
|
||||
- results: `tests/golden/WBV2/product-migration/*-result.json`
|
||||
- aggregate: `tests/golden/{TASK}/product-implementation-report.json`
|
||||
- rollback: stop the runner and preserve the last completed runtime task
|
||||
""")
|
||||
report = root_dir / "runtime-closure-authorization.json"
|
||||
write(report, {"schemaVersion": 1, "task": ROOT_TASK, "operation": "AUTHORIZE_RUNTIME_CLOSURE", "status": "PASS", "pendingTaskCount": len(TASKS), "nextTask": TASK})
|
||||
status_path = ROOT / "docs/status" / f"{ROOT_TASK}.md"
|
||||
text(status_path, f"# {ROOT_TASK} status\n\n- status: `done`\n- nextTask: `{TASK}`\n")
|
||||
manifest = root_dir / "manifest.json"
|
||||
write(manifest, {"schemaVersion": 1, "task": ROOT_TASK, "parentTask": CURRENT, "operation": "AUTHORIZE_RUNTIME_CLOSURE", "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)}}})
|
||||
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)
|
||||
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"runtime-closure-phase-activated task={TASK} pending={len(TASKS)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user