Files
workinf_Blender_Wasm/docs/status/WBV2-P1425-001.md
mes123456 d1e6b63d54
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender feature-gap work, docs and tests
- update native web_engine sources and rebuilt wasm artifacts
- add task cards, status notes and tooling scripts
- add web e2e specs, tutorial UI sources and tutorial assets
- sync golden task manifests and audit records

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-11 10:46:55 +08:00

28 KiB
Raw Blame History

WBV2-P1425-001 status

  • task: WBV2-P1425-001; module: OBJECT_ADD_ROUTE; status: in_progress
  • creation
    • Add menu uses web_blender.desktop_operator(native=true) with revision/scene/area/mode/selection context.
    • Mesh primitives incl. Circle/Grid/Monkey; NURBS curves/surfaces; non-mesh types and enum submenus; Empty display types; Grease Pencil line-art types; random hair; Edit-mode Add; generic F3; quick-toolbar primitives.
  • hierarchy
    • Commands/UI: moveCollection (including sibling ordering), deleteCollection (normal/recursive), createCollection child, setActiveCollection, setScene, setSceneCollectionSettings.
    • Outliner collection drag/drop; Collection Properties delete/child/active/instance/up/down; per-ViewLayer exclude/holdout/indirectOnly/hide; topbar scene selector and scene delete; view layer add/remove; parent types and clear modes.
  • modeling
    • Mesh element selection bridge and post-operator readback; curve control-point selection bridge.
    • Broad mesh/curve/UV operator coverage; selection-aware modal tools; UV projection/select/stitch/pack; vertex groups; shape keys; modifier Apply/Shape Key/Duplicate/Copy; object shading/convert/origin/transform/data/pivot/snap.
    • Modifier whitelist expanded by 20 source-backed types: WeightedNormal, CorrectiveSmooth, LaplacianSmooth, Mask, NormalEdit, Skin, UVProject, UVWarp, Warp, DataTransfer, WeightVGEdit, WeightVGMix, WeightVGProximity, LaplacianDeform, ParticleInstance, Surface, MeshCache, MeshSequenceCache, Multires, Explode.
  • implementation coverage: 486 operator units in five families have an implementation route (317 menu/modal, 26 typed command, 143 F3 operator palette). MESH_OT_ 164/164, OBJECT_OT_ 215/215, CURVE_OT_ 51/51, SURFACE_OT_ 6/6, UV_OT_ 50/50 classified WEB_MAIN in the route catalog; behavioral evidence remains separate and UNVERIFIED for the broader catalog.
  • artifacts: implemented-routes.json, implemented-unit-bindings.json, implementation-summary.json, docs/plans/WEB_BLENDER_THREE_DOMAIN_VALIDATION_CHECKLIST.md.
  • build: WASM rebuilt with the project environment and absolute paths; runtime synced (wasm SHA-256 8ec2268f73584215419b72292cda0bc8fb055a9e9044224722077bfe3f43987d). Command registry check PASS (170 commands, 161 WEB_MAIN routes). Frontend incremental build passed with WBV2_SKIP_TYPECHECK=1.
  • blockers fixed in this round:
    • editors/mesh registers mesh.convex_hull under WITH_BULLET, but the Web bf_bmesh target omitted Bullet and therefore did not register the convex_hull BMO. Executing it ran a null operator type and crashed WASM (table index is out of bounds); SKIN used the same path. The WITH_WEB branch of bmesh/CMakeLists.txt now links bf_intern_rigidbody and bf::dependencies::optional::bullet.
    • Speaker distance_max defaults to FLT_MAX in DNA while SceneIR budgets it to 1e9; the blend reader now clamps distance fields, and empty actionSlotIdentifier values (sound strips created by Speaker Add) are omitted instead of failing the schema.
    • Add → 数据实例 now resolves the active object's data-block name and ID type from SceneIR before invoking object.data_instance_add (invoking it operand-less is correctly cancelled).
    • Embedded the Essentials geometry-node bundles (geometry_nodes_essentials, geometry_nodes_dynamics_assets, procedural_hair_node_assets + catalog) under /blender/datafiles/assets. The browser asset index does not list packaged-FS files, so add_essential_asset_modifier now falls back to appending the requested NodeTree directly from the embedded .blend; hair curves Add completes with the Capture Rest Geometry and Hair Dynamics modifiers.
    • Modifier parameter changes only wrote DNA and left the embedded Python depsgraph’s evaluated copy stale, so object.modifier_apply baked the previous level. Updated parameter publication now uses the concrete modifier RNA struct, full recalc flags (GEOMETRY | SYNC_TO_EVAL | PARAMETERS) and a notifier, re-tags after the SceneIR refresh, and the operator bootstrap re-syncs modifier RNA once before native execution. SUBSURF levels 2 evaluates 98 vertices, Apply bakes 98, and save/reopen keeps 98.
    • Saving while an object was in Edit mode omitted the edit data (e.g. Armature edit-bones). The save export now calls ED_editors_flush_edits before serialization, matching desktop save; added/subdivided/duplicated bones and hair-curves modifiers persist across save/reopen.
    • SceneIR published every Object in Main, so a newly created empty scene still listed the other scene’s objects and native operators failed with OPERATOR_SELECTION_CONTEXT_UNAVAILABLE. The reader now emits nodes only for objects reachable from the active scene’s master collection, and setScene also points every wmWindow at the selected scene. scene.new → switch → scene.delete now publishes 0 nodes for the empty scene, 3 for the original and returns to Scene after deletion.
    • Library append read the source from memory but left a Library entry pointing at a browser locator; snapshot/save packing could not find that path and failed with LIBRARY_DEPENDENCY_UNAVAILABLE. The append path now retains the uploaded source bytes in recovery_library_bytes keyed by the missing-library path, so append, undo/redo, save and reopen work.
    • Collection instances published only an EMPTY owner. The reader now emits instanceCollectionId, SceneNodeIR validates it, and the Three.js viewport recursively expands nested collection instances with composed matrices, respecting collection/member visibility and depth/cycle limits. Display copies share member geometry/materials; the viewport also draws camera frustums and empty axes, so instanced cameras and empties are visible. Picking reports the nearest instance owner, so nested instance contents select their own instance object. Save/reopen keeps the reference.
    • uv.select_overlap now works on Web: the WASM call trapped with 'unreachable' while constructing blender::Array<ChangedInfo> from its bitfield initializer. The local ChangedInfo array now uses std::vector, behavior-equivalent; the operator finishes and the 60-case matrix has no open operator failure.
    • object.shade_auto_smooth now works on Web: the embedded Essentials bundle is not indexed by the asset system, so the operator cancelled with “No asset found … Smooth by Angle”. With WITH_WEB it fetches the bundle, suppresses the false asset report, direct-appends the NodeTree with WM_file_append_datablock, and validates it as the Smooth by Angle group.
    • The full mathutils Python surface is available again on Web: the MATH_WEB_CORE_ONLY compile definition was removed from bf_python_mathutils, so mathutils.geometry/interpolate/noise/bvhtree/kdtree are registered. uv.lightmap_pack (mathutils.geometry.box_pack_2d) now finishes instead of raising AttributeError.
    • Native canvas replacement no longer breaks GPU resources. The Web/native toggle destroys the old WebGL context, but a retained vert/index buffer from that context made the next modal frame throw “Blender WebGL2 drawing resource belongs to another canvas”. WebGLObjects::require_owner now drops the dead GL names and lets bind() re-create and re-upload from the retained CPU bytes, so the next canvas draws normally.
    • Added a Web “调整上一步操作” panel for operators invoked through the native channel. It records the operator, schema and created objects; applying edits deletes the created objects and re-runs the operator with converted RNA values from the original context. Non-creating operators fall back to undo + re-invoke guarded by the history cursor. The panel’s “完整属性…” action opens the existing F3 operator form preselected with the recorded operator and values, so pointer, collection, nested-group, enum-set and matrix properties are editable in the same adjustment flow.
  • operator sweep: web/tests/e2e/wbv2-operator-sweep-production.spec.ts invokes every registered operator in the native mesh/object/uv/curve families (167/250/55/51 identifiers extracted from the desktop registry bpy.ops.*) through desktop_operator with a fresh Main per case, classifies EXECUTED / CONTEXT_POLL / CONTEXT_CANCEL / CONTEXT_MESSAGE / ERROR / TIMEOUT / CRASH and writes verification/operator-sweep-<family>.json bound to the WASM hash. The mesh family sweep is running; results and any real failures will be recorded per family.
  • verification PASS (Chromium, focused slices):
    • web/tests/e2e/wbv2-add-menu-native-production.spec.ts: Add menu → native mesh.primitive_monkey_add → Main/SceneIR → Outliner shows Suzanne/Monkey.
    • web/tests/e2e/wbv2-three-domain-focused-production.spec.ts creation: base 1 → create 2 → undo 3 → redo 4; saved 421,265 bytes; reopen revision 1 with VerifyCube present.
    • hierarchy: create/move/active/delete revisions 3-6, undo 7, redo 8; saved 417,882 bytes; reopen revision 1 with VerifyParent.
    • modeling: meshTriangulate 2, undo 3, redo 4; SUBSURF add 5, remove 6; saved 417,327 bytes; reopen revision 1.
    • web/tests/e2e/wbv2-mesh-native-operator-ui-production.spec.ts: Web mode switch → Edit mode → meshSelection bridge → native mesh.poke; status 已执行:mesh.poke.
    • web/tests/e2e/wbv2-modifier-matrix-production.spec.ts: all 20 expanded modifier types add/evaluate/remove/save/reopen, every case FINISHED (including SKIN after the Bullet linkage fix).
    • web/tests/e2e/wbv2-convex-hull-native-production.spec.ts: Edit mode → mesh.select_all → native mesh.convex_hull state DONE.
    • web/tests/e2e/wbv2-native-operator-matrix-production.spec.ts: 60/60 mesh/UV/object operators FINISHED on WASM 8ec2268f, including shade_auto_smooth, uv.lightmap_pack and uv.select_overlap after the asset, mathutils and overlap-container fixes. The receipt records the WASM SHA-256. UV-editor-only/cursor/active-face operators and no-op selection clears are documented as inapplicable to the detached Layout/VIEW_3D fixture, not counted. Earlier test-only failures were corrected against the native RNA registry (mesh.vertices_smooth, object.hide_view_set without clear, mesh.unsubdivide without threshold, etc.).
    • web/tests/e2e/wbv2-creation-object-types-production.spec.ts: 29 creation/data cases on WASM 8ec2268f — all six SURFACE_OT_ primitives (surface, sphere, circle, curve, cylinder, torus), five curve primitives (Bezier, NURBS curve, NURBS path, Bezier circle, NURBS circle) and the mesh/text/armature/empty/camera/light/metaball/volume/lattice/GP/point cloud/hair curves/speaker/light probe/effector types each produced a SceneIR node; operand-less object.data_instance_add remains cancelled with no mutation. Receipt records the WASM SHA-256.
    • web/tests/e2e/wbv2-data-instance-add-production.spec.ts: Outliner selection → Add → 数据实例 creates Cube.001 through the native ID lookup.
    • web/tests/e2e/wbv2-hair-curves-add-production.spec.ts: hair curves Add appends both embedded Essentials node groups; surface gains Capture Rest Geometry, Curves object gains Hair Dynamics and is parented to BasicCube.
    • web/tests/e2e/wbv2-modifier-parameters-apply-production.spec.ts: SUBSURF levels 2 evaluates 98 vertices through evaluateDepsgraph, undo restores level 1 / 26 vertices, redo restores level 2 / 98, native object.modifier_apply bakes 98 and removes the modifier, save/reopen keeps 98.
    • web/tests/e2e/wbv2-modifier-apply-convert-production.spec.ts: BEVEL width 0.1/segments 2 evaluates 56 vertices and object.modifier_apply bakes 56; SUBSURF level 2 followed by object.convert(target=MESH) bakes 98 and clears the stack.
    • web/tests/e2e/wbv2-curve-edit-operators-production.spec.ts: Bezier create → Edit → 32 curve operators all FINISHED on WASM 1e6e7d3f (added select more/less/next/previous/random/similar/linked, de_select_first/last, smooth radius/tilt/weight, spline_weight_set, shade smooth/flat, dissolve_verts, split, separate, hide, normals_make_consistent) and the receipt records the WASM SHA-256. curve.select_nth/curve.select_row are desktop-inapplicable to a default Bezier circle (same native errors: no active point / poll context incorrect) and are documented instead of counted. Fixed the menu enum value AUTO → AUTOMATIC.
    • web/tests/e2e/wbv2-convert-to-mesh-production.spec.ts: Bezier (13 verts), NURBS surface (256 verts / 225 faces) and default Text (177 verts / 171 faces) all convert to MESH through object.convert.
    • web/tests/e2e/wbv2-armature-edit-production.spec.ts: Armature create → Edit → primitive add (1→2 bones), subdivide (1→2), duplicate (1→2), calculate roll (count unchanged) all FINISHED; save while in Edit mode and reopen preserves the new bone count.
    • web/tests/e2e/wbv2-object-tools-production.spec.ts: two shape keys, one vertex group, modifier copy (two SUBSURF entries) and SIMPLE_DEFORM apply-as-shape-key (2 keys, modifier kept).
    • web/tests/e2e/wbv2-hair-curves-add-production.spec.ts now also saves and reopens: the Curves object keeps Hair Dynamics, its parent and the surface’s Capture Rest Geometry.
    • web/tests/e2e/wbv2-edit-mode-persistence-production.spec.ts: mesh.poke in Edit mode grows the cube from 8 to 14 vertices, and save/reopen preserves 14.
    • web/tests/e2e/wbv2-scene-flow-production.spec.ts: scene.new publishes 0 nodes for the empty scene, switching back publishes 3, switching to the empty scene publishes 0, and scene.delete returns to Scene with 3 nodes.
    • web/tests/e2e/wbv2-collection-instance-production.spec.ts: the instance EMPTY publishes instanceCollectionId: collection:Collection; the Web ViewportRenderer creates 3 member display copies (mesh + light + camera frustum) for the 3-object collection and 1 base-scene helper; save/reopen keeps the reference.
    • web/tests/e2e/wbv2-nested-collection-instance-production.spec.ts: a collection instance inside another collection instance expands recursively; the nested BasicCube clone reports object:NestedInner as its picking owner (not the outer object:Collection), and the nested instance contents render with composed matrices.
    • web/tests/e2e/wbv2-adjust-last-operation-production.spec.ts: Add Circle → Web adjust panel → vertices 8 → apply replaces the original with one 8-vertex Circle; a second adjustment sets location X=3; “完整属性…” opens the preselected F3 form, sets vertices 12, and the final scene has one 12-vertex Circle at [3, 0, 0].
    • web/tests/e2e/modifier-stack-ui-production.spec.ts (existing scenario suite, now unlocked): 8 cases — add/remove/move Bevel and parameters for Bevel/Array/Mirror/Solidify/Surf — pass with sequential single-writer clients, invalid-command rejection, undo/redo, save/reopen and replay; native Blender readback confirms both the modified stack and untouched objects.
    • Unlocked existing production scenarios (17 tests): rig-pose-authoring-native bones/pose with native golden readback and independent replay; mesh-main-reader-production; light-subtypes-reader-production; nonmesh-main-reader-production; native-main-lifecycle; nla-evaluation-golden; library-append-main (append/undo/redo/save/reopen against the desktop canonical report); rna-datablock-reflection-native (native datablock reflection survives reopen; zero-user orphan IDs are excluded because desktop save prunes them).
    • Combined verification: 22 focused three-domain tests + 17 unlocked existing scenarios, all passing with bounded parallelism on the current WASM.
    • Re-run on WASM 8ec2268f: 22 focused production scenarios pass — three-domain creation/hierarchy/modeling (3), creation/operator slices (10), hierarchy/persistence/collection/adjust (7), native operator matrix (60/60 operators), 32 curve operators, the native modal scenario and the ?ui=web object-editor scenario. Receipts were regenerated against this hash.
    • web/tests/e2e/object-hierarchy-production.spec.ts now passes against the production build served by tools/web/deployment-http-server.mjs with ?ui=web (web/playwright.deployment-local.config.ts): parent/unparent UI, collection lifecycle, negatives, undo/redo, save/reopen and native golden readback. web/dist was rebuilt from the current sources/WASM before the run.
    • web/tests/e2e/modeling-native-modal-production.spec.ts now passes on the current WASM: translate cancel restores Main and history, translate/rotate/scale modals, Edit-mode projected-vertex click plus modal transform, undo/redo, save/reopen and Properties readback — across the Web-tools/native-canvas toggles before and after save. The scenario takes ~56s with SwiftShader, so the spec now sets its own 300s budget instead of the 30s default. Receipt: verification/native-modal-production.json plus its native-modal-artifacts/.
    • web/tests/e2e/object-editor-ui-production.spec.ts now passes on ?ui=web against web/dist: clear location/rotation/scale, create CAMERA/LIGHT/EMPTY/ARMATURE, selection, collection viewport/render toggles, undo/redo, negative no-mutation, save/reopen and an independent determinism run; the native desktop readback agrees on every checkpoint. The previous selected mismatch was not a save-path difference: with the native build, Collection.hide_viewport = True deselects the object and showing the collection again keeps it deselected, so the Web-saved selected=false matches desktop. The test checkpoint now records the Outliner activation state that Select Active is undone to, and the reopen step no longer re-clicks the Outliner row (which had masked the selection stored in the file).
    • receipts: tests/golden/WBV2-P1425-001/verification/{add-menu-native-production,create-production,hierarchy-production,modeling-production,mesh-native-operator-ui-production,modifier-matrix-production,convex-hull-native-production,native-operator-matrix-production,creation-object-types-production,data-instance-add-production,hair-curves-add-production,modifier-parameters-apply-production,modifier-apply-convert-production,curve-edit-operators-production,convert-to-mesh-production,armature-edit-production,object-tools-production,edit-mode-persistence-production,scene-flow-production,collection-instance-production,nested-collection-instance-production,adjust-last-operation-production,object-editor-positive,object-editor-negative,object-editor-undo_redo,object-editor-save_reopen,object-editor-determinism,native-modal-production}.json.
  • retained verification blockers (not claimed as passed):
    • Scenario-style specs that create several WebEngineClients must terminate each client before opening the next; the unlocked suites now do this on ?engine=manual and pass. Specs that still drive the app page remain subject to the single-writer contract.
    • resolved in this round: object-editor-ui-production.spec.ts passes on ?ui=web with the deployment server and native readback. Desktop Blender deselects objects when their Collection is hidden and does not restore the selection when it is shown, so the Web-saved selected=false is correct; the stale expectation and two harness assumptions (undo baseline, post-open re-selection) were corrected.
    • resolved in this round: modeling-native-modal-production.spec.ts passes after WebGLObjects re-homes retained CPU bytes onto the replacement canvas; no GPU canvas detach remains.
    • resolved in this round: Web-only Edit-mode switch and mesh.poke pass after passing selectedObjects/activeObject/baseRevision, detaching the GPU surface on the Web-tools toggle, and importing bpy inside the selection helpers; verified by wbv2-mesh-native-operator-ui-production.spec.ts.
  • remaining: broader per-operator behavioral evidence for the 486 routes; Ocean/OpenVDB modifiers cannot be enabled because the Web build has WITH_FFTW3=OFF and WITH_OPENVDB=OFF, and no WASM FFTW/OpenVDB libraries or sources are available locally. Full desktop parity is not claimed.
  • 2026-11-10 operator-family sweep and Web fixes (WASM 33db5f5e):
    • web/tests/e2e/wbv2-operator-sweep-production.spec.ts invokes every live registered operator in the mesh/object/uv/curve families (167/250/55/51) through desktop_operator with a fresh Main per case; each family receipt in verification/operator-sweep-<family>.json records the per-operator outcome plus the WASM SHA-256. Final counts: mesh 136 EXECUTED, object 104, uv 32, curve 43 (315/523). Remaining outcomes are MODAL (PASS_THROUGH), CONTEXT_POLL/CANCEL/MESSAGE matching a native poll/report for the detached Layout/VIEW_3D fixture, or ADDON_MISSING for uv.export_layout (the bundled io_mesh_uv_layout addon is not loaded). Zero CRASH, zero TIMEOUT, zero core OPERATOR_MISSING; the classifier separates native Blender RuntimeError reports from WASM traps.
    • Freestyle mesh-data operators now exist on Web. WITH_FREESTYLE=OFF compiled out mesh.mark_freestyle_edge/face although the Web menu exposed them, so clicking always failed with get_rna_type(...) not found. The registration and the two exec functions are now guarded by WITH_FREESTYLE || WITH_WEB in mesh_ops.cc, editmesh_tools.cc and mesh_intern.hh; they only write the freestyle_edge/freestyle_face boolean attributes and need no Freestyle renderer. Both FINISHED in the 62-case matrix.
    • object.quick_explode now works. The EXPLODE modifier's dynamic vertex_group string has no fixed RNA buffer, so the accessor generator emitted maxLength=-1 and the editor-workflow validator rejected the whole snapshot (EDITOR_LAYOUT_INVALID: Invalid modifier string resource metadata). extract_modifier_accessors.py now clamps a missing declared maxlength to 63; Quick Explode FINISHED with ParticleSystem + Explode modifiers on the object.
    • Geometry Nodes modifier inputs are editable from Python again. node_socket.cc wrapped all 19 make_geometry_nodes_input_srna registrations in #ifndef WITH_WEB, so every NodesModifier input item exposed only rna_type/name (object.quick_fur failed at inputs.Input_12.value). Removing those guards restores value/type/attribute_name/layer_name on boolean, float, object, material, geometry, menu and collection inputs; verified with a temporary Chromium probe (Input_12/15/20/22 expose value), probe removed.
    • Menu wiring: mesh.spin/mesh.screw now pass axis=[0,0,1] (EXEC without a view axis reported Invalid/unset axis), mesh.rip routes through queueLocalDesktopOperator so the native invoke runs, and the curve menu's curve.spin was removed because its poll (ED_operator_editsurf) only accepts an edited surface object.
    • The modifier-stack UI test was retargeted from the legacy .modifier-stack-authoring panel (not rendered in the current Web tools layout) to the live [data-modifier-row] properties panel and its desktop labels (Amount/Segments, Solidify option id). This was a stale test selector, not a product regression. Focused regression on 33db5f5e: 38/38 tests passed with 4 workers (three-domain chains, 62-case operator matrix, modifier-stack UI/native scenarios, creation/adjust/data-instance/hair-curves/modifier-apply/convert/armature/object-tools/edit-persistence/scene/collection, native modal, rig, readers, NLA, library append, RNA reflection).
    • Open gaps (not claimed): object.quick_fur still fails because the linked procedural-hair node group evaluates to non-curves (Evaluated geometry from modifier does not contain curves) and the Web RNA Main-access guard then invalidates the Python callback (Main changed during Python property conversion or callback); QuadriFlow remesh (WITH_QUADRIFLOW=OFF), OpenVDB voxel remesh (WITH_OPENVDB=OFF), Mantaflow fluid quick effects (WITH_MOD_FLUID=OFF) and Ocean bake (WITH_FFTW3=OFF) remain build-flag gaps; object.lightprobe_cache_bake hits the browser draw-manager single-canvas guard; uv.export_layout needs the unloaded bundled addon. The changed spin/screw/rip menu wiring was verified through the same native operator channel, not by clicking the production menu.
    • Receipts on 33db5f5e: verification/operator-sweep-{mesh,object,uv,curve}.json, native-operator-matrix-production.json (62/62), creation-object-types-production.json (29/29), curve-edit-operators-production.json (32/32), object-editor-*.json (5), plus the production hierarchy timestamped receipt.
  • 2026-11-10 late round (WASM dc7a76eb): further modeling gaps closed:
    • Quick Fur and geometry-node assets now evaluate. WEB_headless_node_system_init() registered only a small geometry-node subset and no zone types, so the bundled procedural-hair groups loaded Capture Attribute / Separate Components / Input Radius as NodeUndefined and the node-tree updater called through a null zone vtable (RuntimeError: null function). The Web bf_nodes source set now includes intern/node_register.cc and the engine calls the desktop register_nodes() once. The hair groups load with zero undefined nodes; object.quick_fur creates 1212 points across 101 curves with Generate applied and Set Profile/Interpolate/Noise/Frizz/Hair Dynamics kept.
    • Temporary G_MAIN swaps during library append/link no longer poison an in-flight Python callback. RNAMainAccessScope::restore_after_temporary_main_swap / restore_context_after_temporary_main_swap revive suspended scopes when the same Main is swapped back (genuine replacement/clear still invalidates); BKE_blender_globals_main_swap and the Web context refresh call the restore after rebinding. Quick Fur's nested modifier_apply now completes instead of failing with Main changed during Python property conversion or callback.
    • QuadriFlow remesh is enabled (WITH_QUADRIFLOW=ON): object.quadriflow_remesh finishes with “QuadriFlow: Remeshing completed” and remeshes the 8-vertex test cube to 3954 vertices.
    • Mantaflow fluid is enabled (WITH_MOD_FLUID=ON). MOD_fluid.cc forced the Fluid modifier's RNA srna to nullptr under WITH_WEB, so rna_Modifier_refine dereferenced null when a Fluid modifier was converted to Python. The real &RNA_FluidModifier is restored (and the matching Cloth nullptr is fixed). object.quick_liquid creates a Liquid Domain with Fluid modifiers plus a Liquid Particle System; object.quick_smoke creates a Smoke Domain with Fluid modifiers; a Cloth modifier exposes its settings without crashing.
    • uv.export_layout is available: the Python bootstrap registers the bundled io_mesh_uv_layout addon when the session starts (retry-safe). The operator poll passes for a UV-mapped active mesh and an SVG export writes 1288 bytes to /browser/uv_layout.svg.
    • Final sweep on dc7a76eb: mesh 136/167, object 108/250, uv 32/55, curve 43/51 EXECUTED (319/523); zero CRASH/TIMEOUT/core OPERATOR_MISSING. object.quick_fur, quick_explode, quick_liquid, quick_smoke and quadriflow_remesh are EXECUTED; uv.export_layout is registered (native context message for the empty-filepath sweep invocation instead of ADDON_MISSING).
  • remaining (not claimed): OpenVDB voxel remesh (WITH_OPENVDB=OFF; .deps/native/openvdb contains only include/ and native lib/*.so, no OpenVDB source or wasm OpenVDB/Boost/TBB/blosc artifacts); Ocean bake (WITH_FFTW3=OFF; .deps/native/fftw3 is native only, no WASM FFTW sources/libs); light-probe cache bake runs the EEVEE lightbake job, which calls DRW_gpu_context_create; the headless manual host has no active GPU context, so it reports 'Browser draw manager must be created once per WM canvas'. A canvas-attached UI session is not yet verified, and this is render-engine behavior outside the three modeling domains. The non-executed sweep operators are modal/context classes matching desktop semantics. Full desktop parity remains NOT_CLAIMED pending user acceptance and WASM ports of OpenVDB/FFTW3.
  • next action: port OpenVDB and FFTW3 to WASM to close voxel remesh and ocean bake; keep the documented gaps explicit; full desktop parity remains NOT_CLAIMED pending user acceptance.