Skip to content

Commit 96b6076

Browse files
TMHSDigitalclaude
andcommitted
feat(examples): addon-register-lifecycle covers scaffolding, bl_info migration, panels and props
addon-scaffolding, bl-info-migration and ui-panels were the only skills with no runnable example. examples/addon-register-lifecycle (check-only) builds a dual-format add-on (manifest plus bl_info) with a PropertyGroup, a Panel and a Scene PointerProperty, then, on 4.5.14 and 5.2.1: - extension validate accepts the dual-format package (0) and rejects the bl_info-only one (1, manifest not found); - register() creates panel, group and pointer, and the default reads back; - unregister() deleting the pointer first leaves nothing. Falsifiers: --drop-manifest 3, --legacy-gets-manifest 4, --skip-pointer 5, --classes-first 6 (the pointer survives its type). New finding, in the README and the addon-scaffolding skill: reading a pointer left dangling by unregistering its PropertyGroup first returned an empty stub on 4.5.14 and crashed Blender 5.2.1 (EXCEPTION_ACCESS_VIOLATION). The skill had said it would hard-fail on restart. The example never reads the dangling pointer. build_examples_index.py's PENDING exemption list is now empty: every skill links at least one example, and a skill without one fails the check. Counts: 69 examples (13 check-only). Closes #378 Refs #377 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
1 parent 481183e commit 96b6076

17 files changed

Lines changed: 387 additions & 23 deletions

File tree

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Blender-Developer-Tools/
3636
rules/<rule-name>.mdc # 11 rule files
3737
templates/<template-name>/ # 3 starter templates
3838
snippets/<snippet-name>.py # 29 standalone Python snippets
39-
examples/<name>/ # 68 runnable smoke-gated examples (+ gallery.json)
39+
examples/<name>/ # 69 runnable smoke-gated examples (+ gallery.json)
4040
examples/gallery_framing.py # shared Layer 1 framing measurement (render path only)
4141
showcase/<name>/ # budget-conformance props (sibling of examples/)
4242
showcase/gallery.json # this tree's gallery index; merged into docs/gallery/

‎CLAUDE.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ skills/<skill-name>/SKILL.md - AI workflow definitions, 19 total
2121
rules/<rule-name>.mdc - Anti-pattern rules, 11 total
2222
templates/<template-name>/ - Starter projects, 3 total
2323
snippets/<snippet-name>.py - Standalone code patterns, 29 total
24-
examples/<name>/ - Runnable smoke-gated examples, 68 total (+ gallery.json)
24+
examples/<name>/ - Runnable smoke-gated examples, 69 total (+ gallery.json)
2525
showcase/<name>/ - Budget-conformance props, 80 pieces, each with a gallery `category` (sibling of examples/; see showcase/README.md § Categories)
2626
scripts/build_gallery.py - Regenerates docs/gallery/ from examples/gallery.json + showcase/gallery.json
2727
scripts/site/ - Vendored landing-page build (Jinja2); tokens.css is the shared palette
@@ -112,13 +112,13 @@ AI asset pipeline track: `decimate_to_budget.py`, `convex_hull_collider.py`, `lo
112112

113113
Extensions runtime: `extension-user-data.py` (`extension_path_user` settings file and an `online_access` guard). Long-running work: `thread-queue-timer.py` (worker thread, `queue.Queue`, main-thread drain timer).
114114

115-
## Examples (68)
115+
## Examples (69)
116116

117117
Runnable scripts at `examples/<name>/`, each asserting a real API contract with
118118
deterministic checks (exit non-zero on failure) and optionally rendering a still via
119119
`--output`. All of them run headless on Blender 5.2 LTS and 4.5 LTS in `blender-smoke.yml` (5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch);
120-
**56 of the 68 ship a render in the site gallery** at `docs/gallery/`. The other
121-
twelve are **check-only**: they carry no `--output` path, no gallery entry, and no
120+
**56 of the 69 ship a render in the site gallery** at `docs/gallery/`. The other
121+
thirteen are **check-only**: they carry no `--output` path, no gallery entry, and no
122122
hero asset. The criterion is whether the contract is expressible in pixels. An
123123
example is check-only when its witness is a data or state fact that no scene
124124
redesign can make visible — a datablock name, an RNA attribute's presence, a

‎CONTRIBUTING.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ The drift-check workflow enforces these on every push and PR.
288288

289289
## Aggregate Counts
290290

291-
`README.md` declares aggregate counts (e.g. "19 skills, 11 rules, 3 templates, 29 snippets, 68 examples, and 80 showcase pieces"). The `validate-counts` job in `.github/workflows/validate.yml` enforces these substrings against the filesystem on every push and PR. Showcase pieces are counted separately from examples. When you add or remove content, update the README counts in the same commit.
291+
`README.md` declares aggregate counts (e.g. "19 skills, 11 rules, 3 templates, 29 snippets, 69 examples, and 80 showcase pieces"). The `validate-counts` job in `.github/workflows/validate.yml` enforces these substrings against the filesystem on every push and PR. Showcase pieces are counted separately from examples. When you add or remove content, update the README counts in the same commit.
292292

293293
## Pull Request Process
294294

‎README.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
</p>
2626

2727
<p align="center">
28-
<strong>19 skills</strong> &nbsp;&bull;&nbsp; <strong>11 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>29 snippets</strong> &nbsp;&bull;&nbsp; <strong>68 examples</strong> &nbsp;&bull;&nbsp; <strong>80 showcase pieces</strong>
28+
<strong>19 skills</strong> &nbsp;&bull;&nbsp; <strong>11 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>29 snippets</strong> &nbsp;&bull;&nbsp; <strong>69 examples</strong> &nbsp;&bull;&nbsp; <strong>80 showcase pieces</strong>
2929
</p>
3030

3131
<p align="center">
@@ -44,7 +44,7 @@
4444

4545
## Overview
4646

47-
This repository ships **19 skills, 11 rules, 3 templates, 29 snippets, 68 examples, and 80 showcase pieces** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
47+
This repository ships **19 skills, 11 rules, 3 templates, 29 snippets, 69 examples, and 80 showcase pieces** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
4848

4949
The content is consumed by AI coding agents, installed as a plugin or read from a checkout. An optional second plugin registers the official [Blender Lab MCP server](https://www.blender.org/lab/mcp-server/) for live sessions (see [Live Blender session](#live-blender-session-mcp)); the server is referenced from upstream, not part of this repository, and nothing else depends on it. Cursor and Claude Code both install it as a plugin (see [Quick start](#quick-start)): Cursor loads the skills and applies `rules/*.mdc` wherever their scope globs match; Claude Code loads the skills and gets the rules through a generated `blender-rules` skill, since it does not read Cursor `.mdc` files. An agent picks a skill up when its description matches the task. Any agent that can read files in a workspace can use it the same way. There is no build step for the content — edit the Markdown and Python files directly.
5050

@@ -129,7 +129,7 @@ Releases ship often; [CHANGELOG.md](CHANGELOG.md) lists what each one changed.
129129

130130
## Falsifiers
131131

132-
Every one of the 68 examples carries a **falsifier**: a flag that changes the
132+
Every one of the 69 examples carries a **falsifier**: a flag that changes the
133133
input so a real assertion fails. It never disables the assertion, skips the
134134
check, or short-circuits to an error — it feeds the script something the
135135
contract says must not pass, and the same check that guards the happy path
@@ -157,7 +157,7 @@ per-script exit-code model, are in
157157

158158
## Examples and showcase
159159

160-
**68 examples** in [`examples/`](examples/) are runnable, self-checking scripts: each asserts one API contract, carries a falsifier, and runs headless on Blender 5.2 LTS and 4.5 LTS in the `blender-smoke` workflow (5.1 on the weekly cron or by manual dispatch). Those whose contract is visible also render a still. **80 showcase pieces** in [`showcase/`](showcase/) exist to show the skills working together at production scale: each is a single headless script that models a game prop, then runs it through the same cleanup, bake, LOD, collider and export steps the skills teach, and fails if a measured budget (triangle counts, colliders, materials, real-world size) is missed. They are not API examples. Conventions: [`showcase/README.md`](showcase/README.md).
160+
**69 examples** in [`examples/`](examples/) are runnable, self-checking scripts: each asserts one API contract, carries a falsifier, and runs headless on Blender 5.2 LTS and 4.5 LTS in the `blender-smoke` workflow (5.1 on the weekly cron or by manual dispatch). Those whose contract is visible also render a still. **80 showcase pieces** in [`showcase/`](showcase/) exist to show the skills working together at production scale: each is a single headless script that models a game prop, then runs it through the same cleanup, bake, LOD, collider and export steps the skills teach, and fails if a measured budget (triangle counts, colliders, materials, real-world size) is missed. They are not API examples. Conventions: [`showcase/README.md`](showcase/README.md).
161161

162162
Browse both, with filters, full-size renders and each script's README, in the **[gallery](https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/)**.
163163

@@ -179,7 +179,7 @@ skills/<name>/SKILL.md - 19 skill files, YAML frontmatter, one canonical patte
179179
rules/<name>.mdc - 11 rule files, anti-pattern + correction
180180
templates/<name>/ - 3 template directories (extension-addon-template, headless-batch-script-template, ai-asset-pipeline-template)
181181
snippets/<name>.py - 29 standalone Python snippets, 5 to 75 lines each
182-
examples/<name>/ - 68 example directories: script, README with exit codes and falsifier
182+
examples/<name>/ - 69 example directories: script, README with exit codes and falsifier
183183
showcase/<name>/ - 80 showcase pieces: budget-gated game props, script and README
184184
claude/ - generated Claude Code copies of the rules (blender-rules skill + import file)
185185
```

‎ROADMAP.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ showcase pool further down. Each item is an open issue; nothing here is pinned t
1111

1212
- **New skills:** `porting-to-blender-5`, one index of every 4.x to 5.x break the pack covers ([#466](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/466)); teach the API contracts that shipped examples prove but no skill covers yet, such as Grease Pencil v3, the 5.x compositor, constraints and image pixels ([#456](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/456))
1313
- **Skill corrections:** the full Principled BSDF v2 socket rename table in `procedural-materials-and-shaders` ([#382](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/382))
14-
- **Examples for skills that have none:** check-only examples for `operators`, `ui-panels`, `addon-scaffolding` and `bl-info-migration` ([#378](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/378)); a skill-to-example index and one machine-readable examples manifest with version support ([#377](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/377))
1514
- **Verification:** run the shipped snippets and SKILL.md code blocks in CI instead of frozen copies ([#447](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/447)); a weekly, non-gating smoke run against Blender daily builds to catch API breaks before a release ([#457](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/457))
1615
- **More agents:** first-class support for Codex, Copilot, Windsurf and Gemini through `.agents/skills` and an AGENTS.md rules export ([#376](https://github.com/TMHSDigital/Blender-Developer-Tools/issues/376))
1716

‎docs/gallery/index.html‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@
6262
<h1>Examples and Showcase</h1>
6363
<p>Runnable Blender Python examples, each run headless on Blender 5.2 LTS and 4.5 LTS in CI, so every render reflects code that actually runs — shown beside budget-conformance showcase props.</p>
6464
<details class="check-only" id="check-only">
65-
<summary>56 rendered &middot; 12 check-only examples (data contracts with nothing to see)</summary>
65+
<summary>56 rendered &middot; 13 check-only examples (data contracts with nothing to see)</summary>
6666
<div>
6767
A check-only example asserts a fact no render can show, such as a datablock name,
6868
an RNA attribute, a post-exit sidecar or a topology count. It runs in the same
6969
smoke workflow on every PR, with its own falsifier; its source is on GitHub.
7070
<ul>
71+
<li><a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/addon-register-lifecycle/">addon-register-lifecycle</a>: Check-only witness for the first things every add-on author writes.</li>
7172
<li><a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/coincident-vert-weld/">coincident-vert-weld</a>: Synthesizes two cubes occupying the same space as one mesh, then asserts the duplicates exist before asserting they cross glTF export.</li>
7273
<li><a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/eval-mesh-datablock-name/">eval-mesh-datablock-name</a>: Pathology witness for the 5.2 change in <code>Object.evaluated_get(depsgraph).data.name</code>.</li>
7374
<li><a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/exit-pre-sidecar/">exit-pre-sidecar</a>: <code>bpy.app.handlers.exit_pre</code> writes <code>$BDT_SMOKE_SIDECAR</code> as Blender dies.</li>

‎docs/new-example-prompt.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ metadata — so the render would be identical whether the API held or broke. The
5959
"redesign the scene until failure would be visible" instruction above is the test:
6060
attempt it first, and only when it cannot succeed in principle does the example
6161
become check-only. The exception is for contracts that are invisible, not for
62-
renders that are hard. Ten of the 68 examples currently qualify, and `CLAUDE.md`
62+
renders that are hard. Ten of the 69 examples currently qualify, and `CLAUDE.md`
6363
carries the same rule. A check-only example is otherwise a full example: it still
6464
asserts a real contract, still exits non-zero on failure, still carries a falsifier,
6565
and still takes a `tests/smoke/catalog.json` row so it runs on every PR.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Add-on register lifecycle
2+
3+
Check-only witness for the first things every add-on author writes: the
4+
package layout, the `bl_info` → manifest migration, a Panel, and a
5+
PropertyGroup on `Scene`, registered and unregistered symmetrically. It
6+
builds a dual-format add-on in a temp directory, validates it with Blender's
7+
own extension validator, registers it and unregisters it. There is no
8+
gallery still, because the witness is registry state and the validator's
9+
exit code (same class as
10+
[`extension-package-lifecycle`](../extension-package-lifecycle/)).
11+
12+
Follows [`addon-scaffolding`](../../skills/addon-scaffolding/SKILL.md),
13+
[`bl-info-migration`](../../skills/bl-info-migration/SKILL.md),
14+
[`ui-panels`](../../skills/ui-panels/SKILL.md) and
15+
[`custom-properties`](../../skills/custom-properties/SKILL.md).
16+
17+
**What it witnesses:**
18+
19+
- `blender --command extension validate` accepts a package with both
20+
`blender_manifest.toml` and a legacy `bl_info` (exit 0). Keeping `bl_info`
21+
for pre-4.2 Blender does not break the Extensions Platform path.
22+
- The same `__init__.py` without a manifest is rejected (exit 1,
23+
`blender_manifest.toml not found`): adding the manifest is the migration.
24+
- `register()` creates the Panel class, the PropertyGroup class and the
25+
`Scene.addon_probe` pointer, which reads its default (1.5).
26+
- `unregister()` that deletes the pointer first, then unregisters the
27+
classes in reverse, leaves nothing behind. Unregistering the classes first
28+
leaves `Scene.addon_probe` registered and pointing at a type that no
29+
longer exists. In a probe, *reading* that dangling property returned an
30+
empty stub on 4.5.14 and **crashed Blender 5.2.1**
31+
(`EXCEPTION_ACCESS_VIOLATION`). This example only checks that the pointer
32+
survived, and never reads it.
33+
34+
Also measured, not checked (no falsifier can reach them through Blender
35+
behaviour alone): a Panel `bl_idname` without `_PT_` only prints a warning
36+
and still registers, while a Panel with no `bl_region_type` is rejected at
37+
`register_class` with `AttributeError` (4.5.14 and 5.2.1).
38+
39+
**What failure each check would catch:**
40+
41+
- exit 3 — the dual-format package did not validate (`--drop-manifest` lands here)
42+
- exit 4 — a package without a manifest validated (`--legacy-gets-manifest` lands here)
43+
- exit 5 — `register()` did not create the panel, group and pointer, or the
44+
default did not read back (`--skip-pointer` lands here)
45+
- exit 6 — `unregister()` left something behind (`--classes-first` lands here: the pointer)
46+
47+
## Re-verified
48+
49+
| Measurement | 4.5.14 | 5.2.1 |
50+
| --- | --- | --- |
51+
| validate dual-format / bl_info-only | 0 / 1 | 0 / 1 |
52+
| after register: panel, group, pointer | all present | all present |
53+
| after unregister (right order / classes first) | nothing / pointer left | nothing / pointer left |
54+
| reading the dangling pointer (probe) | empty stub | crash |
55+
| default exit | 0 | 0 |
56+
| falsifier exits | 3 / 4 / 5 / 6 | 3 / 4 / 5 / 6 |
57+
58+
## API reference
59+
60+
- [Extensions: command line](https://docs.blender.org/manual/en/latest/advanced/command_line/extension_arguments.html)
61+
- [`bpy.utils.register_class`](https://docs.blender.org/api/current/bpy.utils.html#bpy.utils.register_class),
62+
[`unregister_class`](https://docs.blender.org/api/current/bpy.utils.html#bpy.utils.unregister_class)
63+
- [`bpy.props.PointerProperty`](https://docs.blender.org/api/current/bpy.props.html#bpy.props.PointerProperty)
64+
- [`bpy.types.Panel`](https://docs.blender.org/api/current/bpy.types.Panel.html)
65+
66+
## Run
67+
68+
```bash
69+
blender --background --python addon_register_lifecycle.py --
70+
blender --background --python addon_register_lifecycle.py -- --drop-manifest
71+
blender --background --python addon_register_lifecycle.py -- --legacy-gets-manifest
72+
blender --background --python addon_register_lifecycle.py -- --skip-pointer
73+
blender --background --python addon_register_lifecycle.py -- --classes-first
74+
```
75+
76+
## Exit codes
77+
78+
Per-script sequential checks.
79+
80+
| Code | Meaning |
81+
| --- | --- |
82+
| 0 | Success |
83+
| 1 | Uncaught exception (FATAL wrapper) |
84+
| 2 | argparse / usage |
85+
| 3 | Dual-format package did not validate (`--drop-manifest`) |
86+
| 4 | Package without a manifest validated (`--legacy-gets-manifest`) |
87+
| 5 | `register()` missed the panel, group or pointer (`--skip-pointer`) |
88+
| 6 | `unregister()` left something behind (`--classes-first`) |
89+
90+
The `blender-smoke` workflow runs the check on Blender 5.2 LTS and 4.5 LTS
91+
(5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch).
92+
Smoke passes no extra flags on the happy path. Its catalog falsifiers are
93+
`--drop-manifest` (exit 3), `--legacy-gets-manifest` (exit 4),
94+
`--skip-pointer` (exit 5) and `--classes-first` (exit 6).

0 commit comments

Comments
 (0)