From fd20183cbb60e24001c3063bd6e7f3e24a904d52 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Mon, 28 Sep 2026 15:08:22 -0700 Subject: [PATCH] feat(blender): extension set live in the GUI; MCP acceptance 9/9, probe made safe-mode compliant --- persistent-memory.md | 8 +-- .../blender-probes/extensions_acceptance.py | 52 +++++++++++++------ stacks/blender/README.md | 23 +++++++- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/persistent-memory.md b/persistent-memory.md index f6ea7a2..e3ed5b6 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -201,10 +201,10 @@ _As of 2026-09-27 ~0900 PT._ add-ons (`stacks/blender/extensions.lock`) built by `scripts/blender-extensions sync` into `fv-ml1:/tank/blender-extensions/5.2/system` (LIVE), mounted read-only as the System repo; `fleet_extensions.py` enables them (GUI startup timer; `blender-run --extensions`). Headless - acceptance 8/9 (CAD Sketcher sketching is GUI-only). **⚠ The stack deploy that wires the GUI - and the hook (`deploy-stack.sh fv-ml1 blender`) was DENIED by the permission classifier on - 2026-09-28 and awaits Prime**; until it runs, `blender-run --extensions` fails cleanly (mount - error) and the MCP acceptance is not done. SurfacePsycho's eval() is patched to literal_eval. + acceptance 8/9 (CAD Sketcher sketching is GUI-only); **MCP 9/9 after Prime ran the deploy himself + (1505; the classifier had refused mine)**. SurfacePsycho's eval() is patched to literal_eval + (held under MCP). Open: agent-drawn CAD Sketcher geometry (its stateful ops want point picks), + and MeasureIt overlays not seen in MCP screenshots. GUI left DOWN. ### Zigbee2MQTT on esh-docker-vm (2026-09-27, Prime go-ahead; ha-dev request) diff --git a/scripts/blender-probes/extensions_acceptance.py b/scripts/blender-probes/extensions_acceptance.py index 7d935c8..607fae6 100644 --- a/scripts/blender-probes/extensions_acceptance.py +++ b/scripts/blender-probes/extensions_acceptance.py @@ -70,11 +70,9 @@ def dims(o): return "x".join(str(round(d * 1000, 3)) for d in world_extent(o)) + " mm (world)" -def run(name, fn): - try: - fn(name) - except Exception as ex: - report(name, False, "raised " + type(ex).__name__ + ": " + str(ex)) +def raised(name, ex): + # repr, not type(ex).__name__: safe mode refuses dunder attributes. + report(name, False, "raised " + repr(ex)[:300]) # 1. 3D-Print Toolbox: clean non-manifold on a cube with one face deleted. @@ -221,17 +219,39 @@ def t_cad_sketcher(name): "sketch objects " + str(len(sketches))) -for n, f in ( - ("print3d_toolbox clean_non_manifold", t_print3d), - ("bool_tool boolean_auto_difference", t_booltool), - ("looptools circle", t_looptools), - ("measureit addsegment", t_measureit), - ("surfacepsycho bezier patch + STEP export", t_surfacepsycho), - ("step_importer import_scene.step", t_step_import), - ("ThreeMF_io export + import", t_threemf), - ("CAD_Sketcher sketch + solve", t_cad_sketcher), -): - run(n, f) +# Each test is called by name: safe mode refuses calling a function held in a variable. +try: + t_print3d("print3d_toolbox clean_non_manifold") +except Exception as ex: + raised("print3d_toolbox clean_non_manifold", ex) +try: + t_booltool("bool_tool boolean_auto_difference") +except Exception as ex: + raised("bool_tool boolean_auto_difference", ex) +try: + t_looptools("looptools circle") +except Exception as ex: + raised("looptools circle", ex) +try: + t_measureit("measureit addsegment") +except Exception as ex: + raised("measureit addsegment", ex) +try: + t_surfacepsycho("surfacepsycho bezier patch + STEP export") +except Exception as ex: + raised("surfacepsycho bezier patch + STEP export", ex) +try: + t_step_import("step_importer import_scene.step") +except Exception as ex: + raised("step_importer import_scene.step", ex) +try: + t_threemf("ThreeMF_io export + import") +except Exception as ex: + raised("ThreeMF_io export + import", ex) +try: + t_cad_sketcher("CAD_Sketcher sketch + solve") +except Exception as ex: + raised("CAD_Sketcher sketch + solve", ex) failed = [r[0] for r in results if not r[1]] print("SUMMARY " + TAG + ": " + str(len(results) - len(failed)) + "/" + str(len(results)) + " passed") diff --git a/stacks/blender/README.md b/stacks/blender/README.md index 162aa84..6671c9c 100644 --- a/stacks/blender/README.md +++ b/stacks/blender/README.md @@ -230,4 +230,25 @@ z = 0, area 399.999982 mm² against 400. Extruded 2 mm, it gave a valid 799.9999 real NURBS surface reaches the kernel, not triangles, and millimetres hold across the hop. That is one run of a flat bilinear patch; curved and trimmed patches are untested. -**MCP (GUI) path:** pending the stack deploy that mounts the repo and hook into the GUI container. +**MCP (GUI) path, 2026-09-28 1506 (after Prime ran the deploy):** the hook log read +`enabled 8/8`, `hardened: surfacepsycho ... literal_eval`, `online access: False`. Through +`scripts/blender-mcp` → `execute_blender_code` under safe mode, the probe passed **9 of 9**, +including CAD Sketcher (a sketch on the XY origin plane, then a full solve), because the GUI has +the workspace tool that `-b` lacks. The eval patch held under MCP: the code payload was refused +(`ValueError: malformed node`) and the literal `{3, 5}` was accepted. +- **CAD Sketcher sketch, re-solved headless:** the MCP-made sketch was saved + (`cad-sketch-mcp.blend`), then `blender-run --cpu --extensions` opened it and + `view3d.slvs_solve(all=True)` returned FINISHED. Authoring is in the GUI; re-solving works in batch. +- **Not solved yet:** drawing CAD Sketcher geometry from code. `slvs_add_rectangle` with + `p1_fallback`/`p2_fallback` and `wait_for_input=False` raised + `'NoneType' object has no attribute 'co'`. Its drawing ops are stateful and expect point picks, + and its Python entity API is out of reach under safe mode. Sketches work; agent-drawn profiles + need more work. +- **Not confirmed:** MeasureIt overlays in `get_viewport_screenshot`. The segment was created and + `measureit.runopengl` returned FINISHED, but two screenshots (a top view with a 20 mm edge + dimension) showed no dimension. +- `blender-run --extensions` (the real wrapper, `--cpu`) ran the same probe at 8 of 9, the one + miss being CAD Sketcher for the headless reason above. + +Safe-mode note for probe authors: safe mode refuses calling a function held in a variable and +any dunder attribute (`type(ex).__name__`). The probe calls each test by name and uses `repr(ex)`.