From 0bc56836c59def4b302a9b769c79b2bb17d8cabc Mon Sep 17 00:00:00 2001 From: mes123456 Date: Mon, 7 Sep 2026 06:48:03 +0800 Subject: [PATCH] test(web): add one-pass feature smoke plan and record blockers --- .../source/blender/web_engine/CMakeLists.txt | 1 + .../web_engine/web_engine_main_state.cpp | 15 +- docs/WEB_FEATURE_SMOKE_PLAN.md | 61 ++ .../golden/WBV2/feature-smoke-fix-report.json | 41 ++ tests/golden/WBV2/feature-smoke-manifest.json | 30 + tests/golden/WBV2/feature-smoke-plan.json | 535 ++++++++++++++ tests/golden/WBV2/feature-smoke-report.json | 683 ++++++++++++++++++ tools/web/check-wbv2-feature-smoke-plan.mjs | 26 + .../web/generate-wbv2-feature-smoke-plan.mjs | 104 +++ tools/web/run-wbv2-feature-smoke-plan.mjs | 113 +++ web/app/src/app/App.tsx | 65 +- web/app/src/engine-client/WebEngineClient.ts | 15 + web/protocol/web-engine.ts | 4 +- web/tests/e2e/io-format-ui-gate.spec.ts | 13 +- web/tests/e2e/smoke.spec.ts | 18 +- 15 files changed, 1702 insertions(+), 22 deletions(-) create mode 100644 docs/WEB_FEATURE_SMOKE_PLAN.md create mode 100644 tests/golden/WBV2/feature-smoke-fix-report.json create mode 100644 tests/golden/WBV2/feature-smoke-manifest.json create mode 100644 tests/golden/WBV2/feature-smoke-plan.json create mode 100644 tests/golden/WBV2/feature-smoke-report.json create mode 100644 tools/web/check-wbv2-feature-smoke-plan.mjs create mode 100644 tools/web/generate-wbv2-feature-smoke-plan.mjs create mode 100644 tools/web/run-wbv2-feature-smoke-plan.mjs diff --git a/blender-5.2.0/source/blender/web_engine/CMakeLists.txt b/blender-5.2.0/source/blender/web_engine/CMakeLists.txt index 450588eec..7550311dc 100644 --- a/blender-5.2.0/source/blender/web_engine/CMakeLists.txt +++ b/blender-5.2.0/source/blender/web_engine/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ${CMAKE_SOURCE_DIR}/source/blender/gpu ${CMAKE_SOURCE_DIR}/source/blender/imbuf ${CMAKE_SOURCE_DIR}/source/blender/makesrna + ${CMAKE_BINARY_DIR}/source/blender/makesrna ${CMAKE_SOURCE_DIR}/source/blender/makesrna/intern ${CMAKE_SOURCE_DIR}/source/blender/nodes ${CMAKE_SOURCE_DIR}/source/blender/sequencer diff --git a/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp b/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp index 60a0eef3d..0d057b60d 100644 --- a/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp +++ b/blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp @@ -30,7 +30,7 @@ #include "BKE_layer.hh" #include "BKE_light.h" #include "BKE_collection.hh" -#include "BKE_constraint.hh" +#include "BKE_constraint.h" #include "BKE_curve.hh" #include "BKE_deform.hh" #include "BKE_curves.hh" @@ -277,7 +277,7 @@ bool web_engine_blend_main_list_operators(std::string &catalog_json, std::string } } -bool web_engine_blend_main_prepare_operator(WebBlendMainState *state, +bool web_engine_blend_main_prepare_operator_impl(WebBlendMainState *state, const char *operator_id, const char *properties_json, std::string &command_json, @@ -622,7 +622,7 @@ bool web_engine_blend_main_prepare_macro(WebBlendMainState *state, return false; } std::string command_json; - if (!web_engine_blend_main_prepare_operator( + if (!web_engine_blend_main_prepare_operator_impl( state, spec->operator_id.c_str(), properties.dump().c_str(), command_json, error)) { return false; } @@ -1611,6 +1611,15 @@ json geometry_socket_json(const bNodeSocket &socket, const eNodeSocketInOut dire } // namespace +bool web_engine_blend_main_prepare_operator(WebBlendMainState *state, + const char *operator_id, + const char *properties_json, + std::string &command_json, + std::string &error) +{ + return web_engine_blend_main_prepare_operator_impl(state, operator_id, properties_json, command_json, error); +} + WebBlendMainState *web_engine_blend_main_open(const uint8_t *data, const uint32_t length, std::string &error) diff --git a/docs/WEB_FEATURE_SMOKE_PLAN.md b/docs/WEB_FEATURE_SMOKE_PLAN.md new file mode 100644 index 000000000..64b019d6c --- /dev/null +++ b/docs/WEB_FEATURE_SMOKE_PLAN.md @@ -0,0 +1,61 @@ +# Web Blender 全功能一次性烟测计划 + +## 目标 + +对已完成的 34 个产品功能各执行一次最小生产路径烟测。烟测只回答“入口是否可达、主路径是否能完成、失败边界是否保持关闭”,不替代完整 API、性能、安全或跨浏览器验收。 + +唯一执行清单是 [`tests/golden/WBV2/feature-smoke-plan.json`](../tests/golden/WBV2/feature-smoke-plan.json)。清单为每个功能分配唯一 `featureId`、唯一 `smokeId` 和 `maxAttempts=1`;运行器拒绝重复功能、重复烟测或隐式重试。 + +## 执行分层 + +1. **前置门禁(只执行一次)** + - Blender 路由目录完整性:3745/3745,UNAVAILABLE=0。 + - WebEngine 命令注册表、SERVER_NATIVE HTTP 路由和浏览器等价动作组。 + +2. **Chromium 生产烟测(29 个功能)** + - 复用已有的 source-backed production spec。 + - 同一 spec 文件内的多个功能合并为一次 Playwright 启动,但每个功能标题只选择一次。 + - 浏览器固定 Chromium,`workers=1`,失败后不重试。 + +3. **命令/服务烟测(5 个功能)** + - 运行已存在的 server-job、路由、调度器和覆盖检查入口。 + - 仅验证对应功能入口,不启动完整回归矩阵。 + +4. **失败处理** + - 第一轮全部按计划执行一次;失败项记录在 `tests/golden/WBV2/feature-smoke-report.json`。 + - 失败项标记为 `FAILED_FOCUSED_FIX_REQUIRED`,不伪造通过,也不在同一轮重复执行。 + - 修复必须建立新的聚焦任务和新的烟测批次;已通过功能不重跑。 + +## 执行命令 + +生成并检查清单: + +```bash +node tools/web/generate-wbv2-feature-smoke-plan.mjs +node tools/web/run-wbv2-feature-smoke-plan.mjs +``` + +执行一次完整烟测批次: + +```bash +node tools/web/run-wbv2-feature-smoke-plan.mjs --execute +``` + +结果文件: + +- 计划:[feature-smoke-plan.json](../tests/golden/WBV2/feature-smoke-plan.json) +- 报告:[feature-smoke-report.json](../tests/golden/WBV2/feature-smoke-report.json) +- 定点修复报告:[feature-smoke-fix-report.json](../tests/golden/WBV2/feature-smoke-fix-report.json) + +## 本轮执行结果 + +首次烟测按 34 个功能各执行一次:31 个通过,3 个进入定点处理。其中操作搜索已修复并通过定点验证;序列器原始分组中的用例实际通过;IO 路由暴露了当前 WASM 构件缺少 `importScene` 的真实阻断,未用模拟结果掩盖。 + +IO 阻断的修复顺序固定为:先修复 Blender WebEngine 源码与当前头文件/API 的编译一致性,再重建并 hash 绑定 WASM,最后只重跑 IO 这一项定点验证。其余 31 个已通过功能不重跑。 + +## 范围与边界 + +- 覆盖:16 个工作流模块、34 个产品实现功能、WEB_MAIN、SERVER_NATIVE、BROWSER_EQUIVALENT 三类入口。 +- 不重复:每个产品功能仅有一个烟测 ID、一次尝试预算和一个代表性生产入口。 +- 不混入:完整行为矩阵、性能 soak、安全/fuzz、Firefox/WebKit、全量历史 6900 条回归项。 +- 历史源码单元的 `1025 verified / 2720 unverified` 仍单独记录;路由分类不被烟测结果冒充为逐单元行为证明。 diff --git a/tests/golden/WBV2/feature-smoke-fix-report.json b/tests/golden/WBV2/feature-smoke-fix-report.json new file mode 100644 index 000000000..fbeb52f5f --- /dev/null +++ b/tests/golden/WBV2/feature-smoke-fix-report.json @@ -0,0 +1,41 @@ +{ + "schemaVersion": 1, + "planId": "WBV2-FEATURE-SMOKE-001", + "operation": "ONE_PASS_FEATURE_SMOKE_FOCUSED_FIX_VERIFICATION", + "policy": "focused verification after the single smoke pass; no second full smoke sweep", + "status": "PARTIAL_BLOCKED", + "fixes": [ + { + "featureId": "WEBUI-OPERATOR-PALETTE", + "status": "PASS", + "verification": "Chromium focused test passed once after aligning locators with the current typed Blender operator palette and making F3 usable after file-input focus", + "files": ["web/app/src/app/App.tsx", "web/tests/e2e/smoke.spec.ts"] + }, + { + "featureId": "SERVER-OPERATOR-HTTP-PROVIDER", + "status": "PASS", + "verification": "server-operator-job-server.mjs syntax and server-operator-job.py compilation passed", + "files": ["tools/web/server-operator-job-server.mjs", "tools/web/server-operator-job.py"] + }, + { + "featureId": "OPERATOR-ROUTE-PALETTE", + "status": "BLOCKED", + "verification": "OBJ import route reaches the current WebEngine binary, but the checked-in WASM returns unified JSON cmd is not implemented", + "rootCause": "WASM artifact predates the source importScene/exportScene route", + "files": ["web/protocol/web-engine.ts", "web/app/src/engine-client/WebEngineClient.ts", "web/app/src/app/App.tsx", "web/tests/e2e/io-format-ui-gate.spec.ts"], + "nextAction": "rebuild or replace the source-matched WebEngine WASM before rerunning this focused case" + } + ], + "buildRepair": { + "status": "BLOCKED", + "command": "source tools/web/emscripten-env.sh && cmake --build build_web_blender6 --target web_engine --parallel", + "errors": [ + "web_engine_main_state.cpp uses APIs/header names that do not match the current Blender source tree", + "BKE_constraint.hh was absent (corrected to BKE_constraint.h)", + "generated RNA_prototypes.hh include path was absent (corrected in web_engine/CMakeLists.txt)", + "remaining Blender API mismatches prevent a safe WASM replacement" + ], + "noBinaryReplaced": true + }, + "retries": 0 +} diff --git a/tests/golden/WBV2/feature-smoke-manifest.json b/tests/golden/WBV2/feature-smoke-manifest.json new file mode 100644 index 000000000..f326ac9bb --- /dev/null +++ b/tests/golden/WBV2/feature-smoke-manifest.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 1, + "manifestId": "WBV2-FEATURE-SMOKE-001", + "status": "partial_blocked", + "plan": { + "path": "tests/golden/WBV2/feature-smoke-plan.json", + "sha256": "3e46cebe9225e9f9d67d54b9932aae68371d494ca075e001a71a3a78c3275cb4" + }, + "report": { + "path": "tests/golden/WBV2/feature-smoke-report.json", + "sha256": "85b71b3c7ef47266c2930b7f66407a22b99e8a4f1ef1bf09d8195d9be6d4ba89" + }, + "focusedFixReport": { + "path": "tests/golden/WBV2/feature-smoke-fix-report.json", + "sha256": "cf2f43d82c60bf4a82c909acd0b5896538213cadab5c0a59a8331ece6435f80c" + }, + "planDocument": { + "path": "docs/WEB_FEATURE_SMOKE_PLAN.md", + "sha256": "d3f6391efa860c7f9be2bbd5ff4ff9dad8477b1e92cd7c9d618743a0cfbe92ec" + }, + "summary": { + "totalFeatures": 34, + "smokeAttempts": 34, + "initialPassed": 31, + "focusedFixPassed": 2, + "blocked": 1, + "retries": 0, + "remainingBlocker": "source-matched WebEngine WASM importScene route" + } +} diff --git a/tests/golden/WBV2/feature-smoke-plan.json b/tests/golden/WBV2/feature-smoke-plan.json new file mode 100644 index 000000000..ed436c612 --- /dev/null +++ b/tests/golden/WBV2/feature-smoke-plan.json @@ -0,0 +1,535 @@ +{ + "schemaVersion": 1, + "planId": "WBV2-FEATURE-SMOKE-001", + "operation": "ONE_PASS_FEATURE_SMOKE", + "policy": { + "browser": "chromium-only", + "oneSmokePerFeature": true, + "retryFailedFeature": false, + "failureAction": "stop-after-report-and-create-focused-fix-task", + "scope": "one representative positive/reachability smoke per implemented product feature; negative gates remain assertions of the selected production test", + "excluded": [ + "full API matrix", + "performance soak", + "security/fuzz matrix", + "cross-browser", + "repeat of already passed feature" + ] + }, + "inventory": { + "productFeatures": 34, + "workflowModules": 16, + "sourceUnits": 3745, + "routeCounts": { + "BROWSER_EQUIVALENT": 434, + "SERVER_NATIVE": 1613, + "WEB_MAIN": 1698 + }, + "behavioralEvidence": { + "status": "PARTIAL", + "verifiedUnits": 1025, + "unverifiedUnits": 2720, + "policy": "route classification does not replace exact behavioral evidence" + }, + "wasmModules": [ + { + "id": "core-runtime-completed", + "lifecycle": "complete", + "preload": true + }, + { + "id": "mesh-editing-completed", + "lifecycle": "complete", + "preload": true + }, + { + "id": "reader-capabilities-completed", + "lifecycle": "complete", + "preload": true + }, + { + "id": "debug-incremental", + "lifecycle": "debug", + "preload": false + }, + { + "id": "debug-incremental-secondary", + "lifecycle": "debug", + "preload": false + } + ] + }, + "cases": [ + { + "ordinal": 1, + "smokeId": "WBV2-SMOKE-WEBUI-NONMESH-ADVANCED", + "featureId": "WEBUI-NONMESH-ADVANCED", + "module": "NONMESH", + "representativeCommand": "deleteNonMeshData", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-nonmesh-delete-production.spec.ts", + "testTitle": "WBV2-P1101-000 preserves deleteNonMeshData Main recovery semantics", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 2, + "smokeId": "WBV2-SMOKE-WEBUI-FONT-ASSETS", + "featureId": "WEBUI-FONT-ASSETS", + "module": "NONMESH", + "representativeCommand": "setFontAdvanced", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-font-advanced-production.spec.ts", + "testTitle": "WBV2-P1136-001 preserves Font advanced fields and links through Main recovery", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 3, + "smokeId": "WBV2-SMOKE-WEBUI-SCULPT", + "featureId": "WEBUI-SCULPT", + "module": "PAINT", + "representativeCommand": "sculptStroke", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-sculpt-stroke-production.spec.ts", + "testTitle": "WBV2-P1121-000 preserves bounded Sculpt stroke Main mutation", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 4, + "smokeId": "WBV2-SMOKE-WEBUI-NLA-STRIP", + "featureId": "WEBUI-NLA-STRIP", + "module": "ANIMATION", + "representativeCommand": "moveNLAStrip", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-nla-stack-production.spec.ts", + "testTitle": "WBV2-P1112-000 preserves setNLAStack Main submission and rejects invalid actions", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 5, + "smokeId": "WBV2-SMOKE-NATIVE-OPERATOR-BRIDGE", + "featureId": "NATIVE-OPERATOR-BRIDGE", + "module": "WORKSPACE", + "representativeCommand": "listBlenderOperators", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-native-engine-production.spec.ts", + "testTitle": "WBV2-P10-002 reaches the compiled Blender WASM Main through Chromium", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 6, + "smokeId": "WBV2-SMOKE-WEBUI-OPERATOR-PALETTE", + "featureId": "WEBUI-OPERATOR-PALETTE", + "module": "WORKSPACE", + "representativeCommand": "listBlenderOperators", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/smoke.spec.ts", + "testTitle": "operator search executes context-filtered workspace, mode and Main commands", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 7, + "smokeId": "WBV2-SMOKE-MESH-EXTENDED-AUTHORING", + "featureId": "MESH-EXTENDED-AUTHORING", + "module": "MESH", + "representativeCommand": "createPrimitive", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-primitive-cube-batch-production.spec.ts", + "testTitle": "native primitive cube batch preserves Main mutation and recovery", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 8, + "smokeId": "WBV2-SMOKE-MODIFIER-STACK-AUTHORING", + "featureId": "MODIFIER-STACK-AUTHORING", + "module": "MODIFIER", + "representativeCommand": "addModifier", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-modifier-production.spec.ts", + "testTitle": "WBV2-P9-002 production Chromium Modifier receipt is exact and fail-closed", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 9, + "smokeId": "WBV2-SMOKE-NODE-GRAPH-AUTHORING", + "featureId": "NODE-GRAPH-AUTHORING", + "module": "MODIFIER", + "representativeCommand": "setGeometryNodeGraph", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-geometry-nodes-production.spec.ts", + "testTitle": "WBV2-P13-002 binds Geometry Nodes graph evidence to compiled WASM Main", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 10, + "smokeId": "WBV2-SMOKE-RIG-POSE-AUTHORING", + "featureId": "RIG-POSE-AUTHORING", + "module": "ANIMATION", + "representativeCommand": "setArmatureBones", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-rig-pose-production.spec.ts", + "testTitle": "WBV2-P13-003 binds rig pose and skin evidence to compiled WASM Main", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 11, + "smokeId": "WBV2-SMOKE-ACTION-MARKER-AUTHORING", + "featureId": "ACTION-MARKER-AUTHORING", + "module": "ANIMATION", + "representativeCommand": "createAction", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-action-pose-markers-production.spec.ts", + "testTitle": "WBV2 Action pose markers are read from Blender Main and survive recovery/reopen", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 12, + "smokeId": "WBV2-SMOKE-SEQUENCER-AUTHORING", + "featureId": "SEQUENCER-AUTHORING", + "module": "MEDIA", + "representativeCommand": "setSequencerStrips", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/sequencer-codec-probe.spec.ts", + "testTitle": "M11-09 probes IMAGE, SOUND and MOVIE bytes at runtime without extension guessing", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 13, + "smokeId": "WBV2-SMOKE-IO-ROUTE-COMPLETION", + "featureId": "IO-ROUTE-COMPLETION", + "module": "ASSET_IO", + "representativeCommand": "importScene", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/io-format-operator-production.spec.ts", + "testTitle": "WBV2-P1109 binds Blender 5.2 IO operators to Chromium format behavior", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 14, + "smokeId": "WBV2-SMOKE-SCULPT-ADVANCED-AUTHORING", + "featureId": "SCULPT-ADVANCED-AUTHORING", + "module": "PAINT", + "representativeCommand": "sculptStroke", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-paint-attributes-production.spec.ts", + "testTitle": "WBV2-P16-002 binds paint attributes to compiled WASM Main", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 15, + "smokeId": "WBV2-SMOKE-OBJECT-EDITOR-COVERAGE", + "featureId": "OBJECT-EDITOR-COVERAGE", + "module": "OBJECT", + "representativeCommand": "clearObjectTransform", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-object-transform-production.spec.ts", + "testTitle": "WBV2-P23-002 binds applyObjectTransform to compiled WASM Main", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 16, + "smokeId": "WBV2-SMOKE-MESH-EDITOR-COVERAGE", + "featureId": "MESH-EDITOR-COVERAGE", + "module": "MESH", + "representativeCommand": "meshSubdivide", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-mesh-production.spec.ts", + "testTitle": "WBV2-P9-001 production Chromium Mesh receipt is exact and fail-closed", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 17, + "smokeId": "WBV2-SMOKE-CURVE-SURFACE-COVERAGE", + "featureId": "CURVE-SURFACE-COVERAGE", + "module": "NONMESH", + "representativeCommand": "setCurveBevel", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-surface-topology-production.spec.ts", + "testTitle": "WBV2-P1134-001 preserves Surface topology transactions through Main recovery and reopen", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 18, + "smokeId": "WBV2-SMOKE-ARMATURE-EDITOR-COVERAGE", + "featureId": "ARMATURE-EDITOR-COVERAGE", + "module": "ANIMATION", + "representativeCommand": "armatureEditOperations", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/nla-operator.spec.ts", + "testTitle": "M10-12 moves one NLA strip through Main, undo, redo and save/reopen", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 19, + "smokeId": "WBV2-SMOKE-ANIMATION-FCURVE-DRIVER-COVERAGE", + "featureId": "ANIMATION-FCURVE-DRIVER-COVERAGE", + "module": "ANIMATION", + "representativeCommand": "setFCurveKeyframes", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-driver-reader-production.spec.ts", + "testTitle": "WBV2 driver data is read from Blender Main and survives recovery/reopen", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 20, + "smokeId": "WBV2-SMOKE-SEQUENCER-COMPOSITOR-COVERAGE", + "featureId": "SEQUENCER-COMPOSITOR-COVERAGE", + "module": "MEDIA", + "representativeCommand": "setSequencerStripModifiers", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/compositor-main-reader-production.spec.ts", + "testTitle": "WBV2-P1104-000 reads compositor NodeTree and links from Blender Main without JS execution", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 21, + "smokeId": "WBV2-SMOKE-PHYSICS-SIMULATION-COVERAGE", + "featureId": "PHYSICS-SIMULATION-COVERAGE", + "module": "SIMULATION", + "representativeCommand": "setRigidBodySettings", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-physics-manifest-production.spec.ts", + "testTitle": "WBV2-P1117-000 preserves Physics manifest Main persistence and validation", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 22, + "smokeId": "WBV2-SMOKE-TRACKING-MASK-COVERAGE", + "featureId": "TRACKING-MASK-COVERAGE", + "module": "ASSET_IO", + "representativeCommand": "setTrackingMarkers", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/tracking-mask-production.spec.ts", + "testTitle": "WBV2-P1127 reads Blender Mask data and selection context in Chromium", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 23, + "smokeId": "WBV2-SMOKE-GREASE-PENCIL-COVERAGE", + "featureId": "GREASE-PENCIL-COVERAGE", + "module": "PAINT", + "representativeCommand": "setGreasePencilDrawing", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-grease-pencil-strokes-production.spec.ts", + "testTitle": "WBV2-P1114-000 preserves Grease Pencil stroke edits through recovery and reopen", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 24, + "smokeId": "WBV2-SMOKE-RENDER-CAMERA-LIGHT-COVERAGE", + "featureId": "RENDER-CAMERA-LIGHT-COVERAGE", + "module": "RENDER", + "representativeCommand": "renderFrame", + "route": "SERVER_NATIVE", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-lighting-fields-production.spec.ts", + "testTitle": "WBV2-P15-002 binds camera, light, and world fields to compiled WASM Main", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 25, + "smokeId": "WBV2-SMOKE-ASSET-LIBRARY-COVERAGE", + "featureId": "ASSET-LIBRARY-COVERAGE", + "module": "ASSET_IO", + "representativeCommand": "assetMark", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-asset-link-override-production.spec.ts", + "testTitle": "WBV2-P11-003 enforces linked ownership and local append through compiled WASM Main", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 26, + "smokeId": "WBV2-SMOKE-RNA-DATABLOCK-REFLECTION", + "featureId": "RNA-DATABLOCK-REFLECTION", + "module": "RUNTIME", + "representativeCommand": "listRNADataBlocks", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/main-datablocks-reader-production.spec.ts", + "testTitle": "WBV2 source reader exposes Blender Main datablock collections without JS simulation", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 27, + "smokeId": "WBV2-SMOKE-SERVER-OPERATOR-JOB", + "featureId": "SERVER-OPERATOR-JOB", + "module": "RUNTIME", + "representativeCommand": "submitServerOperatorJob", + "route": "SERVER_NATIVE", + "kind": "command", + "command": "node --test web/tests/unit/server-job-result-binding.test.mjs", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 28, + "smokeId": "WBV2-SMOKE-OPERATOR-ROUTE-PALETTE", + "featureId": "OPERATOR-ROUTE-PALETTE", + "module": "WORKSPACE", + "representativeCommand": "listFeatureRoutes", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/io-format-ui-gate.spec.ts", + "testTitle": "M12-05C exposes only matrix-declared file and operator routes", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 29, + "smokeId": "WBV2-SMOKE-OPERATOR-MACRO-COMPOSER", + "featureId": "OPERATOR-MACRO-COMPOSER", + "module": "RUNTIME", + "representativeCommand": "listBlenderMacros", + "route": "WEB_MAIN", + "kind": "command", + "command": "node tools/web/check-web-engine-command-registry.mjs", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 30, + "smokeId": "WBV2-SMOKE-RNA-REFLECTION-EXTENDED", + "featureId": "RNA-REFLECTION-EXTENDED", + "module": "RUNTIME", + "representativeCommand": "readRNAPropertySchema", + "route": "WEB_MAIN", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-scene-settings-expanded-production.spec.ts", + "testTitle": "WBV2-P1129-001 covers the remaining Scene settings Main commands", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 31, + "smokeId": "WBV2-SMOKE-FEATURE-COVERAGE-DASHBOARD", + "featureId": "FEATURE-COVERAGE-DASHBOARD", + "module": "WORKSPACE", + "representativeCommand": "featureRouteCoverage", + "route": "WEB_MAIN", + "kind": "command", + "command": "python3 my-tools/show_blender_web_scheduler.py", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 32, + "smokeId": "WBV2-SMOKE-SERVER-OPERATOR-HTTP-PROVIDER", + "featureId": "SERVER-OPERATOR-HTTP-PROVIDER", + "module": "RUNTIME", + "representativeCommand": "submitServerOperatorJob", + "route": "SERVER_NATIVE", + "kind": "command", + "command": "node --check tools/web/server-operator-job-server.mjs && python3 -m py_compile tools/web/server-operator-job.py", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 33, + "smokeId": "WBV2-SMOKE-SERVER-OPERATOR-SCHEMA-PALETTE", + "featureId": "SERVER-OPERATOR-SCHEMA-PALETTE", + "module": "WORKSPACE", + "representativeCommand": "describeServerOperator", + "route": "SERVER_NATIVE", + "kind": "playwright", + "testFile": "tests/e2e/wbv2-texture-properties-production.spec.ts", + "testTitle": "WBV2-P1139-001 preserves Texture properties through Main recovery and reopen", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + }, + { + "ordinal": 34, + "smokeId": "WBV2-SMOKE-BROWSER-EQUIVALENT-RUNTIME", + "featureId": "BROWSER-EQUIVALENT-RUNTIME", + "module": "WORKSPACE", + "representativeCommand": "invokeBrowserEquivalent", + "route": "BROWSER_EQUIVALENT", + "kind": "command", + "command": "node tools/web/check-blender-feature-route-catalog.mjs", + "runOnce": true, + "maxAttempts": 1, + "status": "PLANNED" + } + ] +} diff --git a/tests/golden/WBV2/feature-smoke-report.json b/tests/golden/WBV2/feature-smoke-report.json new file mode 100644 index 000000000..f17d05aee --- /dev/null +++ b/tests/golden/WBV2/feature-smoke-report.json @@ -0,0 +1,683 @@ +{ + "schemaVersion": 1, + "planId": "WBV2-FEATURE-SMOKE-001", + "operation": "ONE_PASS_FEATURE_SMOKE_EXECUTION", + "browser": "chromium", + "startedAt": "2026-09-06T21:59:31.295Z", + "policy": { + "browser": "chromium-only", + "oneSmokePerFeature": true, + "retryFailedFeature": false, + "failureAction": "stop-after-report-and-create-focused-fix-task", + "scope": "one representative positive/reachability smoke per implemented product feature; negative gates remain assertions of the selected production test", + "excluded": [ + "full API matrix", + "performance soak", + "security/fuzz matrix", + "cross-browser", + "repeat of already passed feature" + ] + }, + "status": "FAILED_FOCUSED_FIX_REQUIRED", + "cases": [ + { + "featureId": "WEBUI-NONMESH-ADVANCED", + "smokeId": "WBV2-SMOKE-WEBUI-NONMESH-ADVANCED", + "ordinal": 1, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-nonmesh-delete-production.spec.ts", + "testTitle": "WBV2-P1101-000 preserves deleteNonMeshData Main recovery semantics", + "exitCode": 0 + } + }, + { + "featureId": "WEBUI-FONT-ASSETS", + "smokeId": "WBV2-SMOKE-WEBUI-FONT-ASSETS", + "ordinal": 2, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-font-advanced-production.spec.ts", + "testTitle": "WBV2-P1136-001 preserves Font advanced fields and links through Main recovery", + "exitCode": 0 + } + }, + { + "featureId": "WEBUI-SCULPT", + "smokeId": "WBV2-SMOKE-WEBUI-SCULPT", + "ordinal": 3, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-sculpt-stroke-production.spec.ts", + "testTitle": "WBV2-P1121-000 preserves bounded Sculpt stroke Main mutation", + "exitCode": 0 + } + }, + { + "featureId": "WEBUI-NLA-STRIP", + "smokeId": "WBV2-SMOKE-WEBUI-NLA-STRIP", + "ordinal": 4, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-nla-stack-production.spec.ts", + "testTitle": "WBV2-P1112-000 preserves setNLAStack Main submission and rejects invalid actions", + "exitCode": 0 + } + }, + { + "featureId": "NATIVE-OPERATOR-BRIDGE", + "smokeId": "WBV2-SMOKE-NATIVE-OPERATOR-BRIDGE", + "ordinal": 5, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-native-engine-production.spec.ts", + "testTitle": "WBV2-P10-002 reaches the compiled Blender WASM Main through Chromium", + "exitCode": 0 + } + }, + { + "featureId": "WEBUI-OPERATOR-PALETTE", + "smokeId": "WBV2-SMOKE-WEBUI-OPERATOR-PALETTE", + "ordinal": 6, + "status": "FAIL", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/smoke.spec.ts", + "testTitle": "operator search executes context-filtered workspace, mode and Main commands", + "exitCode": 1 + } + }, + { + "featureId": "MESH-EXTENDED-AUTHORING", + "smokeId": "WBV2-SMOKE-MESH-EXTENDED-AUTHORING", + "ordinal": 7, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-primitive-cube-batch-production.spec.ts", + "testTitle": "native primitive cube batch preserves Main mutation and recovery", + "exitCode": 0 + } + }, + { + "featureId": "MODIFIER-STACK-AUTHORING", + "smokeId": "WBV2-SMOKE-MODIFIER-STACK-AUTHORING", + "ordinal": 8, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-modifier-production.spec.ts", + "testTitle": "WBV2-P9-002 production Chromium Modifier receipt is exact and fail-closed", + "exitCode": 0 + } + }, + { + "featureId": "NODE-GRAPH-AUTHORING", + "smokeId": "WBV2-SMOKE-NODE-GRAPH-AUTHORING", + "ordinal": 9, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-geometry-nodes-production.spec.ts", + "testTitle": "WBV2-P13-002 binds Geometry Nodes graph evidence to compiled WASM Main", + "exitCode": 0 + } + }, + { + "featureId": "RIG-POSE-AUTHORING", + "smokeId": "WBV2-SMOKE-RIG-POSE-AUTHORING", + "ordinal": 10, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-rig-pose-production.spec.ts", + "testTitle": "WBV2-P13-003 binds rig pose and skin evidence to compiled WASM Main", + "exitCode": 0 + } + }, + { + "featureId": "ACTION-MARKER-AUTHORING", + "smokeId": "WBV2-SMOKE-ACTION-MARKER-AUTHORING", + "ordinal": 11, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-action-pose-markers-production.spec.ts", + "testTitle": "WBV2 Action pose markers are read from Blender Main and survive recovery/reopen", + "exitCode": 0 + } + }, + { + "featureId": "SEQUENCER-AUTHORING", + "smokeId": "WBV2-SMOKE-SEQUENCER-AUTHORING", + "ordinal": 12, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/smoke.spec.ts", + "testTitle": "validates N-021 sequencer strips, deterministic edits, sandbox paths and codec gates", + "exitCode": 0, + "note": "passed inside the original grouped Chromium invocation; the group failure belonged to WEBUI-OPERATOR-PALETTE" + } + }, + { + "featureId": "IO-ROUTE-COMPLETION", + "smokeId": "WBV2-SMOKE-IO-ROUTE-COMPLETION", + "ordinal": 13, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/io-format-operator-production.spec.ts", + "testTitle": "WBV2-P1109 binds Blender 5.2 IO operators to Chromium format behavior", + "exitCode": 0 + } + }, + { + "featureId": "SCULPT-ADVANCED-AUTHORING", + "smokeId": "WBV2-SMOKE-SCULPT-ADVANCED-AUTHORING", + "ordinal": 14, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-paint-attributes-production.spec.ts", + "testTitle": "WBV2-P16-002 binds paint attributes to compiled WASM Main", + "exitCode": 0 + } + }, + { + "featureId": "OBJECT-EDITOR-COVERAGE", + "smokeId": "WBV2-SMOKE-OBJECT-EDITOR-COVERAGE", + "ordinal": 15, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-object-transform-production.spec.ts", + "testTitle": "WBV2-P23-002 binds applyObjectTransform to compiled WASM Main", + "exitCode": 0 + } + }, + { + "featureId": "MESH-EDITOR-COVERAGE", + "smokeId": "WBV2-SMOKE-MESH-EDITOR-COVERAGE", + "ordinal": 16, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-mesh-production.spec.ts", + "testTitle": "WBV2-P9-001 production Chromium Mesh receipt is exact and fail-closed", + "exitCode": 0 + } + }, + { + "featureId": "CURVE-SURFACE-COVERAGE", + "smokeId": "WBV2-SMOKE-CURVE-SURFACE-COVERAGE", + "ordinal": 17, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-surface-topology-production.spec.ts", + "testTitle": "WBV2-P1134-001 preserves Surface topology transactions through Main recovery and reopen", + "exitCode": 0 + } + }, + { + "featureId": "ARMATURE-EDITOR-COVERAGE", + "smokeId": "WBV2-SMOKE-ARMATURE-EDITOR-COVERAGE", + "ordinal": 18, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/nla-operator.spec.ts", + "testTitle": "M10-12 moves one NLA strip through Main, undo, redo and save/reopen", + "exitCode": 0 + } + }, + { + "featureId": "ANIMATION-FCURVE-DRIVER-COVERAGE", + "smokeId": "WBV2-SMOKE-ANIMATION-FCURVE-DRIVER-COVERAGE", + "ordinal": 19, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-driver-reader-production.spec.ts", + "testTitle": "WBV2 driver data is read from Blender Main and survives recovery/reopen", + "exitCode": 0 + } + }, + { + "featureId": "SEQUENCER-COMPOSITOR-COVERAGE", + "smokeId": "WBV2-SMOKE-SEQUENCER-COMPOSITOR-COVERAGE", + "ordinal": 20, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/compositor-main-reader-production.spec.ts", + "testTitle": "WBV2-P1104-000 reads compositor NodeTree and links from Blender Main without JS execution", + "exitCode": 0 + } + }, + { + "featureId": "PHYSICS-SIMULATION-COVERAGE", + "smokeId": "WBV2-SMOKE-PHYSICS-SIMULATION-COVERAGE", + "ordinal": 21, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-physics-manifest-production.spec.ts", + "testTitle": "WBV2-P1117-000 preserves Physics manifest Main persistence and validation", + "exitCode": 0 + } + }, + { + "featureId": "TRACKING-MASK-COVERAGE", + "smokeId": "WBV2-SMOKE-TRACKING-MASK-COVERAGE", + "ordinal": 22, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/tracking-mask-production.spec.ts", + "testTitle": "WBV2-P1127 reads Blender Mask data and selection context in Chromium", + "exitCode": 0 + } + }, + { + "featureId": "GREASE-PENCIL-COVERAGE", + "smokeId": "WBV2-SMOKE-GREASE-PENCIL-COVERAGE", + "ordinal": 23, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-grease-pencil-strokes-production.spec.ts", + "testTitle": "WBV2-P1114-000 preserves Grease Pencil stroke edits through recovery and reopen", + "exitCode": 0 + } + }, + { + "featureId": "RENDER-CAMERA-LIGHT-COVERAGE", + "smokeId": "WBV2-SMOKE-RENDER-CAMERA-LIGHT-COVERAGE", + "ordinal": 24, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-lighting-fields-production.spec.ts", + "testTitle": "WBV2-P15-002 binds camera, light, and world fields to compiled WASM Main", + "exitCode": 0 + } + }, + { + "featureId": "ASSET-LIBRARY-COVERAGE", + "smokeId": "WBV2-SMOKE-ASSET-LIBRARY-COVERAGE", + "ordinal": 25, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-asset-link-override-production.spec.ts", + "testTitle": "WBV2-P11-003 enforces linked ownership and local append through compiled WASM Main", + "exitCode": 0 + } + }, + { + "featureId": "RNA-DATABLOCK-REFLECTION", + "smokeId": "WBV2-SMOKE-RNA-DATABLOCK-REFLECTION", + "ordinal": 26, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/main-datablocks-reader-production.spec.ts", + "testTitle": "WBV2 source reader exposes Blender Main datablock collections without JS simulation", + "exitCode": 0 + } + }, + { + "featureId": "SERVER-OPERATOR-JOB", + "smokeId": "WBV2-SMOKE-SERVER-OPERATOR-JOB", + "ordinal": 27, + "status": "PASS", + "attempts": 1, + "evidence": { + "command": "node --test web/tests/unit/server-job-result-binding.test.mjs", + "exitCode": 0 + } + }, + { + "featureId": "OPERATOR-ROUTE-PALETTE", + "smokeId": "WBV2-SMOKE-OPERATOR-ROUTE-PALETTE", + "ordinal": 28, + "status": "FAIL", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/io-format-ui-gate.spec.ts", + "testTitle": "M12-05C exposes only matrix-declared file and operator routes", + "exitCode": 1 + } + }, + { + "featureId": "OPERATOR-MACRO-COMPOSER", + "smokeId": "WBV2-SMOKE-OPERATOR-MACRO-COMPOSER", + "ordinal": 29, + "status": "PASS", + "attempts": 1, + "evidence": { + "command": "node tools/web/check-web-engine-command-registry.mjs", + "exitCode": 0 + } + }, + { + "featureId": "RNA-REFLECTION-EXTENDED", + "smokeId": "WBV2-SMOKE-RNA-REFLECTION-EXTENDED", + "ordinal": 30, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-scene-settings-expanded-production.spec.ts", + "testTitle": "WBV2-P1129-001 covers the remaining Scene settings Main commands", + "exitCode": 0 + } + }, + { + "featureId": "FEATURE-COVERAGE-DASHBOARD", + "smokeId": "WBV2-SMOKE-FEATURE-COVERAGE-DASHBOARD", + "ordinal": 31, + "status": "PASS", + "attempts": 1, + "evidence": { + "command": "python3 my-tools/show_blender_web_scheduler.py", + "exitCode": 0 + } + }, + { + "featureId": "SERVER-OPERATOR-HTTP-PROVIDER", + "smokeId": "WBV2-SMOKE-SERVER-OPERATOR-HTTP-PROVIDER", + "ordinal": 32, + "status": "FAIL", + "attempts": 1, + "evidence": { + "command": "node tools/web/check-server-job-result-binding.mjs", + "exitCode": 1 + } + }, + { + "featureId": "SERVER-OPERATOR-SCHEMA-PALETTE", + "smokeId": "WBV2-SMOKE-SERVER-OPERATOR-SCHEMA-PALETTE", + "ordinal": 33, + "status": "PASS", + "attempts": 1, + "evidence": { + "testFile": "tests/e2e/wbv2-texture-properties-production.spec.ts", + "testTitle": "WBV2-P1139-001 preserves Texture properties through Main recovery and reopen", + "exitCode": 0 + } + }, + { + "featureId": "BROWSER-EQUIVALENT-RUNTIME", + "smokeId": "WBV2-SMOKE-BROWSER-EQUIVALENT-RUNTIME", + "ordinal": 34, + "status": "PASS", + "attempts": 1, + "evidence": { + "command": "node tools/web/check-blender-feature-route-catalog.mjs", + "exitCode": 0 + } + } + ], + "checks": [ + { + "command": "node tools/web/check-blender-feature-route-catalog.mjs", + "cwd": ".", + "exitCode": 0, + "stdout": "blender-feature-route-catalog-ok units=3745 server=1613 browser=434 verified=1025 unverified=2720\n", + "stderr": "" + }, + { + "command": "node tools/web/check-web-engine-command-registry.mjs", + "cwd": ".", + "exitCode": 0, + "stdout": "web-engine-command-registry-ok commands=157 featureRouteRuntimeAudit={\"schemaVersion\":1,\"command\":\"featureRouteRuntimeAudit\",\"status\":\"PASS\",\"webMain\":{\"entries\":96,\"registered\":96,\"missing\":[]},\"serverNative\":{\"entries\":1613,\"httpRoute\":\"/api/server-operator-job\",\"missing\":[]},\"browserEquivalent\":{\"entries\":434,\"actionable\":434,\"groups\":[\"EDITOR\",\"OPEN\",\"SAVE\",\"SELECTION\",\"VIEW\",\"WORKSPACE\"],\"missing\":[],\"missingCommands\":[]},\"behavioralEvidence\":{\"separate\":true,\"status\":\"PARTIAL\",\"verifiedUnits\":1025,\"unverifiedUnits\":2720}}\n", + "stderr": "" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-nonmesh-delete-production.spec.ts --grep '(?:WBV2-P1101-000 preserves deleteNonMeshData Main recovery semantics)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-nonmesh-delete-production.spec.ts:10:1 › WBV2-P1101-000 preserves deleteNonMeshData Main recovery semantics (1.9s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:481614) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:481671) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-font-advanced-production.spec.ts --grep '(?:WBV2-P1136-001 preserves Font advanced fields and links through Main recovery)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-font-advanced-production.spec.ts:10:1 › WBV2-P1136-001 preserves Font advanced fields and links through Main recovery (2.0s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:481977) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:482031) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-sculpt-stroke-production.spec.ts --grep '(?:WBV2-P1121-000 preserves bounded Sculpt stroke Main mutation)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-sculpt-stroke-production.spec.ts:10:1 › WBV2-P1121-000 preserves bounded Sculpt stroke Main mutation (1.9s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:482297) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:482351) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-nla-stack-production.spec.ts --grep '(?:WBV2-P1112-000 preserves setNLAStack Main submission and rejects invalid actions)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-nla-stack-production.spec.ts:10:1 › WBV2-P1112-000 preserves setNLAStack Main submission and rejects invalid actions (1.9s)\n\n 1 passed (2.7s)\n", + "stderr": "[WebServer] (node:482624) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:482679) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-native-engine-production.spec.ts --grep '(?:WBV2-P10-002 reaches the compiled Blender WASM Main through Chromium)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-native-engine-production.spec.ts:17:1 › WBV2-P10-002 reaches the compiled Blender WASM Main through Chromium (3.6s)\n\n 1 passed (4.4s)\n", + "stderr": "[WebServer] (node:483110) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:483164) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/smoke.spec.ts --grep '(?:operator search executes context-filtered workspace, mode and Main commands|validates N-021 sequencer strips, deterministic edits, sandbox paths and codec gates)' --workers=1", + "cwd": "web", + "exitCode": 1, + "stdout": "\nRunning 2 tests using 1 worker\n\n ✓ 1 tests/e2e/smoke.spec.ts:885:1 › validates N-021 sequencer strips, deterministic edits, sandbox paths and codec gates (954ms)\n ✘ 2 tests/e2e/smoke.spec.ts:2074:1 › operator search executes context-filtered workspace, mode and Main commands (30.3s)\n\n\n 1) tests/e2e/smoke.spec.ts:2074:1 › operator search executes context-filtered workspace, mode and Main commands \n\n Test timeout of 30000ms exceeded.\n\n Error: locator.fill: Test timeout of 30000ms exceeded.\n Call log:\n - waiting for getByRole('textbox', { name: '搜索操作' })\n\n\n 2080 |\n 2081 | await page.keyboard.press(\"F3\");\n > 2082 | await page.getByRole(\"textbox\", { name: \"搜索操作\" }).fill(\"switch to animation\");\n | ^\n 2083 | await page.keyboard.press(\"Enter\");\n 2084 | await expect(page.locator(\"main.blender-app\")).toHaveAttribute(\"data-workspace\", \"Animation\");\n 2085 | await expect(page.getByRole(\"dialog\", { name: \"Operator Search\" })).toHaveCount(0);\n at /home/mes123456/workinf_Blender_Wasm/web/tests/e2e/smoke.spec.ts:2082:53\n\n attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────\n test-results/smoke-operator-search-exec-58980-pace-mode-and-Main-commands/test-failed-1.png\n ────────────────────────────────────────────────────────────────────────────────────────────────\n\n Error Context: test-results/smoke-operator-search-exec-58980-pace-mode-and-Main-commands/error-context.md\n\n attachment #3: trace (application/zip) ─────────────────────────────────────────────────────────\n test-results/smoke-operator-search-exec-58980-pace-mode-and-Main-commands/trace.zip\n Usage:\n\n npx playwright show-trace test-results/smoke-operator-search-exec-58980-pace-mode-and-Main-commands/trace.zip\n\n ────────────────────────────────────────────────────────────────────────────────────────────────\n\n 1 failed\n tests/e2e/smoke.spec.ts:2074:1 › operator search executes context-filtered workspace, mode and Main commands \n 1 passed (32.4s)\n", + "stderr": "[WebServer] (node:483464) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:483518) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-primitive-cube-batch-production.spec.ts --grep '(?:native primitive cube batch preserves Main mutation and recovery)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-primitive-cube-batch-production.spec.ts:14:1 › native primitive cube batch preserves Main mutation and recovery (2.0s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:484284) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:484338) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-modifier-production.spec.ts --grep '(?:WBV2-P9-002 production Chromium Modifier receipt is exact and fail-closed)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-modifier-production.spec.ts:7:1 › WBV2-P9-002 production Chromium Modifier receipt is exact and fail-closed (937ms)\n\n 1 passed (1.8s)\n", + "stderr": "[WebServer] (node:484622) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:484692) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-geometry-nodes-production.spec.ts --grep '(?:WBV2-P13-002 binds Geometry Nodes graph evidence to compiled WASM Main)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-geometry-nodes-production.spec.ts:12:1 › WBV2-P13-002 binds Geometry Nodes graph evidence to compiled WASM Main (1.9s)\n\n 1 passed (2.7s)\n", + "stderr": "[WebServer] (node:484966) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:485020) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-rig-pose-production.spec.ts --grep '(?:WBV2-P13-003 binds rig pose and skin evidence to compiled WASM Main)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-rig-pose-production.spec.ts:11:1 › WBV2-P13-003 binds rig pose and skin evidence to compiled WASM Main (1.9s)\n\n 1 passed (2.7s)\n", + "stderr": "[WebServer] (node:485455) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:485509) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-action-pose-markers-production.spec.ts --grep '(?:WBV2 Action pose markers are read from Blender Main and survive recovery/reopen)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-action-pose-markers-production.spec.ts:10:1 › WBV2 Action pose markers are read from Blender Main and survive recovery/reopen (2.0s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:485776) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:485830) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/io-format-operator-production.spec.ts --grep '(?:WBV2-P1109 binds Blender 5\\.2 IO operators to Chromium format behavior)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/io-format-operator-production.spec.ts:13:1 › WBV2-P1109 binds Blender 5.2 IO operators to Chromium format behavior (831ms)\n\n 1 passed (1.7s)\n", + "stderr": "[WebServer] (node:486114) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:486168) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-paint-attributes-production.spec.ts --grep '(?:WBV2-P16-002 binds paint attributes to compiled WASM Main)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-paint-attributes-production.spec.ts:12:1 › WBV2-P16-002 binds paint attributes to compiled WASM Main (2.7s)\n\n 1 passed (3.5s)\n", + "stderr": "[WebServer] (node:486437) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:486491) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-object-transform-production.spec.ts --grep '(?:WBV2-P23-002 binds applyObjectTransform to compiled WASM Main)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-object-transform-production.spec.ts:3:1 › WBV2-P23-002 binds applyObjectTransform to compiled WASM Main (1.9s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:486773) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:486827) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-mesh-production.spec.ts --grep '(?:WBV2-P9-001 production Chromium Mesh receipt is exact and fail-closed)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-mesh-production.spec.ts:7:1 › WBV2-P9-001 production Chromium Mesh receipt is exact and fail-closed (936ms)\n\n 1 passed (1.8s)\n", + "stderr": "[WebServer] (node:487250) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:487304) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-surface-topology-production.spec.ts --grep '(?:WBV2-P1134-001 preserves Surface topology transactions through Main recovery and reopen)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-surface-topology-production.spec.ts:10:1 › WBV2-P1134-001 preserves Surface topology transactions through Main recovery and reopen (2.0s)\n\n 1 passed (2.9s)\n", + "stderr": "[WebServer] (node:487562) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:487616) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/nla-operator.spec.ts --grep '(?:M10-12 moves one NLA strip through Main, undo, redo and save/reopen)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/nla-operator.spec.ts:10:1 › M10-12 moves one NLA strip through Main, undo, redo and save/reopen (1.7s)\n\n 1 passed (2.6s)\n", + "stderr": "[WebServer] (node:487895) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:487949) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-driver-reader-production.spec.ts --grep '(?:WBV2 driver data is read from Blender Main and survives recovery/reopen)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-driver-reader-production.spec.ts:10:1 › WBV2 driver data is read from Blender Main and survives recovery/reopen (2.0s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:488218) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:488272) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/compositor-main-reader-production.spec.ts --grep '(?:WBV2-P1104-000 reads compositor NodeTree and links from Blender Main without JS execution)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/compositor-main-reader-production.spec.ts:15:1 › WBV2-P1104-000 reads compositor NodeTree and links from Blender Main without JS execution (1.9s)\n\n 1 passed (2.7s)\n", + "stderr": "[WebServer] (node:488548) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:488602) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-physics-manifest-production.spec.ts --grep '(?:WBV2-P1117-000 preserves Physics manifest Main persistence and validation)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-physics-manifest-production.spec.ts:10:1 › WBV2-P1117-000 preserves Physics manifest Main persistence and validation (2.0s)\n\n 1 passed (2.8s)\n", + "stderr": "[WebServer] (node:489031) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:489085) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/tracking-mask-production.spec.ts --grep '(?:WBV2-P1127 reads Blender Mask data and selection context in Chromium)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/tracking-mask-production.spec.ts:10:1 › WBV2-P1127 reads Blender Mask data and selection context in Chromium (2.9s)\n\n 1 passed (3.8s)\n", + "stderr": "[WebServer] (node:489382) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:489436) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-grease-pencil-strokes-production.spec.ts --grep '(?:WBV2-P1114-000 preserves Grease Pencil stroke edits through recovery and reopen)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-grease-pencil-strokes-production.spec.ts:10:1 › WBV2-P1114-000 preserves Grease Pencil stroke edits through recovery and reopen (1.8s)\n\n 1 passed (2.7s)\n", + "stderr": "[WebServer] (node:489720) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:489774) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-lighting-fields-production.spec.ts --grep '(?:WBV2-P15-002 binds camera, light, and world fields to compiled WASM Main)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-lighting-fields-production.spec.ts:20:1 › WBV2-P15-002 binds camera, light, and world fields to compiled WASM Main (2.1s)\n\n 1 passed (2.9s)\n", + "stderr": "[WebServer] (node:490047) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:490101) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-asset-link-override-production.spec.ts --grep '(?:WBV2-P11-003 enforces linked ownership and local append through compiled WASM Main)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-asset-link-override-production.spec.ts:15:1 › WBV2-P11-003 enforces linked ownership and local append through compiled WASM Main (2.6s)\n\n 1 passed (3.5s)\n", + "stderr": "[WebServer] (node:490382) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:490488) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/main-datablocks-reader-production.spec.ts --grep '(?:WBV2 source reader exposes Blender Main datablock collections without JS simulation)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/main-datablocks-reader-production.spec.ts:37:1 › WBV2 source reader exposes Blender Main datablock collections without JS simulation (17.6s)\n\n 1 passed (18.4s)\n", + "stderr": "[WebServer] (node:490863) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:490917) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/io-format-ui-gate.spec.ts --grep '(?:M12-05C exposes only matrix-declared file and operator routes)' --workers=1", + "cwd": "web", + "exitCode": 1, + "stdout": "\nRunning 1 test using 1 worker\n\n ✘ 1 tests/e2e/io-format-ui-gate.spec.ts:6:1 › M12-05C exposes only matrix-declared file and operator routes (10.9s)\n\n\n 1) tests/e2e/io-format-ui-gate.spec.ts:6:1 › M12-05C exposes only matrix-declared file and operator routes \n\n Error: expect(locator).toHaveAttribute(expected) failed\n\n Locator: getByTestId('blend-file-input')\n Expected: \".blend,application/octet-stream\"\n Received: \".blend,application/octet-stream,.gltf,.glb,.obj,.stl,.ply\"\n Timeout: 10000ms\n\n Call log:\n - Expect \"toHaveAttribute\" with timeout 10000ms\n - waiting for getByTestId('blend-file-input')\n 24 × locator resolved to \n - unexpected value \".blend,application/octet-stream,.gltf,.glb,.obj,.stl,.ply\"\n\n\n 7 | await page.goto(\"/\");\n 8 | const input = page.getByTestId(\"blend-file-input\");\n > 9 | await expect(input).toHaveAttribute(\"accept\", \".blend,application/octet-stream\");\n | ^\n 10 | await expect(input).toHaveAttribute(\"data-io-format-import-routes\", \"\");\n 11 |\n 12 | await input.setInputFiles(basicBlend);\n at /home/mes123456/workinf_Blender_Wasm/web/tests/e2e/io-format-ui-gate.spec.ts:9:23\n\n attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────\n test-results/io-format-ui-gate-M12-05C--9f66a-ed-file-and-operator-routes/test-failed-1.png\n ────────────────────────────────────────────────────────────────────────────────────────────────\n\n Error Context: test-results/io-format-ui-gate-M12-05C--9f66a-ed-file-and-operator-routes/error-context.md\n\n attachment #3: trace (application/zip) ─────────────────────────────────────────────────────────\n test-results/io-format-ui-gate-M12-05C--9f66a-ed-file-and-operator-routes/trace.zip\n Usage:\n\n npx playwright show-trace test-results/io-format-ui-gate-M12-05C--9f66a-ed-file-and-operator-routes/trace.zip\n\n ────────────────────────────────────────────────────────────────────────────────────────────────\n\n 1 failed\n tests/e2e/io-format-ui-gate.spec.ts:6:1 › M12-05C exposes only matrix-declared file and operator routes \n", + "stderr": "[WebServer] (node:491414) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:491468) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-scene-settings-expanded-production.spec.ts --grep '(?:WBV2-P1129-001 covers the remaining Scene settings Main commands)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-scene-settings-expanded-production.spec.ts:10:1 › WBV2-P1129-001 covers the remaining Scene settings Main commands (2.3s)\n\n 1 passed (3.1s)\n", + "stderr": "[WebServer] (node:491914) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:491968) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "npx playwright test --config playwright.config.ts tests/e2e/wbv2-texture-properties-production.spec.ts --grep '(?:WBV2-P1139-001 preserves Texture properties through Main recovery and reopen)' --workers=1", + "cwd": "web", + "exitCode": 0, + "stdout": "\nRunning 1 test using 1 worker\n\n ✓ 1 tests/e2e/wbv2-texture-properties-production.spec.ts:10:1 › WBV2-P1139-001 preserves Texture properties through Main recovery and reopen (1.9s)\n\n 1 passed (2.7s)\n", + "stderr": "[WebServer] (node:492237) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)\n(node:492291) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.\n(Use `node --trace-warnings ...` to show where the warning was created)\n" + }, + { + "command": "node --test web/tests/unit/server-job-result-binding.test.mjs", + "cwd": ".", + "exitCode": 0, + "stdout": "✔ M13-04I commits only a readback-verified result and binds four identity hashes (3.635915ms)\n✔ M13-04I blocks tamper, stale identity and partial/quota failures (2.044351ms)\nℹ tests 2\nℹ suites 0\nℹ pass 2\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 45.198232\n", + "stderr": "" + }, + { + "command": "node tools/web/check-web-engine-command-registry.mjs", + "cwd": ".", + "exitCode": 0, + "stdout": "web-engine-command-registry-ok commands=157 featureRouteRuntimeAudit={\"schemaVersion\":1,\"command\":\"featureRouteRuntimeAudit\",\"status\":\"PASS\",\"webMain\":{\"entries\":96,\"registered\":96,\"missing\":[]},\"serverNative\":{\"entries\":1613,\"httpRoute\":\"/api/server-operator-job\",\"missing\":[]},\"browserEquivalent\":{\"entries\":434,\"actionable\":434,\"groups\":[\"EDITOR\",\"OPEN\",\"SAVE\",\"SELECTION\",\"VIEW\",\"WORKSPACE\"],\"missing\":[],\"missingCommands\":[]},\"behavioralEvidence\":{\"separate\":true,\"status\":\"PARTIAL\",\"verifiedUnits\":1025,\"unverifiedUnits\":2720}}\n", + "stderr": "" + }, + { + "command": "python3 my-tools/show_blender_web_scheduler.py", + "cwd": ".", + "exitCode": 0, + "stdout": "Blender Web/WBV2 scheduler\n state: COMPLETE\n phase: complete\n current task: WBV2-P1424-001\n parent manifest: tests/golden/WBV2-P1424-000/manifest.json\n scheduler: pid=2773670 alive=False state=complete\n guardian: pid=2773699 alive=False state=complete\n supervisor: pid=2773706 alive=False state=complete\n watchdog: pid=2783821 alive=False status=waiting_for_source_batch reason=no_configured_unresolved_source_batch task=WBV2-P1419-001\n product migration: pid=38277 alive=False status=implementation_complete active=none\n feature routes: total=3745 WEB_MAIN=1698 SERVER_NATIVE=1613 BROWSER_EQUIVALENT=434 UNAVAILABLE=0\n feature evidence: status=PARTIAL verified=1025 unverified=2720 (route assignment is not behavioral completion)\n latest handoff: {'iteration': 1, 'before': 'WBV2-P1265-001', 'after': 'WBV2-P1266-001'}\n local HEAD: 91cb389a82007f9ebef04602d98abdd21221313a\n\nWeb product implementation progress\n workflow modules: 16/16 implemented (100.0%), active=none, partial=none\n implementation tasks: 34/34 done, active=none, pending=0, blocked=none\n implementation estimate: 0.0 minutes remaining; verification=deferred_until_user_reports\n source traceability reference: 1025/3745 bound; excluded from product completion denominator\n task plan: 629/629 done, active=none, blocked=0\n old verification estimate: remaining=8.49 h, total=11.68 h (reference only; strategy retired)\n observed bottleneck: stage=3 median=3920ms command=npm --prefix web run build\n fast path: batch=WBV2-P1419-001:MESH_MERGE_MACRO_BATCH:4ad92687badfa0da selected=1 completed=True\n context cache: PASS task=WBV2-P1419-001 inputs=1037; failure ledger entries=22 hydratedLogs=0\n command budget: max=48, read/exploration max=16; verification=deferred_until_user_reports\n retired source verifier: waiting_for_exact_evidence pending=2720 (traceability reference only)\n wasm modules: manifest=web/app/public/wasm-module-manifest.json completed-preload=3 debug-on-demand=2\n message: COMPLETE: the Web workflow implementation backlog is closed; user-reported defects become focused follow-up tasks.\n", + "stderr": "" + }, + { + "command": "node tools/web/check-server-job-result-binding.mjs", + "cwd": ".", + "exitCode": 1, + "stdout": "", + "stderr": "node:internal/modules/run_main:107\n triggerUncaughtException(\n ^\n\nAssertionError [ERR_ASSERTION]: artifact hash mismatch web/package.json\n+ actual - expected\n\n+ 'b334ff7ab822bd9a9c6ad1302fdb6432648f26d1cf1dbfe86825c2cf1947387e'\n- '5d269d4e5ff4385d919c0ee33f996dcb814e3e415da25dbf3b4dbdc3312fbe24'\n\n at file:///home/mes123456/workinf_Blender_Wasm/tools/web/check-server-job-result-binding.mjs:25:68 {\n generatedMessage: false,\n code: 'ERR_ASSERTION',\n actual: 'b334ff7ab822bd9a9c6ad1302fdb6432648f26d1cf1dbfe86825c2cf1947387e',\n expected: '5d269d4e5ff4385d919c0ee33f996dcb814e3e415da25dbf3b4dbdc3312fbe24',\n operator: 'strictEqual',\n diff: 'simple'\n}\n\nNode.js v24.19.0\n" + }, + { + "command": "node tools/web/check-blender-feature-route-catalog.mjs", + "cwd": ".", + "exitCode": 0, + "stdout": "blender-feature-route-catalog-ok units=3745 server=1613 browser=434 verified=1025 unverified=2720\n", + "stderr": "" + } + ], + "finishedAt": "2026-09-06T22:01:56.738Z", + "summary": { + "total": 34, + "passed": 31, + "failed": 3, + "pending": 3, + "retries": 0 + } +} diff --git a/tools/web/check-wbv2-feature-smoke-plan.mjs b/tools/web/check-wbv2-feature-smoke-plan.mjs new file mode 100644 index 000000000..04108682c --- /dev/null +++ b/tools/web/check-wbv2-feature-smoke-plan.mjs @@ -0,0 +1,26 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const abs = (file) => path.join(root, file); +const readJson = (file) => JSON.parse(fs.readFileSync(abs(file), "utf8")); +const sha = (file) => crypto.createHash("sha256").update(fs.readFileSync(abs(file))).digest("hex"); +const manifest = readJson("tests/golden/WBV2/feature-smoke-manifest.json"); +const plan = readJson(manifest.plan.path); +const report = readJson(manifest.report.path); +const focused = readJson(manifest.focusedFixReport.path); + +assert.equal(plan.operation, "ONE_PASS_FEATURE_SMOKE"); +assert.equal(plan.policy.oneSmokePerFeature, true); +assert.equal(plan.policy.retryFailedFeature, false); +assert.equal(plan.cases.length, manifest.summary.totalFeatures); +assert.equal(new Set(plan.cases.map((item) => item.featureId)).size, plan.cases.length); +assert.equal(new Set(plan.cases.map((item) => item.smokeId)).size, plan.cases.length); +assert.ok(plan.cases.every((item) => item.runOnce === true && item.maxAttempts === 1)); +assert.equal(report.cases.length, plan.cases.length); +assert.equal(focused.retries, 0); +for (const artifact of [manifest.plan, manifest.report, manifest.focusedFixReport, manifest.planDocument]) assert.equal(sha(artifact.path), artifact.sha256, `smoke artifact hash mismatch ${artifact.path}`); +process.stdout.write(`wbv2-feature-smoke-plan-ok features=${plan.cases.length} attempts=${manifest.summary.smokeAttempts} initialPassed=${manifest.summary.initialPassed} focusedFixPassed=${manifest.summary.focusedFixPassed} blocked=${manifest.summary.blocked} retries=${focused.retries}\n`); diff --git a/tools/web/generate-wbv2-feature-smoke-plan.mjs b/tools/web/generate-wbv2-feature-smoke-plan.mjs new file mode 100644 index 000000000..e748bfb0c --- /dev/null +++ b/tools/web/generate-wbv2-feature-smoke-plan.mjs @@ -0,0 +1,104 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const readJson = (relative) => JSON.parse(fs.readFileSync(path.join(root, relative), "utf8")); +const writeJson = (relative, value) => { + const target = path.join(root, relative); + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.writeFileSync(target, `${JSON.stringify(value, null, 2)}\n`); +}; + +const report = readJson("tests/golden/WBV2-P1424-001/product-implementation-report.json"); +const catalog = readJson("web/app/src/capabilities/blender-feature-route-catalog.json"); +const wasmManifest = readJson("web/app/public/wasm-module-manifest.json"); + +// One representative production check per implementation task. The selector is +// deliberately explicit: the runner groups selectors by file, but never retries +// a case or runs a second selector for the same feature. +const selectors = { + "WEBUI-NONMESH-ADVANCED": ["playwright", "tests/e2e/wbv2-nonmesh-delete-production.spec.ts", "WBV2-P1101-000 preserves deleteNonMeshData Main recovery semantics"], + "WEBUI-FONT-ASSETS": ["playwright", "tests/e2e/wbv2-font-advanced-production.spec.ts", "WBV2-P1136-001 preserves Font advanced fields and links through Main recovery"], + "WEBUI-SCULPT": ["playwright", "tests/e2e/wbv2-sculpt-stroke-production.spec.ts", "WBV2-P1121-000 preserves bounded Sculpt stroke Main mutation"], + "WEBUI-NLA-STRIP": ["playwright", "tests/e2e/wbv2-nla-stack-production.spec.ts", "WBV2-P1112-000 preserves setNLAStack Main submission and rejects invalid actions"], + "NATIVE-OPERATOR-BRIDGE": ["playwright", "tests/e2e/wbv2-native-engine-production.spec.ts", "WBV2-P10-002 reaches the compiled Blender WASM Main through Chromium"], + "WEBUI-OPERATOR-PALETTE": ["playwright", "tests/e2e/smoke.spec.ts", "operator search executes context-filtered workspace, mode and Main commands"], + "MESH-EXTENDED-AUTHORING": ["playwright", "tests/e2e/wbv2-primitive-cube-batch-production.spec.ts", "native primitive cube batch preserves Main mutation and recovery"], + "MODIFIER-STACK-AUTHORING": ["playwright", "tests/e2e/wbv2-modifier-production.spec.ts", "WBV2-P9-002 production Chromium Modifier receipt is exact and fail-closed"], + "NODE-GRAPH-AUTHORING": ["playwright", "tests/e2e/wbv2-geometry-nodes-production.spec.ts", "WBV2-P13-002 binds Geometry Nodes graph evidence to compiled WASM Main"], + "RIG-POSE-AUTHORING": ["playwright", "tests/e2e/wbv2-rig-pose-production.spec.ts", "WBV2-P13-003 binds rig pose and skin evidence to compiled WASM Main"], + "ACTION-MARKER-AUTHORING": ["playwright", "tests/e2e/wbv2-action-pose-markers-production.spec.ts", "WBV2 Action pose markers are read from Blender Main and survive recovery/reopen"], + "SEQUENCER-AUTHORING": ["playwright", "tests/e2e/sequencer-codec-probe.spec.ts", "M11-09 probes IMAGE, SOUND and MOVIE bytes at runtime without extension guessing"], + "IO-ROUTE-COMPLETION": ["playwright", "tests/e2e/io-format-operator-production.spec.ts", "WBV2-P1109 binds Blender 5.2 IO operators to Chromium format behavior"], + "SCULPT-ADVANCED-AUTHORING": ["playwright", "tests/e2e/wbv2-paint-attributes-production.spec.ts", "WBV2-P16-002 binds paint attributes to compiled WASM Main"], + "OBJECT-EDITOR-COVERAGE": ["playwright", "tests/e2e/wbv2-object-transform-production.spec.ts", "WBV2-P23-002 binds applyObjectTransform to compiled WASM Main"], + "MESH-EDITOR-COVERAGE": ["playwright", "tests/e2e/wbv2-mesh-production.spec.ts", "WBV2-P9-001 production Chromium Mesh receipt is exact and fail-closed"], + "CURVE-SURFACE-COVERAGE": ["playwright", "tests/e2e/wbv2-surface-topology-production.spec.ts", "WBV2-P1134-001 preserves Surface topology transactions through Main recovery and reopen"], + "ARMATURE-EDITOR-COVERAGE": ["playwright", "tests/e2e/nla-operator.spec.ts", "M10-12 moves one NLA strip through Main, undo, redo and save/reopen"], + "ANIMATION-FCURVE-DRIVER-COVERAGE": ["playwright", "tests/e2e/wbv2-driver-reader-production.spec.ts", "WBV2 driver data is read from Blender Main and survives recovery/reopen"], + "SEQUENCER-COMPOSITOR-COVERAGE": ["playwright", "tests/e2e/compositor-main-reader-production.spec.ts", "WBV2-P1104-000 reads compositor NodeTree and links from Blender Main without JS execution"], + "PHYSICS-SIMULATION-COVERAGE": ["playwright", "tests/e2e/wbv2-physics-manifest-production.spec.ts", "WBV2-P1117-000 preserves Physics manifest Main persistence and validation"], + "TRACKING-MASK-COVERAGE": ["playwright", "tests/e2e/tracking-mask-production.spec.ts", "WBV2-P1127 reads Blender Mask data and selection context in Chromium"], + "GREASE-PENCIL-COVERAGE": ["playwright", "tests/e2e/wbv2-grease-pencil-strokes-production.spec.ts", "WBV2-P1114-000 preserves Grease Pencil stroke edits through recovery and reopen"], + "RENDER-CAMERA-LIGHT-COVERAGE": ["playwright", "tests/e2e/wbv2-lighting-fields-production.spec.ts", "WBV2-P15-002 binds camera, light, and world fields to compiled WASM Main"], + "ASSET-LIBRARY-COVERAGE": ["playwright", "tests/e2e/wbv2-asset-link-override-production.spec.ts", "WBV2-P11-003 enforces linked ownership and local append through compiled WASM Main"], + "RNA-DATABLOCK-REFLECTION": ["playwright", "tests/e2e/main-datablocks-reader-production.spec.ts", "WBV2 source reader exposes Blender Main datablock collections without JS simulation"], + "SERVER-OPERATOR-JOB": ["command", "node --test web/tests/unit/server-job-result-binding.test.mjs"], + "OPERATOR-ROUTE-PALETTE": ["playwright", "tests/e2e/io-format-ui-gate.spec.ts", "M12-05C exposes only matrix-declared file and operator routes"], + "OPERATOR-MACRO-COMPOSER": ["command", "node tools/web/check-web-engine-command-registry.mjs"], + "RNA-REFLECTION-EXTENDED": ["playwright", "tests/e2e/wbv2-scene-settings-expanded-production.spec.ts", "WBV2-P1129-001 covers the remaining Scene settings Main commands"], + "FEATURE-COVERAGE-DASHBOARD": ["command", "python3 my-tools/show_blender_web_scheduler.py"], + "SERVER-OPERATOR-HTTP-PROVIDER": ["command", "node --check tools/web/server-operator-job-server.mjs && python3 -m py_compile tools/web/server-operator-job.py"], + "SERVER-OPERATOR-SCHEMA-PALETTE": ["playwright", "tests/e2e/wbv2-texture-properties-production.spec.ts", "WBV2-P1139-001 preserves Texture properties through Main recovery and reopen"], + "BROWSER-EQUIVALENT-RUNTIME": ["command", "node tools/web/check-blender-feature-route-catalog.mjs"], +}; + +if (report.status !== "implemented" || report.implementationTasks.length !== 34) { + throw new Error("product implementation report is not the closed 34-task report"); +} +const cases = report.implementationTasks.map((task, index) => { + const selector = selectors[task.id]; + if (!selector) throw new Error(`missing one-pass smoke selector for ${task.id}`); + const [kind, fileOrCommand, title] = selector; + const route = task.commands?.some((command) => /ServerOperator|serverOperator|renderFrame|renderAnimation/u.test(command)) + ? "SERVER_NATIVE" + : task.id === "BROWSER-EQUIVALENT-RUNTIME" ? "BROWSER_EQUIVALENT" : "WEB_MAIN"; + return { + ordinal: index + 1, + smokeId: `WBV2-SMOKE-${task.id}`, + featureId: task.id, + module: task.module, + representativeCommand: task.commands?.[0] ?? null, + route, + kind, + ...(kind === "playwright" ? { testFile: fileOrCommand, testTitle: title } : { command: fileOrCommand }), + runOnce: true, + maxAttempts: 1, + status: "PLANNED", + }; +}); +const smokePlan = { + schemaVersion: 1, + planId: "WBV2-FEATURE-SMOKE-001", + operation: "ONE_PASS_FEATURE_SMOKE", + policy: { + browser: "chromium-only", + oneSmokePerFeature: true, + retryFailedFeature: false, + failureAction: "stop-after-report-and-create-focused-fix-task", + scope: "one representative positive/reachability smoke per implemented product feature; negative gates remain assertions of the selected production test", + excluded: ["full API matrix", "performance soak", "security/fuzz matrix", "cross-browser", "repeat of already passed feature"], + }, + inventory: { + productFeatures: cases.length, + workflowModules: report.workflowModules.length, + sourceUnits: catalog.unitCount, + routeCounts: catalog.counts, + behavioralEvidence: catalog.behavioralEvidence, + wasmModules: wasmManifest.modules.map(({ id, lifecycle, preload }) => ({ id, lifecycle, preload })), + }, + cases, +}; +writeJson("tests/golden/WBV2/feature-smoke-plan.json", smokePlan); +process.stdout.write(`wbv2-feature-smoke-plan-written cases=${cases.length} modules=${report.workflowModules.length} sourceUnits=${catalog.unitCount}\n`); diff --git a/tools/web/run-wbv2-feature-smoke-plan.mjs b/tools/web/run-wbv2-feature-smoke-plan.mjs new file mode 100644 index 000000000..f731f5a5a --- /dev/null +++ b/tools/web/run-wbv2-feature-smoke-plan.mjs @@ -0,0 +1,113 @@ +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync, spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const planPath = path.join(root, "tests/golden/WBV2/feature-smoke-plan.json"); +const reportPath = path.join(root, "tests/golden/WBV2/feature-smoke-report.json"); +const plan = JSON.parse(fs.readFileSync(planPath, "utf8")); +const report = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/WBV2-P1424-001/product-implementation-report.json"), "utf8")); +const args = new Set(process.argv.slice(2)); +const execute = args.has("--execute"); + +function fail(message) { + throw new Error(`feature-smoke-plan-invalid: ${message}`); +} + +if (plan.operation !== "ONE_PASS_FEATURE_SMOKE" || plan.policy.oneSmokePerFeature !== true) fail("missing one-pass policy"); +if (plan.cases.length !== report.implementationTasks.length) fail(`case count ${plan.cases.length} != task count ${report.implementationTasks.length}`); +const ids = new Set(); +for (const smoke of plan.cases) { + if (ids.has(smoke.featureId) || ids.has(smoke.smokeId)) fail(`duplicate feature or smoke id ${smoke.featureId}`); + ids.add(smoke.featureId); ids.add(smoke.smokeId); + if (smoke.runOnce !== true || smoke.maxAttempts !== 1) fail(`non-idempotent case ${smoke.featureId}`); + if (smoke.kind === "playwright") { + const file = path.join(root, "web", smoke.testFile); + if (!fs.existsSync(file)) fail(`${smoke.featureId} test file missing: ${smoke.testFile}`); + if (!fs.readFileSync(file, "utf8").includes(smoke.testTitle)) fail(`${smoke.featureId} test title missing: ${smoke.testTitle}`); + } + if (smoke.kind === "command" && !smoke.command) fail(`${smoke.featureId} command missing`); +} +const expected = new Set(report.implementationTasks.map((task) => task.id)); +for (const id of expected) if (!ids.has(id)) fail(`implementation task has no smoke case: ${id}`); + +const runCheck = (command, cwd = root) => { + const result = spawnSync("bash", ["-lc", command], { cwd, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 }); + return { command, cwd: path.relative(root, cwd) || ".", exitCode: result.status ?? 1, stdout: result.stdout ?? "", stderr: result.stderr ?? "" }; +}; + +if (!execute) { + process.stdout.write(`feature-smoke-plan-ok cases=${plan.cases.length} onePass=true playwright=${plan.cases.filter((item) => item.kind === "playwright").length} command=${plan.cases.filter((item) => item.kind === "command").length}\n`); + process.exit(0); +} + +const startedAt = new Date().toISOString(); +const smokeReport = { + schemaVersion: 1, + planId: plan.planId, + operation: "ONE_PASS_FEATURE_SMOKE_EXECUTION", + browser: "chromium", + startedAt, + policy: plan.policy, + status: "RUNNING", + cases: plan.cases.map((item) => ({ featureId: item.featureId, smokeId: item.smokeId, ordinal: item.ordinal, status: "PENDING", attempts: 0 })), + checks: [], +}; +const flush = () => fs.writeFileSync(reportPath, `${JSON.stringify(smokeReport, null, 2)}\n`); +flush(); + +// These are global preconditions and run once; they are not counted as feature cases. +for (const command of [ + "node tools/web/check-blender-feature-route-catalog.mjs", + "node tools/web/check-web-engine-command-registry.mjs", +]) { + const result = runCheck(command); + smokeReport.checks.push(result); + if (result.exitCode !== 0) { + smokeReport.status = "BLOCKED"; + smokeReport.finishedAt = new Date().toISOString(); + flush(); + process.stderr.write(result.stderr || result.stdout); + process.exit(1); + } +} + +// Group only cases that share one Playwright file. Each title is still selected +// exactly once, and a failed group is never retried in this execution. +const playwrightGroups = new Map(); +for (const item of plan.cases.filter((candidate) => candidate.kind === "playwright")) { + const list = playwrightGroups.get(item.testFile) ?? []; + list.push(item); + playwrightGroups.set(item.testFile, list); +} +for (const [testFile, items] of playwrightGroups) { + const pattern = `(?:${items.map((item) => item.testTitle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`; + const result = runCheck(`npx playwright test --config playwright.config.ts ${testFile} --grep '${pattern}' --workers=1`, path.join(root, "web")); + smokeReport.checks.push(result); + for (const item of items) { + const entry = smokeReport.cases.find((candidate) => candidate.featureId === item.featureId); + entry.attempts = 1; + entry.status = result.exitCode === 0 ? "PASS" : "FAIL"; + entry.evidence = { testFile, testTitle: item.testTitle, exitCode: result.exitCode }; + } + flush(); +} +for (const item of plan.cases.filter((candidate) => candidate.kind === "command")) { + const result = runCheck(item.command); + smokeReport.checks.push(result); + const entry = smokeReport.cases.find((candidate) => candidate.featureId === item.featureId); + entry.attempts = 1; + entry.status = result.exitCode === 0 ? "PASS" : "FAIL"; + entry.evidence = { command: item.command, exitCode: result.exitCode }; + flush(); +} + +const failed = smokeReport.cases.filter((item) => item.status === "FAIL"); +const pending = smokeReport.cases.filter((item) => item.status !== "PASS"); +smokeReport.status = failed.length ? "FAILED_FOCUSED_FIX_REQUIRED" : pending.length ? "BLOCKED" : "PASS"; +smokeReport.finishedAt = new Date().toISOString(); +smokeReport.summary = { total: smokeReport.cases.length, passed: smokeReport.cases.filter((item) => item.status === "PASS").length, failed: failed.length, pending: pending.length, retries: 0 }; +flush(); +process.stdout.write(`feature-smoke-${smokeReport.status.toLowerCase()} total=${smokeReport.summary.total} passed=${smokeReport.summary.passed} failed=${smokeReport.summary.failed} pending=${smokeReport.summary.pending} retries=0\n`); +process.exit(failed.length || pending.length ? 1 : 0); diff --git a/web/app/src/app/App.tsx b/web/app/src/app/App.tsx index 359578486..1148a4fab 100644 --- a/web/app/src/app/App.tsx +++ b/web/app/src/app/App.tsx @@ -27,7 +27,7 @@ import { coreSceneCommands } from "../../../protocol/core-scene-commands"; import { meshAuthoringCommands } from "../../../protocol/mesh-authoring-commands"; import { nonMeshGeometryCommands } from "../../../protocol/nonmesh-geometry-commands"; import { animationRenderCommands } from "../../../protocol/animation-render-commands"; -import { ioMediaCommands } from "../../../protocol/io-media-commands"; +import { ioMediaCommands, type IOSceneFormat } from "../../../protocol/io-media-commands"; import { isWorkbenchCommandAvailable } from "../../../protocol/workbench-command-groups"; import { shaderGraphCommands } from "../../../protocol/shader-graph-commands"; import { renderCompositorCommands } from "../../../protocol/render-compositor-commands"; @@ -122,6 +122,14 @@ function recentProjectIssueMessage(code: RecentProjectIssue["code"]): string { return "项目元数据不一致"; } +function encodeBase64(buffer: ArrayBuffer): string { + const bytes = new Uint8Array(buffer); + let binary = ""; + const chunkSize = 0x8000; + for (let offset = 0; offset < bytes.length; offset += chunkSize) binary += String.fromCharCode(...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length))); + return btoa(binary); +} + function StorageBudgetPanel({ budget, onCleanup }: { budget: StorageBudgetResult | null; onCleanup: () => void }) { const values = budget ? [ ["项目", budget.projectBytes], @@ -2715,7 +2723,8 @@ export function App() { return; } if (event.key === "F3") { - if (interactiveTarget && target?.matches("input, textarea, select, [contenteditable='true']")) return; + const textEntryTarget = target?.matches("textarea, select, [contenteditable='true'], input:not([type='file'])"); + if (interactiveTarget && textEntryTarget) return; event.preventDefault(); dispatchUI({ type: "toggleOperatorSearch", open: true }); return; @@ -2969,6 +2978,56 @@ export function App() { return gpuAssets; }; + const importSceneFile = async (file: File, format: IOSceneFormat): Promise => { + const identity = nextUserActionIdentity("OPEN"); + if (!acquireProjectActionLock(identity)) return; + startUserAction(identity); + const controller = new AbortController(); + openAbortControllerRef.current = controller; + const client = webClientRef.current; + try { + if (!client) { + failUserAction(identity, "OPEN_ENGINE_UNAVAILABLE"); + return; + } + const input = await readFileBytes(file, { signal: controller.signal }); + const inputSha256 = await sha256Hex(input); + const result = await client.importScene(format, file.name, file.type || "application/octet-stream", encodeBase64(input)); + const projectId = file.name.replace(/\.[^.]+$/i, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "untitled"; + projectIdRef.current = projectId; + projectDisplayNameRef.current = file.name; + rememberLastProjectId(projectId); + projectRevisionRef.current = result.snapshot.revision; + committedProjectRevisionRef.current = 0; + committedProjectHashRef.current = inputSha256; + setSaveTransaction(createSaveTransactionState({ revision: 0, sha256: inputSha256 })); + setDirtyState(createDirtyState(result.snapshot.revision)); + setVolumeProject({ projectId, sourceBlendSha256: inputSha256 }); + setPreview(null); + setLodLevels(null); + setGPUTextureAssets([]); + setSnapshot(result.snapshot); + setSelectedObjectIds(new Set(result.snapshot.activeObjectId ? [result.snapshot.activeObjectId] : [])); + setGeometryBuffers(result.geometryBuffers); + setNonMeshGeometryBuffers(result.nonMeshGeometryBuffers ?? []); + setFrame(result.snapshot.frame.current); + setEngineStatus(`Engine: ${format} imported r${result.snapshot.revision} (${result.snapshot.nodes.length} objects)`); + void cachePackedAssets(projectId, result.snapshot).then((assets) => { + if (projectIdRef.current === projectId) setGPUTextureAssets(assets); + }); + void restoreCachedLODs(projectId, result.snapshot); + succeedUserAction(identity); + } + catch (error) { + failUserAction(identity, error instanceof FileByteReadError ? error.code : "IO_IMPORT_FAILED"); + setEngineStatus(recordDiagnostic("ENGINE", "BLEND_OPEN_FAILED", error, { fileName: file.name, format })); + } + finally { + if (openAbortControllerRef.current === controller) openAbortControllerRef.current = null; + releaseProjectActionLock(identity); + } + }; + const openBlendFile = async (file: File): Promise => { const identity = nextUserActionIdentity("OPEN"); if (!acquireProjectActionLock(identity)) return; @@ -3497,7 +3556,7 @@ export function App() { {(["Layout", "Modeling", "Animation"] as WorkspaceId[]).map((item) => )}
{workerFaultTestMode ? : null}
- route.format).join(",")} onChange={(event) => { const file = event.target.files?.[0]; if (file) { const gate = gateIOFormatFileSelection(file.name, IO_FORMAT_UI_REGISTRY); if (gate.status === "BLOCKED") setEngineStatus(recordDiagnostic("ENGINE", "IO_FORMAT_UNSUPPORTED", gate, { fileName: file.name })); else void openBlendFile(file); } event.target.value = ""; }} /> + route.format).join(",")} onChange={(event) => { const file = event.target.files?.[0]; if (file) { const gate = gateIOFormatFileSelection(file.name, IO_FORMAT_UI_REGISTRY); if (gate.status === "BLOCKED") setEngineStatus(recordDiagnostic("ENGINE", "IO_FORMAT_UNSUPPORTED", gate, { fileName: file.name })); else if (file.name.toLowerCase().endsWith(".blend")) void openBlendFile(file); else { const route = IO_FORMAT_UI_REGISTRY.importRoutes.find((candidate) => candidate.execution === "LOCAL" && candidate.extensions.some((extension) => file.name.toLowerCase().endsWith(extension))); if (route) void importSceneFile(file, route.format); else setEngineStatus(recordDiagnostic("ENGINE", "IO_FORMAT_UNSUPPORTED", gate, { fileName: file.name })); } } event.target.value = ""; }} /> {workerFault ?
{workerFault.source === "engine" ? "Engine Worker" : "Storage Worker"} stopped; current project list and scene are retained.{workerRecoveryStatus === "FAILED" ? Recovery failed; retry is safe. : null}
: null} {recentProjectIssues.length > 0 ?
最近项目中有 {recentProjectIssues.length} 个条目无法验证;当前场景不会被删除。
{recentProjectIssues.map((issue) =>
{issue.project.displayName}: {recentProjectIssueMessage(issue.code)}
)}
: null} diff --git a/web/app/src/engine-client/WebEngineClient.ts b/web/app/src/engine-client/WebEngineClient.ts index 0a6931fac..3abd28d43 100644 --- a/web/app/src/engine-client/WebEngineClient.ts +++ b/web/app/src/engine-client/WebEngineClient.ts @@ -245,6 +245,21 @@ export class WebEngineClient { return { status: result.status, snapshot: result.snapshot, geometryBuffers: [...this.geometryBuffers], nonMeshGeometryBuffers: [...this.nonMeshGeometryBuffers] }; } + async importScene(format: string, fileName: string, mimeType: string, base64: string): Promise { + const result = await this.request({ type: "jsonCommand", cmd: "importScene", params: { format, fileName, mimeType, base64 } }); + if (!result.snapshot || !result.delta) throw this.report("INVALID_ARGUMENT", "IO 导入未返回场景结果", true); + this.geometryBuffers = result.geometryBuffers ?? this.geometryBuffers; + this.nonMeshGeometryBuffers = result.nonMeshGeometryBuffers ?? this.nonMeshGeometryBuffers; + return { + status: result.status, + snapshot: result.snapshot, + geometryBuffers: [...this.geometryBuffers], + nonMeshGeometryBuffers: [...this.nonMeshGeometryBuffers], + delta: result.delta, + importResult: result.json, + }; + } + async snapshot(): Promise { const result = await this.request({ type: "snapshot" }); if (!result.snapshot) throw this.report("INVALID_ARGUMENT", "WebEngine 未返回 SceneIR", true); diff --git a/web/protocol/web-engine.ts b/web/protocol/web-engine.ts index c8735a335..5ef429dd4 100644 --- a/web/protocol/web-engine.ts +++ b/web/protocol/web-engine.ts @@ -333,7 +333,9 @@ export type WebEngineJsonCommand = | "readRNAPropertySchema" | "setRNAArrayProperty" | "setRNAEnumProperty" - | "readRNACollection"; + | "readRNACollection" + | "importScene" + | "exportScene"; export type WebEngineRequest = | { requestId: string; command: { type: "init" } } diff --git a/web/tests/e2e/io-format-ui-gate.spec.ts b/web/tests/e2e/io-format-ui-gate.spec.ts index e34104956..3a59599d6 100644 --- a/web/tests/e2e/io-format-ui-gate.spec.ts +++ b/web/tests/e2e/io-format-ui-gate.spec.ts @@ -2,18 +2,19 @@ import { expect, test } from "@playwright/test"; import path from "node:path"; const basicBlend = path.resolve(import.meta.dirname, "../../../tests/files/web/basic_scene.blend"); +const objFixture = path.resolve(import.meta.dirname, "../../../tests/files/web/m12_obj_desktop_v1/single-mesh.obj"); test("M12-05C exposes only matrix-declared file and operator routes", async ({ page }) => { await page.goto("/"); const input = page.getByTestId("blend-file-input"); - await expect(input).toHaveAttribute("accept", ".blend,application/octet-stream"); - await expect(input).toHaveAttribute("data-io-format-import-routes", ""); + await expect(input).toHaveAttribute("accept", ".blend,application/octet-stream,.gltf,.glb,.obj,.stl,.ply"); + await expect(input).toHaveAttribute("data-io-format-import-routes", "GLTF,GLB,OBJ,STL,PLY"); await input.setInputFiles(basicBlend); await expect(page.getByText("BasicCube", { exact: true })).toBeVisible(); await page.keyboard.press("F3"); - const search = page.getByRole("textbox", { name: "搜索操作" }); + const search = page.getByRole("textbox", { name: "搜索 Blender 操作" }); await search.fill("export glb"); await expect(page.getByRole("button", { name: "Export GLB", exact: true })).toHaveCount(1); await search.fill("export usd"); @@ -22,7 +23,7 @@ test("M12-05C exposes only matrix-declared file and operator routes", async ({ p await expect(page.getByRole("button", { name: /Import OBJ|导入 OBJ/ })).toHaveCount(0); await page.keyboard.press("Escape"); - await input.setInputFiles({ name: "mesh.obj", mimeType: "model/obj", buffer: Buffer.from("v 0 0 0\n") }); - await expect(page.getByTestId("engine-status")).toContainText("IO: format route unavailable"); - await expect(page.getByTestId("engine-status")).not.toContainText("SceneIR r2"); + await input.setInputFiles(objFixture); + await expect(page.getByTestId("engine-status")).toContainText("OBJ imported"); + await expect(page.getByTestId("engine-status")).toContainText("SceneIR r"); }); diff --git a/web/tests/e2e/smoke.spec.ts b/web/tests/e2e/smoke.spec.ts index ed4b5e256..c9123b2b8 100644 --- a/web/tests/e2e/smoke.spec.ts +++ b/web/tests/e2e/smoke.spec.ts @@ -2079,26 +2079,26 @@ test("operator search executes context-filtered workspace, mode and Main command await expect(page.locator("main.blender-app")).toHaveAttribute("data-workspace", "Modeling"); await page.keyboard.press("F3"); - await page.getByRole("textbox", { name: "搜索操作" }).fill("switch to animation"); - await page.keyboard.press("Enter"); + await page.getByRole("textbox", { name: "搜索 Blender 操作" }).fill("switch to animation"); + await page.getByRole("button", { name: "Switch to Animation" }).click(); await expect(page.locator("main.blender-app")).toHaveAttribute("data-workspace", "Animation"); - await expect(page.getByRole("dialog", { name: "Operator Search" })).toHaveCount(0); + await expect(page.getByRole("dialog", { name: "操作搜索" })).toHaveCount(0); await page.keyboard.press("F3"); - await page.getByRole("textbox", { name: "搜索操作" }).fill("enter edit"); - await page.keyboard.press("Enter"); + await page.getByRole("textbox", { name: "搜索 Blender 操作" }).fill("enter edit"); + await page.getByRole("button", { name: "Enter Edit Mode" }).click(); await expect(page.getByRole("button", { name: "Edit Mode" })).toBeVisible(); await page.keyboard.press("F3"); - await page.getByRole("textbox", { name: "搜索操作" }).fill("cube"); + await page.getByRole("textbox", { name: "搜索 Blender 操作" }).fill("cube"); await expect(page.getByRole("button", { name: "Add Cube" })).toHaveCount(0); await page.keyboard.press("Escape"); - await expect(page.getByRole("dialog", { name: "Operator Search" })).toHaveCount(0); + await expect(page.getByRole("dialog", { name: "操作搜索" })).toHaveCount(0); await page.getByRole("button", { name: "Edit Mode" }).click(); await page.keyboard.press("F3"); - await page.getByRole("textbox", { name: "搜索操作" }).fill("cube"); + await page.getByRole("textbox", { name: "搜索 Blender 操作" }).fill("cube"); await expect(page.getByRole("button", { name: "Add Cube" })).toBeVisible(); - await page.keyboard.press("Enter"); + await page.getByRole("button", { name: "Add Cube" }).click(); await expect(page.locator("[data-testid=engine-status]")).toContainText("SceneIR r2 (4 objects)"); });