You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit bce71a9
Browse filesBrowse the repository at this point in the historyBrowse files
docs: fix version and API facts in CLAUDE.md, custom-properties and operators skills
- CLAUDE.md rules table: bpy.context.copy() passed to operators was
"deprecated 4.x, removed 5.x"; it was deprecated in 3.2 and removed in 4.0
(what README and claude/blender-rules.md already say).
- custom-properties: plain Python attributes on a datablock do not "silently"
vanish on reload; `bpy.context.scene.my_intensity = 1.5` raises
AttributeError immediately (verified on .scratch 4.5.11 and 5.2.1). Text now
says so and notes ID properties (scene["key"]) as the untyped alternative.
- operators: the anatomy example looped `for v in mesh.vertices: v.co += ...`
right under "Operate on bpy.data", the pattern rule
use-foreach-set-for-bulk-data flags, and was named "extrude" without
extruding. Now MESH_OT_offset_along_normals with one foreach_get/foreach_set
pair. Ran the extracted example on 5.2.1: {'FINISHED'}, cube corner
(-1,-1,-1) -> (-1.289,-1.289,-1.289) at distance 0.5.
- docs/technical-audit.md labelled historical (v0.5.0 snapshot), with the
standing findings log called out as the maintained part.
Closes#333
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/technical-audit.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,8 @@
2
2
3
3
_Audit date: 2026-06-20 · Repo @ v0.5.0 · Auditor: Principal Eng review_
4
4
5
+
> **Historical.** The audit sections describe the repository as of v0.5.0 and are kept as a record; counts and file references in them are not current. Only "Findings the release notes did not list" below is a standing, maintained log.
6
+
5
7
## Findings the release notes did not list
6
8
7
9
_Standing log. Notes-derived 5.1→5.2 (and later) deltas are incomplete by default._
Copy file name to clipboardExpand all lines: skills/custom-properties/SKILL.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -196,11 +196,11 @@ api_key = prefs.api_key
196
196
## Why "just use Python attributes" does not work
197
197
198
198
```python
199
-
# WRONG: this attribute does not survive save and load.
199
+
# WRONG: raises AttributeError: 'Scene' object has no attribute 'my_intensity'
200
200
bpy.context.scene.my_intensity =1.5
201
201
```
202
202
203
-
Properties attached as plain Python attributes to a Blender datablock are not serialized into the `.blend` file. The .blend file format only saves registered `bpy.props`. Reload the file and the value is gone.
203
+
Blender datablocks do not accept arbitrary Python attributes: the assignment fails immediately, on 4.5 LTS and 5.x alike. Only registered `bpy.props` (or ID properties set with `scene["key"] = value`) exist on a datablock and are saved into the `.blend` file.
204
204
205
205
This is a frequent AI mistake when generating quick scripts. Always wrap state in a `PropertyGroup` bound via `PointerProperty` if it must persist.
206
206
@@ -218,9 +218,11 @@ This is a frequent AI mistake when generating quick scripts. Always wrap state i
218
218
2.**Storing complex state on Python attributes**:
219
219
220
220
```python
221
-
scene.my_data = {"items": [...]} #gone after save and reload
221
+
scene.my_data = {"items": [...]} #AttributeError: not a registered property
222
222
```
223
223
224
+
Ad-hoc ID properties (`scene["my_data"] = ...`) do save, but they are untyped and invisible to the UI; use a `PropertyGroup` for anything an add-on owns.
225
+
224
226
3.**Wrong unregister order** (unregister class before deleting binding) -> crash on save.
225
227
226
228
4.**Binding individual properties directly to Scene** instead of grouping into a `PropertyGroup`. Hard to unregister cleanly, pollutes the namespace.
bl_description ="Move every vertex of the active mesh a fixed distance along its normal"
35
35
bl_options = {'REGISTER', 'UNDO'}
36
36
37
37
distance: bpy.props.FloatProperty(
38
38
name="Distance",
39
-
description="How far to extrude",
39
+
description="How far to move each vertex",
40
40
default=0.5,
41
41
min=-100.0,
42
42
max=100.0,
@@ -54,14 +54,20 @@ class MESH_OT_extrude_normal(bpy.types.Operator):
54
54
self.report({'ERROR'}, "No active object")
55
55
return {'CANCELLED'}
56
56
57
-
# Operate on bpy.data, not bpy.ops, in tight loops.
58
-
# See the mesh-editing-and-bmesh skill for the full pattern.
57
+
# Operate on bpy.data, not bpy.ops, and in bulk: one foreach_get /
58
+
# foreach_set pair instead of a Python loop over mesh.vertices (rule
59
+
# use-foreach-set-for-bulk-data). See the mesh-editing-and-bmesh skill.
59
60
mesh = obj.data
60
-
for v in mesh.vertices:
61
-
v.co += v.normal *self.distance
61
+
n =len(mesh.vertices)
62
+
co = [0.0] * (n *3)
63
+
nor = [0.0] * (n *3)
64
+
mesh.vertices.foreach_get("co", co)
65
+
mesh.vertices.foreach_get("normal", nor)
66
+
d =self.distance
67
+
mesh.vertices.foreach_set("co", [c + k * d for c, k inzip(co, nor)])
62
68
63
69
mesh.update()
64
-
self.report({'INFO'}, f"Extruded {len(mesh.vertices)} vertices by {self.distance:.3f}")
70
+
self.report({'INFO'}, f"Offset {n} vertices by {d:.3f}")
65
71
return {'FINISHED'}
66
72
```
67
73
@@ -71,7 +77,7 @@ class MESH_OT_extrude_normal(bpy.types.Operator):
71
77
| --- | --- | --- | --- |
72
78
|`poll(cls, context)`| Optional | Every UI redraw to decide if the operator is enabled. Must be cheap. |`True` to enable, `False` or falsy to disable |
73
79
|`invoke(self, context, event)`| Optional | The user triggers the operator interactively (button, menu, keymap). Default forwards to `execute`. |`{'FINISHED'}`, `{'CANCELLED'}`, `{'PASS_THROUGH'}`, `{'RUNNING_MODAL'}`|
74
-
|`execute(self, context)`| Yes | The operator runs, either after `invoke` or directly via `bpy.ops.mesh.extrude_normal()`|`{'FINISHED'}` or `{'CANCELLED'}`|
80
+
|`execute(self, context)`| Yes | The operator runs, either after `invoke` or directly via `bpy.ops.mesh.offset_along_normals()`|`{'FINISHED'}` or `{'CANCELLED'}`|
75
81
|`modal(self, context, event)`| Optional | After `invoke` returns `{'RUNNING_MODAL'}`. Called for each event until you return `{'FINISHED'}` or `{'CANCELLED'}`. | Standard return set |
76
82
|`draw(self, context)`| Optional | The redo panel after the operator runs. Defaults to drawing all properties. | None |
0 commit comments