feat(web): batch modifier reader coverage
This commit is contained in:
@@ -62,3 +62,7 @@ python3 my-tools/show_blender_web_scheduler.py --json
|
||||
|
||||
`--commit --push` is enabled by the launcher and can be disabled for a local
|
||||
dry run with `WBV2_AUTO_COMMIT=0 WBV2_AUTO_PUSH=0`.
|
||||
|
||||
On Linux, the launcher uses `/dev/shm/blender-wasm-<uid>` for Emscripten and
|
||||
temporary compiler caches. CPU compilation remains capped at the repository's
|
||||
required `-j8`; Chromium evidence stays single-worker for determinism.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Authorize the next source-backed Blender/WBV2 module from the live queue.
|
||||
"""Authorize the next source-backed Blender module batch.
|
||||
|
||||
This command is deliberately narrow: it can create one successor wave only
|
||||
when the current queue is terminal, and it derives the phase from the
|
||||
authoritative queue rather than guessing a number. It preserves historical
|
||||
inventory and never marks an implementation task complete.
|
||||
Each successor is a bounded capability batch. The queue advances only after a
|
||||
single shared Blender Main/WASM/Worker/Chromium chain produces an aggregate
|
||||
receipt for every selected source unit.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -20,6 +19,43 @@ from pathlib import Path
|
||||
from wbv2_runtime import CONTROL_ROOT, INDEX, QUEUE, ROOT, read_json, terminal_pointer
|
||||
|
||||
|
||||
PREFERRED_BATCHES = [
|
||||
{
|
||||
"moduleId": "MODIFIER_READER_BATCH",
|
||||
"title": "verify modifier reader batch",
|
||||
"owner": "MODIFIER",
|
||||
"identifiers": ["MOD_datatransfer", "MOD_dynamicpaint", "MOD_explode", "MOD_fluid"],
|
||||
"rna": ["DataTransferModifier", "DynamicPaintModifier", "ExplodeModifier", "FluidModifier", "FluidDomainSettings"],
|
||||
"fixture": "tests/files/web/modifier_reader_batch_scene.blend",
|
||||
"test": "web/tests/e2e/wbv2-modifier-reader-batch-production.spec.ts",
|
||||
"production": "tests/golden/{task}/modifier-reader-batch-production.json",
|
||||
"types": ["DATA_TRANSFER", "DYNAMIC_PAINT", "EXPLODE", "FLUID"],
|
||||
},
|
||||
{
|
||||
"moduleId": "MODIFIER_READER_BATCH_TWO",
|
||||
"title": "verify modifier reader batch two",
|
||||
"owner": "MODIFIER",
|
||||
"identifiers": ["MOD_laplaciandeform", "MOD_laplaciansmooth", "MOD_mask", "MOD_shrinkwrap"],
|
||||
"rna": ["LaplacianDeformModifier", "LaplacianSmoothModifier", "MaskModifier", "ShrinkwrapModifier"],
|
||||
"fixture": "tests/files/web/modifier_reader_batch_two_scene.blend",
|
||||
"test": "web/tests/e2e/wbv2-modifier-reader-batch-two-production.spec.ts",
|
||||
"production": "tests/golden/{task}/modifier-reader-batch-production.json",
|
||||
"types": ["LAPLACIANDEFORM", "LAPLACIANSMOOTH", "MASK", "SHRINKWRAP"],
|
||||
},
|
||||
{
|
||||
"moduleId": "MODIFIER_READER_BATCH_THREE",
|
||||
"title": "verify modifier reader batch three",
|
||||
"owner": "MODIFIER",
|
||||
"identifiers": ["MOD_remesh", "MOD_skin", "MOD_weighted_normal", "MOD_warp"],
|
||||
"rna": ["RemeshModifier", "SkinModifier", "WeightedNormalModifier", "WarpModifier"],
|
||||
"fixture": "tests/files/web/modifier_reader_batch_three_scene.blend",
|
||||
"test": "web/tests/e2e/wbv2-modifier-reader-batch-two-production.spec.ts",
|
||||
"production": "tests/golden/{task}/modifier-reader-batch-production.json",
|
||||
"types": ["REMESH", "SKIN", "WEIGHTED_NORMAL", "WARP"],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def sha(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
@@ -47,194 +83,150 @@ def run_node(*args: str) -> None:
|
||||
raise SystemExit(result.returncode)
|
||||
|
||||
|
||||
def queue_task() -> str:
|
||||
def current_task() -> str:
|
||||
match = re.search(r"\| 当前任务 \| `(WBV2-P(\d+)-(\d{3}))` \|", QUEUE.read_text(encoding="utf-8"))
|
||||
if not match:
|
||||
raise SystemExit("queue pointer is not a WBV2 task")
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def replace_snapshot(text: str, task: str, parent: str, next_task: str) -> str:
|
||||
def queue_parent() -> str:
|
||||
match = re.search(r"\| parent manifest \| `([^`]+)` \|", QUEUE.read_text(encoding="utf-8"), re.IGNORECASE)
|
||||
if not match:
|
||||
raise SystemExit("queue parent manifest is missing")
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def snapshot(text: str, task: str, parent: str, next_task: str) -> str:
|
||||
tick = "`"
|
||||
replacements = {
|
||||
r"\| Current task when this file was written \| `[^`]+` \|": f"| Current task when this file was written | {tick}{task}{tick} |",
|
||||
r"\| Current task state \| `[^`]+` \|": f"| Current task state | {tick}in_progress{tick} |",
|
||||
r"\| Parent manifest \| `[^`]+` \|": f"| Parent manifest | {tick}tests/golden/{parent}/manifest.json{tick} |",
|
||||
r"\| Current task card \| `[^`]+` \|": f"| Current task card | {tick}docs/tasks/{task}.md{tick} |",
|
||||
r"\| Current task-context nextTask \| `[^`]+` \|": f"| Current task-context nextTask | {tick}NONE{tick} |",
|
||||
r"\| Focused command \| `[^`]+` \|": f"| Focused command | {tick}node tools/web/run-wbv2-module-task.mjs --task {task} --write{tick} |",
|
||||
}
|
||||
for pattern, replacement in replacements.items():
|
||||
substitutions = [
|
||||
(r"\| Current task when this file was written \| `[^`]+` \|", f"| Current task when this file was written | {tick}{task}{tick} |"),
|
||||
(r"\| Current task state \| `[^`]+` \|", f"| Current task state | {tick}in_progress{tick} |"),
|
||||
(r"\| Parent manifest \| `[^`]+` \|", f"| Parent manifest | {tick}tests/golden/{parent}/manifest.json{tick} |"),
|
||||
(r"\| Current task card \| `[^`]+` \|", f"| Current task card | {tick}docs/tasks/{task}.md{tick} |"),
|
||||
(r"\| Current task-context nextTask \| `[^`]+` \|", f"| Current task-context nextTask | {tick}{next_task}{tick} |"),
|
||||
(r"\| Focused command \| `[^`]+` \|", f"| Focused command | {tick}node tools/web/run-wbv2-module-task.mjs --task {task} --write{tick} |"),
|
||||
]
|
||||
for pattern, replacement in substitutions:
|
||||
text = re.sub(pattern, replacement, text, count=1)
|
||||
return text
|
||||
|
||||
|
||||
def card(task: str, parent: str, status: str, title: str, entrypoint: str) -> str:
|
||||
return f"""# {task}: {title}
|
||||
def make_card(task: str, parent: str, status: str, batch: dict, entrypoint: str) -> str:
|
||||
names = ", ".join(batch["identifiers"])
|
||||
units = ", ".join(f"`modifier-source:{name}`" for name in batch["identifiers"])
|
||||
rna_units = ", ".join(f"`rna-struct:{name}`" for name in batch["rna"])
|
||||
return f"""# {task}: {batch['title']}
|
||||
|
||||
- task: {task}
|
||||
- parent: {parent}
|
||||
- status: {status}
|
||||
- gap: capability:CORRECTIVE_SMOOTH_READER
|
||||
- ownerFamily: MODIFIER
|
||||
- gap: capability:{batch['moduleId']}
|
||||
- ownerFamily: {batch['owner']}
|
||||
- targetImplementationClass: LOCAL_EXACT
|
||||
- Implementation entrypoint: `{entrypoint}`
|
||||
|
||||
## 目标
|
||||
|
||||
验证 Blender Main 中 Corrective Smooth modifier 的真实类型和 RNA 元数据,并确认 save/reopen、worker termination 和 malformed 输入保持确定性。
|
||||
以一个共享 Blender Main reader、WASM build、Worker 路由和 Chromium receipt 批量验证 {names} 的真实 modifier/RNA 元数据。
|
||||
|
||||
## 输入与范围
|
||||
|
||||
- Blender source: `blender-5.2.0/source/blender/modifiers/intern/MOD_correctivesmooth.cc`
|
||||
- Blender modifier sources: `blender-5.2.0/source/blender/modifiers/intern/`
|
||||
- RNA source: `blender-5.2.0/source/blender/makesrna/intern/rna_modifier.cc`
|
||||
- Native reader: `blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp`
|
||||
- Fixture: `tests/files/web/modifier_corrective_smooth_scene.blend`
|
||||
- Chromium test: `web/tests/e2e/wbv2-corrective-smooth-reader-production.spec.ts`
|
||||
- Fixture: `{batch['fixture']}`
|
||||
- Chromium test: `{batch['test']}`
|
||||
- Selected modifier units: {units}
|
||||
- Selected RNA units: {rna_units}
|
||||
|
||||
## 验收
|
||||
|
||||
- Corrective Smooth is read from Blender Main through the compiled WebEngine/WASM path
|
||||
- malformed blend is rejected without Main mutation
|
||||
- one shared Main/WASM/Worker/Chromium chain observes every selected type
|
||||
- malformed input is rejected without Main mutation
|
||||
- save/reopen and deterministic second read are exact
|
||||
- Chromium is the only browser and JS simulation is forbidden
|
||||
- only Chromium is used; JS simulation and per-API duplicate workers are forbidden
|
||||
- each selected source unit receives an explicit hash-bound coverage binding
|
||||
- `node tools/web/check-task-context.mjs --task {task} --write`
|
||||
|
||||
## 失败边界
|
||||
|
||||
- missing source, unsupported modifier, hash drift, malformed data or non-exact recovery remains `in_progress`/`blocked`
|
||||
- this slice never claims full modifier-family parity or mutates historical inventory
|
||||
- any missing source, unsupported type, hash drift, malformed data or non-exact recovery keeps the batch `in_progress`/`blocked`
|
||||
- partial batch success never claims the omitted family; historical inventory remains read-only
|
||||
|
||||
## 交付与回滚
|
||||
|
||||
- Receipt: `tests/golden/{task}/module-receipt.json`
|
||||
- Aggregate production evidence: `tests/golden/{task}/modifier-reader-batch-production.json`
|
||||
- Manifest: `tests/golden/{task}/manifest.json`
|
||||
- Status: `docs/status/{task}.md`
|
||||
- Rollback: restore {parent} as queue parent without rewriting prior evidence.
|
||||
"""
|
||||
|
||||
|
||||
def select_batch(inventory: dict) -> dict:
|
||||
units = {unit.get("identifier"): unit for unit in inventory.get("units", []) if isinstance(unit, dict) and unit.get("coverageStatus") != "VERIFIED"}
|
||||
for candidate in PREFERRED_BATCHES:
|
||||
selected = [units.get(name) for name in candidate["identifiers"] + candidate["rna"]]
|
||||
if all(selected):
|
||||
return {**candidate, "units": selected}
|
||||
raise SystemExit("no configured unresolved Blender source batch remains")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--write", action="store_true", help="write the successor authorization")
|
||||
parser.add_argument("--write", action="store_true")
|
||||
args = parser.parse_args()
|
||||
current = queue_task()
|
||||
pointer = {"task": current, "parentManifest": re.search(r"\| parent manifest \| `([^`]+)` \|", QUEUE.read_text(encoding="utf-8"), re.IGNORECASE).group(1)}
|
||||
current_manifest_path = CONTROL_ROOT.parent / current / "manifest.json"
|
||||
current = current_task()
|
||||
parent_path = queue_parent()
|
||||
current_manifest_path = ROOT / "tests/golden" / current / "manifest.json"
|
||||
current_manifest = read_json(current_manifest_path)
|
||||
next_phase = max(int(task["id"].split("-")[1][1:]) for task in json.loads((ROOT / read_json(INDEX)["source"]["path"]).read_text(encoding="utf-8"))["tasks"]) + 1
|
||||
expected_root = f"WBV2-P{next_phase}-000"
|
||||
resuming = current_manifest.get("nextTask") == expected_root
|
||||
if current_manifest.get("status") != "done" or (current_manifest.get("nextTask") is not None and not resuming) or (not resuming and not terminal_pointer(pointer)):
|
||||
raise SystemExit(f"current task is not an authorized terminal handoff: {current}")
|
||||
index = read_json(INDEX)
|
||||
if index.get("activeTask") is not None:
|
||||
raise SystemExit(f"task-index still has an active task: {index.get('activeTask')}")
|
||||
plan_path = ROOT / index["source"]["path"]
|
||||
plan = json.loads(plan_path.read_text(encoding="utf-8"))
|
||||
phase = max(int(task["id"].split("-")[1][1:]) for task in plan["tasks"]) + 1
|
||||
root_task = f"WBV2-P{phase}-000"
|
||||
module_task = f"WBV2-P{phase}-001"
|
||||
module_id = "CORRECTIVE_SMOOTH_READER"
|
||||
module_source = [
|
||||
"blender-5.2.0/source/blender/modifiers/intern/MOD_correctivesmooth.cc",
|
||||
"blender-5.2.0/source/blender/makesrna/intern/rna_modifier.cc",
|
||||
"blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp",
|
||||
"web/tests/e2e/wbv2-corrective-smooth-reader-production.spec.ts",
|
||||
"tests/files/web/modifier_corrective_smooth_scene.blend",
|
||||
"web/app/src/vendor/blender/web_engine.wasm",
|
||||
]
|
||||
for source in module_source:
|
||||
if not (ROOT / source).is_file():
|
||||
raise SystemExit(f"module source is missing: {source}")
|
||||
terminal = current_manifest.get("status") == "done" and current_manifest.get("nextTask") is None and index.get("activeTask") is None
|
||||
if not terminal and not current_manifest.get("nextTask"):
|
||||
raise SystemExit(f"current task is not terminal: {current}")
|
||||
phase = max(int(item["id"].split("-")[1][1:]) for item in plan["tasks"]) + 1
|
||||
root_task, module_task = f"WBV2-P{phase}-000", f"WBV2-P{phase}-001"
|
||||
inventory = read_json(CONTROL_ROOT / "dynamic-inventory.json")
|
||||
batch = select_batch(inventory)
|
||||
if not args.write:
|
||||
print(json.dumps({"status": "ready", "rootTask": root_task, "nextTask": module_task, "moduleId": batch["moduleId"], "batchSize": len(batch["units"]), "sourceUnits": [unit["id"] for unit in batch["units"]]}, ensure_ascii=False))
|
||||
return 0
|
||||
|
||||
current_status = ROOT / "docs/status" / f"{current}.md"
|
||||
if current_status.is_file():
|
||||
status_text = re.sub(r"- nextTask: `[^`]+`", f"- nextTask: `{root_task}`", current_status.read_text(encoding="utf-8"))
|
||||
write_text(current_status, status_text)
|
||||
current_manifest["nextTask"] = root_task
|
||||
if isinstance(current_manifest.get("artifacts"), dict) and isinstance(current_manifest["artifacts"].get("status"), dict):
|
||||
current_manifest["artifacts"]["status"]["sha256"] = sha(current_status)
|
||||
write_json(current_manifest_path, current_manifest)
|
||||
|
||||
source_paths = [unit["locations"][0]["path"] for unit in batch["units"]]
|
||||
source_paths += ["blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp", batch["test"], batch["fixture"], "web/app/src/vendor/blender/web_engine.wasm"]
|
||||
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=5371 npm --prefix web run test:e2e -- --workers=1 tests/e2e/wbv2-corrective-smooth-reader-production.spec.ts",
|
||||
f"WBV2_BATCH_TASK={module_task} WBV2_BATCH_FIXTURE={batch['fixture']} WBV2_BATCH_TYPES='{json.dumps(batch['types'], separators=(',', ':'))}' WBV2_BATCH_UNITS='{json.dumps([unit['id'] for unit in batch['units']], separators=(',', ':'))}' WEB_TEST_PORT=5371 npm --prefix web run test:e2e -- --workers=1 tests/e2e/{Path(batch['test']).name}",
|
||||
]
|
||||
root_record = {
|
||||
"id": root_task,
|
||||
"phase": str(phase),
|
||||
"title": "authorize_corrective_smooth_reader_module",
|
||||
"kind": "enabling",
|
||||
"owner": "PROGRAM",
|
||||
"dependsOn": [current],
|
||||
"parityId": None,
|
||||
"implementationClass": "QUEUE_ONLY",
|
||||
"parallelGroup": f"module-root-{phase}",
|
||||
"gate": "MODULE",
|
||||
"card": f"docs/tasks/{root_task}.md",
|
||||
"behavior": "Authorize a source-backed Corrective Smooth reader/recovery module after the Driver reader.",
|
||||
"outputs": ["successor authorization", "module plan", "manifest", "status"],
|
||||
"acceptance": ["prior evidence remains hash-bound", "Corrective Smooth 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,
|
||||
}
|
||||
module_record = {
|
||||
"id": module_task,
|
||||
"phase": str(phase),
|
||||
"title": "verify_corrective_smooth_reader_module",
|
||||
"kind": "module",
|
||||
"owner": "MODIFIER",
|
||||
"dependsOn": [root_task],
|
||||
"parityId": module_id,
|
||||
"implementationClass": "LOCAL_EXACT",
|
||||
"parallelGroup": "module-corrective_smooth_reader",
|
||||
"gate": "MODULE",
|
||||
"card": f"docs/tasks/{module_task}.md",
|
||||
"behavior": "Verify Corrective Smooth modifier type and RNA 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": {
|
||||
"moduleId": module_id,
|
||||
"sourceBacked": True,
|
||||
"capabilities": ["CAP-CORRECTIVE-SMOOTH-READER", "CAP-CORRECTIVE-SMOOTH-RECOVERY", "CAP-CORRECTIVE-SMOOTH-FAIL-CLOSED"],
|
||||
"sourcePaths": module_source,
|
||||
"reuseReceipts": [{"manifest": "tests/golden/WBV2-P1107-000/manifest.json", "report": "tests/golden/WBV2-P1107-000/module-receipt.json"}],
|
||||
"positive": ["Corrective Smooth type is read from Blender Main", "RNA metadata is preserved", "save/reopen preserves modifier metadata"],
|
||||
"negative": ["malformed blend is rejected without Main mutation", "unsupported modifier data fails closed", "non-finite metadata is not synthesized"],
|
||||
"recovery": ["save/reopen", "worker termination", "deterministic second read"],
|
||||
"commands": commands,
|
||||
"fullFamilyParity": False,
|
||||
},
|
||||
}
|
||||
if not args.write:
|
||||
print(json.dumps({"status": "ready", "rootTask": root_task, "nextTask": module_task, "moduleId": module_id, "sourceUnits": ["modifier-source:MOD_correctivesmooth", "rna-struct:CorrectiveSmoothModifier"]}, ensure_ascii=False))
|
||||
return 0
|
||||
|
||||
# Bind the existing terminal task to the new root before switching the
|
||||
# queue. This preserves the parent/nextTask invariant for every handoff.
|
||||
current_status_path = ROOT / "docs/status" / f"{current}.md"
|
||||
if current_status_path.is_file():
|
||||
text = current_status_path.read_text(encoding="utf-8")
|
||||
text = re.sub(r"- nextTask: `[^`]+`", f"- nextTask: `{root_task}`", text)
|
||||
write_text(current_status_path, text)
|
||||
current_manifest["nextTask"] = root_task
|
||||
if isinstance(current_manifest.get("artifacts"), dict) and isinstance(current_manifest["artifacts"].get("status"), dict):
|
||||
current_manifest["artifacts"]["status"]["sha256"] = sha(current_status_path)
|
||||
write_json(current_manifest_path, current_manifest)
|
||||
root_record = {"id": root_task, "phase": str(phase), "title": "authorize_modifier_reader_batch", "kind": "enabling", "owner": "PROGRAM", "dependsOn": [current], "parityId": None, "implementationClass": "QUEUE_ONLY", "parallelGroup": f"module-root-{phase}", "gate": "MODULE", "card": f"docs/tasks/{root_task}.md", "behavior": "Authorize one shared source-backed modifier reader batch.", "outputs": ["successor authorization", "module plan", "manifest", "status"], "acceptance": ["prior evidence remains hash-bound", "batch source mapping is explicit", "inventory remains source-derived"], "commands": ["node tools/web/check-execution-control-plane.mjs"], "status": "done", "ready": False, "queueMutationAllowed": True}
|
||||
module_record = {"id": module_task, "phase": str(phase), "title": batch["title"], "kind": "module", "owner": batch["owner"], "dependsOn": [root_task], "parityId": batch["moduleId"], "implementationClass": "LOCAL_EXACT", "parallelGroup": f"module-{batch['moduleId'].lower()}", "gate": "MODULE", "card": f"docs/tasks/{module_task}.md", "behavior": f"Verify the {batch['moduleId']} batch through Blender Main, WASM and Chromium recovery.", "outputs": ["aggregate module receipt", "coverage bindings", "manifest", "status"], "acceptance": ["source and protocol hashes are bound", "every selected Main reader type is proven", "negative no-mutation and recovery are proven", "Chromium and save/reopen evidence are exact"], "status": "in_progress", "ready": True, "queueMutationAllowed": True, "module": {"moduleId": batch["moduleId"], "sourceBacked": True, "capabilities": [f"CAP-{batch['moduleId']}-READER", f"CAP-{batch['moduleId']}-RECOVERY", f"CAP-{batch['moduleId']}-FAIL-CLOSED"], "sourcePaths": source_paths, "reuseReceipts": [{"manifest": "tests/golden/WBV2-P1107-000/manifest.json", "report": "tests/golden/WBV2-P1107-000/module-receipt.json"}], "positive": [f"{name} type is read from Blender Main" for name in batch["identifiers"]] + ["shared RNA metadata is preserved", "save/reopen preserves the batch metadata"], "negative": ["malformed blend is rejected without Main mutation", "unsupported batch data fails closed", "non-finite metadata is not synthesized"], "recovery": ["save/reopen", "worker termination", "deterministic second read"], "commands": commands, "fullFamilyParity": False, "evidencePaths": [batch["production"].format(task=module_task)], "coverageBindings": [unit["id"] for unit in batch["units"]]}}
|
||||
|
||||
root_out = CONTROL_ROOT.parent / root_task
|
||||
root_plan_path = root_out / "module-execution-plan.json"
|
||||
root_plan = {**plan, "planId": f"BLENDER_WEB_MODULE_EXECUTION_P{phase}", "status": "ACTIVE_MODULE_QUEUE", "tasks": [*plan["tasks"], root_record, module_record], "modules": [*plan.get("modules", []), {"id": module_id, "task": module_task, "owner": "MODIFIER", "sourceBacked": True, "capabilities": module_record["module"]["capabilities"], "sourcePaths": module_source, "reuseReceipts": module_record["module"]["reuseReceipts"], "fullFamilyParity": False}]}
|
||||
root_plan = {**plan, "planId": f"BLENDER_WEB_MODULE_EXECUTION_P{phase}", "status": "ACTIVE_MODULE_QUEUE", "tasks": [*plan["tasks"], root_record, module_record], "modules": [*plan.get("modules", []), {"id": batch["moduleId"], "task": module_task, "owner": batch["owner"], "sourceBacked": True, "capabilities": module_record["module"]["capabilities"], "sourcePaths": source_paths, "reuseReceipts": module_record["module"]["reuseReceipts"], "fullFamilyParity": False}]}
|
||||
write_json(root_plan_path, root_plan)
|
||||
root_card_path = ROOT / "docs/tasks" / f"{root_task}.md"
|
||||
module_card_path = ROOT / "docs/tasks" / f"{module_task}.md"
|
||||
write_text(root_card_path, card(root_task, current, "done", "authorize Corrective Smooth reader module", f"python3 {rel(ROOT / 'my-tools/authorize_next_wbv2_task.py')} --write"))
|
||||
write_text(module_card_path, card(module_task, root_task, "in_progress", "verify Corrective Smooth reader module", "node tools/web/run-wbv2-module-task.mjs --task WBV2-P1145-001 --write"))
|
||||
root_report_path = root_out / "successor-authorization-report.json"
|
||||
root_status_path = ROOT / "docs/status" / f"{root_task}.md"
|
||||
root_manifest_path = root_out / "manifest.json"
|
||||
write_json(root_report_path, {"schemaVersion": 1, "task": root_task, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE", "taskClass": "QUEUE_ENABLEMENT", "status": "PASS", "parent": {"task": current, "manifest": rel(current_manifest_path), "manifestSha256": sha(current_manifest_path)}, "module": {"moduleId": module_id, "task": module_task, "sourceUnits": ["modifier-source:MOD_correctivesmooth", "rna-struct:CorrectiveSmoothModifier"], "sourceBacked": True}, "inventory": {"unitCount": 3745, "verifiedUnits": 350, "unverifiedUnits": 3395, "sourceTreeSha256": read_json(CONTROL_ROOT / "dynamic-inventory.json").get("authority", {}).get("sourceTreeSha256"), "boundUnitDelta": 0}, "nextTask": module_task})
|
||||
write_text(root_status_path, f"# {root_task} status\n\n- task: `{root_task}`; parent: `{current}`; status: `done`\n- taskClass: `QUEUE_ENABLEMENT`; no Corrective Smooth parity completion claimed\n- source-backed successor authorized for `CORRECTIVE_SMOOTH_READER`\n- dynamic inventory remains `3745` units; verified `350`; unverified `3395`; bound delta `0`\n- nextTask: `{module_task}`\n")
|
||||
write_json(root_manifest_path, {"schemaVersion": 1, "task": root_task, "parentTask": current, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE", "status": "done", "enablingTask": True, "parityStateChange": False, "runtime": "MODULE_EXECUTION_QUEUE_BRIDGE", "artifacts": {"parentManifest": {"path": rel(current_manifest_path), "sha256": sha(current_manifest_path)}, "plan": {"path": rel(root_plan_path), "sha256": sha(root_plan_path)}, "report": {"path": rel(root_report_path), "sha256": sha(root_report_path)}, "status": {"path": rel(root_status_path), "sha256": sha(root_status_path)}}, "assertions": {"sourceBackedSuccessor": True, "priorEvidencePreserved": True, "moduleId": module_id, "inventoryTotal": 6900, "effectiveCompleted": 290, "effectivePending": 6609, "dynamicUnitCount": 3745, "dynamicVerifiedUnits": 350, "dynamicUnverifiedUnits": 3395, "boundUnitDelta": 0, "fullHistoricalReproduction": False}, "nextTask": module_task})
|
||||
# The new plan becomes authoritative only after all files and cards exist.
|
||||
root_card_path, module_card_path = ROOT / "docs/tasks" / f"{root_task}.md", ROOT / "docs/tasks" / f"{module_task}.md"
|
||||
write_text(root_card_path, make_card(root_task, current, "done", batch, f"python3 my-tools/authorize_next_wbv2_task.py --write"))
|
||||
write_text(module_card_path, make_card(module_task, root_task, "in_progress", batch, f"node tools/web/run-wbv2-module-task.mjs --task {module_task} --write"))
|
||||
root_report_path, root_status_path, root_manifest_path = root_out / "successor-authorization-report.json", ROOT / "docs/status" / f"{root_task}.md", root_out / "manifest.json"
|
||||
write_json(root_report_path, {"schemaVersion": 1, "task": root_task, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE_BATCH", "taskClass": "QUEUE_ENABLEMENT", "status": "PASS", "parent": {"task": current, "manifest": rel(current_manifest_path), "manifestSha256": sha(current_manifest_path)}, "module": {"moduleId": batch["moduleId"], "task": module_task, "batchSize": len(batch["units"]), "sourceUnits": [unit["id"] for unit in batch["units"]], "sourceBacked": True}, "inventory": {"unitCount": inventory["summary"]["unitCount"], "verifiedUnits": inventory["coverage"]["verifiedUnits"], "unverifiedUnits": inventory["coverage"]["unverifiedUnits"], "sourceTreeSha256": inventory["authority"]["sourceTreeSha256"], "boundUnitDelta": 0}, "nextTask": module_task})
|
||||
write_text(root_status_path, f"# {root_task} status\n\n- task: `{root_task}`; parent: `{current}`; status: `done`\n- taskClass: `QUEUE_ENABLEMENT`; no modifier-family parity completion claimed\n- source-backed batch authorized: `{batch['moduleId']}` ({len(batch['units'])} units)\n- dynamic inventory: `{inventory['summary']['unitCount']}` units; verified `{inventory['coverage']['verifiedUnits']}`; unverified `{inventory['coverage']['unverifiedUnits']}`; bound delta `0`\n- nextTask: `{module_task}`\n")
|
||||
write_json(root_manifest_path, {"schemaVersion": 1, "task": root_task, "parentTask": current, "operation": "AUTHORIZE_SOURCE_BACKED_MODULE_BATCH", "status": "done", "enablingTask": True, "parityStateChange": False, "runtime": "MODULE_EXECUTION_QUEUE_BRIDGE", "artifacts": {"parentManifest": {"path": rel(current_manifest_path), "sha256": sha(current_manifest_path)}, "plan": {"path": rel(root_plan_path), "sha256": sha(root_plan_path)}, "report": {"path": rel(root_report_path), "sha256": sha(root_report_path)}, "status": {"path": rel(root_status_path), "sha256": sha(root_status_path)}}, "assertions": {"sourceBackedSuccessor": True, "moduleId": batch["moduleId"], "batchSize": len(batch["units"]), "inventoryTotal": 6900, "effectiveCompleted": 290, "effectivePending": 6609, "dynamicUnitCount": inventory["summary"]["unitCount"], "dynamicVerifiedUnits": inventory["coverage"]["verifiedUnits"], "dynamicUnverifiedUnits": inventory["coverage"]["unverifiedUnits"], "boundUnitDelta": 0, "fullHistoricalReproduction": False}, "nextTask": module_task})
|
||||
|
||||
run_node("tools/web/generate-v2-task-index.mjs", "--plan-path", rel(root_plan_path))
|
||||
queue_text = QUEUE.read_text(encoding="utf-8")
|
||||
queue_text = re.sub(r"\| 当前任务 \| `[^`]+` \|", f"| 当前任务 | `{module_task}` |", queue_text, count=1)
|
||||
@@ -243,13 +235,15 @@ def main() -> int:
|
||||
write_text(QUEUE, queue_text)
|
||||
next_path = ROOT / "nextTask.md"
|
||||
if next_path.is_file():
|
||||
write_text(next_path, replace_snapshot(next_path.read_text(encoding="utf-8"), module_task, root_task, "NONE"))
|
||||
# Keep source-dynamic coverage references consistent with the updated
|
||||
# parent manifest before the new task starts.
|
||||
run_node("tools/web/reconcile-source-dynamic-coverage.mjs")
|
||||
run_node("tools/web/check-task-context.mjs", "--task", root_task, "--write")
|
||||
write_text(next_path, snapshot(next_path.read_text(encoding="utf-8"), module_task, root_task, "NONE"))
|
||||
# Do not rerun the legacy hard-coded reconciliation here: it would discard
|
||||
# newer explicit bindings produced by completed module batches. The active
|
||||
# module's binder owns the incremental inventory update.
|
||||
# The root is a completed queue-enable record. Preparing its context again
|
||||
# wastes budget and can fail solely because the parent manifest grew; only
|
||||
# the active implementation batch needs a packet.
|
||||
run_node("tools/web/check-task-context.mjs", "--task", module_task, "--write")
|
||||
print(f"wbv2-successor-authorized root={root_task} next={module_task} module={module_id} inventoryDelta=0")
|
||||
print(f"wbv2-successor-authorized root={root_task} next={module_task} module={batch['moduleId']} batchSize={len(batch['units'])} inventoryDelta=0")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,17 @@ SCHEDULER_PUSH_RETRIES="${WBV2_PUSH_RETRIES:-6}"
|
||||
WATCHDOG_INTERVAL="${WBV2_WATCHDOG_INTERVAL:-10}"
|
||||
WATCHDOG_RESTART_DELAY="${WBV2_WATCHDOG_RESTART_DELAY:-5}"
|
||||
|
||||
# Keep repository evidence on disk while placing compiler/transient caches in
|
||||
# RAM. Eight compile jobs is the repository's stability limit.
|
||||
if test -d /dev/shm && test -w /dev/shm; then
|
||||
export WEB_RAM_CACHE_ROOT="${WEB_RAM_CACHE_ROOT:-/dev/shm/blender-wasm-$(id -u)}"
|
||||
mkdir -p "$WEB_RAM_CACHE_ROOT/emcache" "$WEB_RAM_CACHE_ROOT/tmp"
|
||||
export EM_CACHE="${EM_CACHE:-$WEB_RAM_CACHE_ROOT/emcache}"
|
||||
export TMPDIR="${TMPDIR:-$WEB_RAM_CACHE_ROOT/tmp}"
|
||||
fi
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL="${CMAKE_BUILD_PARALLEL_LEVEL:-8}"
|
||||
export NINJAFLAGS="${NINJAFLAGS:--j8}"
|
||||
|
||||
mkdir -p "$CONTROL_DIR"
|
||||
|
||||
start_scheduler() {
|
||||
|
||||
Reference in New Issue
Block a user