Skip to content

Commit c801f77

Browse files
fix: correctness pass on Blender skills, snippets, and docs (#3)
Code bugs the AI serves to users: - operators: import Vector from mathutils (not bpy.types) in the offset example and column-normalize the basis so object scale no longer distorts the local-axis direction. - cross-version-property-delete: property_unset resets RNA props to default, it does not remove a custom ID property. Use del on all versions and drop the bogus version branch; note the property_unset distinction. - version-branch-skeleton: ID-property deletion is not version-dependent; clear_property now uses del unconditionally. EEVEE engine id stays as the genuine divergence example. - custom-properties: stop crediting property_unset for unbinding a type PointerProperty or removing an ID property; both are del on all versions. - shader-node-group: modernize ShaderNodeMixRGB to ShaderNodeMix with data_type='RGBA', wiring RGBA sockets by index to avoid the node's duplicate socket names. Doc drift: - README: drop the static version-0.2.0 shields badge that drifts every release; the dynamic github/v/release badge already covers it. - ROADMAP: v0.2.0 row -> Shipped, drop the per-row Current marker so the action-owned Current: prose line is the single source of truth. - SECURITY: Supported Versions -> 0.2.x; scope text -> two templates. - CONTRIBUTING: snippet length 5 to 50 (was 5 to 30); standards-version wording points at meta-repo STANDARDS_VERSION, not VERSION. - CLAUDE: relabel the 7 materials/drivers/migration snippets v0.2.1 -> v0.2.0 to match ROADMAP/CHANGELOG/README. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent d04f6ba commit c801f77

10 files changed

Lines changed: 48 additions & 37 deletions

File tree

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Small standalone `.py` files at `snippets/<name>.py`, each 5 to 50 lines.
7474

7575
v0.1.0: canonical object creation and deletion, depsgraph evaluated mesh, bmesh load-edit-free, temp_override context, foreach_set vertex bulk write, register_classes_factory, PointerProperty binding, cross-version property delete, and the `action_ensure_channelbag_for_slot` slotted-actions bridge.
7676

77-
v0.2.1: Principled BSDF material, driver-with-custom-function via `driver_namespace`, application handler registration, shader node group with cross-version `interface` API, `foreach_get` bulk vertex read, version-branch skeleton, and USD export with `evaluation_mode='RENDER'`.
77+
v0.2.0: Principled BSDF material, driver-with-custom-function via `driver_namespace`, application handler registration, shader node group with cross-version `interface` API, `foreach_get` bulk vertex read, version-branch skeleton, and USD export with `evaluation_mode='RENDER'`.
7878

7979
## Development Workflow
8080

‎CONTRIBUTING.md‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ templates/
3939

4040
- **`skills/`** - one directory per skill, each containing `SKILL.md` with YAML frontmatter (`name`, `description`, `standards-version`).
4141
- **`rules/`** - Cursor-style rules as `.mdc` files with YAML frontmatter (`description`, `alwaysApply`, `globs`, `standards-version`).
42-
- **`snippets/`** - small standalone `.py` files (5 to 30 lines) demonstrating a single canonical pattern.
42+
- **`snippets/`** - small standalone `.py` files (5 to 50 lines) demonstrating a single canonical pattern.
4343
- **`templates/`** - copy-paste starting points; one directory per template.
4444

4545
## Adding a Skill
@@ -51,7 +51,7 @@ templates/
5151
---
5252
name: procedural-materials
5353
description: One-line description, under 200 chars.
54-
standards-version: <current meta-repo VERSION>
54+
standards-version: <current meta-repo STANDARDS_VERSION>
5555
---
5656
```
5757

@@ -69,7 +69,7 @@ templates/
6969
alwaysApply: true
7070
globs:
7171
- "**/*.py"
72-
standards-version: <current meta-repo VERSION>
72+
standards-version: <current meta-repo STANDARDS_VERSION>
7373
---
7474
```
7575

@@ -78,7 +78,7 @@ templates/
7878
## Adding a Snippet
7979

8080
1. Add a `.py` file under `snippets/`, e.g. `snippets/depsgraph-evaluated-mesh.py`.
81-
2. Keep it 5 to 30 lines, fully working code, with a header comment naming the snippet and citing the relevant Blender doc URL or research section.
81+
2. Keep it 5 to 50 lines, fully working code, with a header comment naming the snippet and citing the relevant Blender doc URL or research section.
8282
3. Snippets are validated for Python syntax in CI.
8383

8484
## Adding a Template
@@ -101,10 +101,10 @@ else:
101101

102102
## Standards-version Markers
103103

104-
Files that participate in ecosystem drift checking must carry a `standards-version` marker matching the current meta-repo `VERSION`:
104+
Files that participate in ecosystem drift checking must carry a `standards-version` marker matching the current meta-repo `STANDARDS_VERSION` (which is decoupled from this repo's `VERSION`):
105105

106-
- `AGENTS.md`, `CLAUDE.md`, `ROADMAP.md`: HTML comment first line, e.g. `<!-- standards-version: 1.9.1 -->`.
107-
- `skills/*/SKILL.md`, `rules/*.mdc`: YAML frontmatter field `standards-version: 1.9.1`.
106+
- `AGENTS.md`, `CLAUDE.md`, `ROADMAP.md`: HTML comment first line, e.g. `<!-- standards-version: <STANDARDS_VERSION> -->`.
107+
- `skills/*/SKILL.md`, `rules/*.mdc`: YAML frontmatter field `standards-version: <STANDARDS_VERSION>`.
108108

109109
The drift-check workflow enforces these on every push and PR.
110110

‎README.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
</p>
88

99
<p align="center">
10-
<a href="https://github.com/TMHSDigital/Blender-Developer-Tools/releases"><img src="https://img.shields.io/badge/version-0.2.0-e87d0d?style=flat-square" alt="Version" /></a>
1110
<a href="https://github.com/TMHSDigital/Blender-Developer-Tools/releases"><img src="https://img.shields.io/github/v/release/TMHSDigital/Blender-Developer-Tools?style=flat-square&color=e87d0d&label=release" alt="Release" /></a>
1211
<a href="LICENSE"><img src="https://img.shields.io/badge/license-CC--BY--NC--ND--4.0-384d54?style=flat-square" alt="License" /></a>
1312
</p>

‎ROADMAP.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
| Version | Theme | Skills | Rules | Templates | Snippets | Status |
88
| --- | --- | --- | --- | --- | --- | --- |
99
| v0.1.0 | Foundation | 8 | 4 | 1 | 10 | Shipped |
10-
| v0.2.0 | Materials, drivers, migration | 12 | 6 | 2 | 17 | **Current** |
10+
| v0.2.0 | Materials, drivers, migration | 12 | 6 | 2 | 17 | Shipped |
1111
| v0.3.0 | 5.2 LTS sweep, modal operators, USD | TBD | TBD | TBD | TBD | Planned |
1212
| v1.0.0 | Stable | TBD | TBD | TBD | TBD | Planned |
1313

‎SECURITY.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Please include:
1515

1616
## Scope
1717

18-
This repository ships Markdown skill files, MDC rule files, Python snippets, and one Blender extension add-on template. The primary security concerns are:
18+
This repository ships Markdown skill files, MDC rule files, Python snippets, and two starter templates (a Blender extension add-on and a headless batch script). The primary security concerns are:
1919

2020
- **Snippets or templates demonstrating insecure patterns** (executing arbitrary code from `.blend` files, loading remote scripts without validation, leaking filesystem paths into logs).
2121
- **The extension-addon template declaring over-broad permissions** in `blender_manifest.toml` (e.g. `network`, `files`, `clipboard`, `camera`) without a documented justification.
@@ -28,8 +28,8 @@ Issues with the Blender Python API itself (`bpy`, `bmesh`, `bpy_extras`) belong
2828

2929
| Version | Supported |
3030
|---------|-----------|
31-
| 0.1.x | Yes |
32-
| < 0.1.0 | No |
31+
| 0.2.x | Yes |
32+
| < 0.2.0 | No |
3333

3434
## Response Timeline
3535

‎skills/custom-properties/SKILL.md‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,11 @@ The order matters. The bound `PointerProperty` references the `PropertyGroup` cl
133133

134134
```python
135135
def unregister():
136-
del bpy.types.Scene.my_addon # or property_unset() on 5.0+
136+
del bpy.types.Scene.my_addon
137137
bpy.utils.unregister_class(MY_ADDON_PG_settings)
138138
```
139139

140-
See the snippet `cross-version-property-delete.py` for the 5.0+ `property_unset` shape and the 4.5 LTS `del` shape.
140+
Unbinding a type-level property is `del bpy.types.Scene.my_addon` on all Blender versions. See the snippet `cross-version-property-delete.py` for removing a custom ID property (also `del`, version-stable).
141141

142142
## CollectionProperty (lists)
143143

@@ -231,7 +231,12 @@ This is a frequent AI mistake when generating quick scripts. Always wrap state i
231231

232232
## Compatibility paths (4.5 LTS vs 5.0+)
233233

234-
In Blender 5.0+, `bpy.types.Scene.my_addon.property_unset(...)` is the preferred unbind for individual props. In 4.5 LTS, `del bpy.types.Scene.my_addon` is still required for `PointerProperty` bindings. The snippet `cross-version-property-delete.py` shows both.
234+
Two distinct operations, both version-stable via `del`:
235+
236+
- **Unbind a type-level property** (e.g. a `PointerProperty` bound to `bpy.types.Scene`): `del bpy.types.Scene.my_addon`. Same on 4.5 LTS and 5.0+.
237+
- **Remove a custom ID property** (the dict-style `obj["key"]`): `del obj["key"]`. Same on 4.5 LTS and 5.0+. The snippet `cross-version-property-delete.py` shows this.
238+
239+
Do not reach for `property_unset()` here. It resets a registered RNA property to its default, which is neither unbinding a type property nor removing an ID property.
235240

236241
## Related
237242

‎skills/operators/SKILL.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ Pair `report({'ERROR'}, ...)` with `return {'CANCELLED'}` so the operator does n
129129

130130
```python
131131
import bpy
132+
from mathutils import Vector
132133

133134

134135
class OBJECT_OT_offset_active(bpy.types.Operator):
@@ -157,7 +158,10 @@ class OBJECT_OT_offset_active(bpy.types.Operator):
157158
offset = (self.offset_x, self.offset_y, self.offset_z)
158159

159160
if self.use_local:
160-
obj.location += obj.matrix_world.to_3x3() @ bpy.types.Vector(offset)
161+
# Column-normalize the basis so object scale does not distort the
162+
# local-axis direction; to_3x3() alone carries scale into the result.
163+
basis = obj.matrix_world.to_3x3().normalized()
164+
obj.location += basis @ Vector(offset)
161165
else:
162166
obj.location.x += self.offset_x
163167
obj.location.y += self.offset_y

‎snippets/cross-version-property-delete.py‎

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
# Cross-version property deletion: property_unset (5.0+) vs del (4.5 LTS).
2-
# Custom ID properties (the dict-style obj["my_key"] = value) are removed
3-
# differently across Blender versions. property_unset is the cleaner
4-
# 5.x-and-up form; del obj["key"] still works on 4.5 LTS.
1+
# Removing a custom ID property (the dict-style obj["my_key"] = value).
2+
# ID-property removal is version-stable: `del id_block[key]` works on all
3+
# Blender versions (4.5 LTS and 5.x alike). There is no version branch here.
4+
#
5+
# Do NOT use property_unset() for this. property_unset() RESETS a registered
6+
# RNA property to its default value; it does not remove a custom ID property.
7+
# That is a different operation.
58
#
69
# Reference:
710
# https://docs.blender.org/api/current/bpy.types.bpy_struct.html
@@ -13,11 +16,7 @@ def remove_custom_property(id_block, key):
1316
if key not in id_block.keys():
1417
return False
1518

16-
if bpy.app.version >= (5, 0, 0):
17-
id_block.property_unset(key)
18-
else:
19-
del id_block[key]
20-
19+
del id_block[key]
2120
return True
2221

2322

‎snippets/shader-node-group.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@ def make_tint_group(name="Tint"):
2323

2424
group_in = nodes.new('NodeGroupInput')
2525
group_out = nodes.new('NodeGroupOutput')
26-
mix = nodes.new('ShaderNodeMixRGB')
26+
mix = nodes.new('ShaderNodeMix')
27+
mix.data_type = 'RGBA'
2728
mix.blend_type = 'MULTIPLY'
2829

29-
links.new(group_in.outputs['Color'], mix.inputs['Color1'])
30-
links.new(group_in.outputs['Strength'], mix.inputs['Fac'])
31-
links.new(mix.outputs['Color'], group_out.inputs['Result'])
30+
# ShaderNodeMix shares socket names (Factor/A/B/Result) across every
31+
# data_type, so name lookup is ambiguous. Wire the RGBA sockets by index:
32+
# inputs[0]=Factor (Float), inputs[6]=A (Color), inputs[7]=B (Color)
33+
# outputs[2]=Result (Color)
34+
links.new(group_in.outputs['Strength'], mix.inputs[0])
35+
links.new(group_in.outputs['Color'], mix.inputs[6])
36+
links.new(mix.outputs[2], group_out.inputs['Result'])
3237
return group

‎snippets/version-branch-skeleton.py‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66

77

88
def clear_property(obj, key):
9-
"""Delete a custom property cross-version.
9+
"""Delete a custom ID property.
1010
11-
Blender 5.0 introduced property_unset() as the supported path; on 4.5 LTS
12-
fall back to del obj[key]. Either form raises if key is absent, so check first.
11+
NOT version-dependent: del obj[key] works on every Blender version
12+
(4.5 LTS and 5.x). del raises if key is absent, so check membership first.
13+
See get_eevee_engine_id() below for the file's genuine version-divergence
14+
example.
1315
"""
1416
if key not in obj:
1517
return False
1618

17-
if bpy.app.version >= (5, 0, 0):
18-
obj.property_unset(f'["{key}"]')
19-
else:
20-
del obj[key]
19+
del obj[key]
2120
return True
2221

2322

0 commit comments

Comments
 (0)