Skip to content

Commit 46dffec

Browse files
feat: add sibling showcase/ tree and shipping-crate piece (#148)
* feat: add sibling showcase/ category to the content schema Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * docs: record showcase category conventions Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * feat: add shipping-crate as the first showcase piece Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 876f82a commit 46dffec

20 files changed

Lines changed: 1966 additions & 28 deletions

File tree

.cursor-plugin/plugin.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,8 @@
136136
"examples/vse-gamma-cross",
137137
"examples/vse-linear-modifiers",
138138
"examples/wave-displace"
139+
],
140+
"showcase": [
141+
"showcase/shipping-crate"
139142
]
140143
}

.github/workflows/pages.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ on:
1818
- ".cursor-plugin/plugin.json"
1919
- "assets/**"
2020
- "examples/**"
21+
- "showcase/**"
2122
- "docs/gallery/**"
2223
- "scripts/build_gallery.py"
2324
- "scripts/site/**"

.github/workflows/validate.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ jobs:
184184
)
185185
186186
# Every manifest path must exist on disk.
187-
for key in ('skills', 'rules', 'snippets', 'templates', 'examples'):
187+
for key in ('skills', 'rules', 'snippets', 'templates', 'examples', 'showcase'):
188188
for path in manifest.get(key, []):
189189
if not os.path.exists(path):
190190
errors.append(f'{key}: manifest lists missing path {path}')
@@ -200,6 +200,9 @@ jobs:
200200
'examples': sorted(
201201
d for d in glob.glob('examples/*') if os.path.isdir(d)
202202
),
203+
'showcase': sorted(
204+
d for d in glob.glob('showcase/*') if os.path.isdir(d)
205+
),
203206
}
204207
for key, paths in expected.items():
205208
listed = {p.replace('\\', '/') for p in manifest.get(key, [])}
@@ -252,6 +255,13 @@ jobs:
252255
if os.path.isdir(os.path.join('examples', d))
253256
and os.path.exists(os.path.join('examples', d, 'README.md'))
254257
])
258+
showcase_count = 0
259+
if os.path.isdir('showcase'):
260+
showcase_count = len([
261+
d for d in os.listdir('showcase')
262+
if os.path.isdir(os.path.join('showcase', d))
263+
and os.path.exists(os.path.join('showcase', d, 'README.md'))
264+
])
255265
256266
readme = open('README.md').read()
257267
if f'{skill_count} skills' not in readme:
@@ -265,13 +275,19 @@ jobs:
265275
errors.append(f'README snippet count mismatch (expected "{snippet_count} snippets" substring)')
266276
if f'{example_count} examples' not in readme:
267277
errors.append(f'README example count mismatch (expected "{example_count} examples" substring)')
278+
showcase_word = 'piece' if showcase_count == 1 else 'pieces'
279+
showcase_needle = f'{showcase_count} showcase {showcase_word}'
280+
if showcase_needle not in readme:
281+
errors.append(
282+
f'README showcase count mismatch (expected "{showcase_needle}" substring)'
283+
)
268284
269285
if errors:
270286
for e in errors:
271287
print(f'::error::{e}', file=sys.stderr)
272288
sys.exit(1)
273289
274-
print(f'Counts verified: {skill_count} skills, {rule_count} rules, {template_count} {template_word}, {snippet_count} snippets, {example_count} examples')
290+
print(f'Counts verified: {skill_count} skills, {rule_count} rules, {template_count} {template_word}, {snippet_count} snippets, {example_count} examples, {showcase_needle}')
275291
PYEOF
276292
277293
validate-harness:

AGENTS.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ a `.cursor-plugin/plugin.json` manifest so the ecosystem drift checker
2020
classifies it as a `cursor-plugin`. This is content the AI loads when the user
2121
asks Blender questions or works on Blender add-ons in Cursor or Claude Code.
2222

23-
The content base is 16 skills, 9 rules, 3 templates, 27 snippets, and 59
24-
examples (counts are CI-enforced against README.md and the manifest). The full
25-
inventory tables and per-item purposes live in `CLAUDE.md`. Example anatomy
26-
and authoring rules: copy `examples/bmesh-gear/`; the render look is specified
23+
The content base is 16 skills, 9 rules, 3 templates, 27 snippets, 59
24+
examples, and 1 showcase piece (counts are CI-enforced against README.md
25+
and the manifest). The full inventory tables and per-item purposes live in
26+
`CLAUDE.md`. Example anatomy and authoring rules: copy `examples/bmesh-gear/`;
27+
showcase conventions: `showcase/README.md`. The render look is specified
2728
in `docs/VISUAL-STYLE.md`; the canonical run prompt is
2829
`docs/new-example-prompt.md`.
2930

@@ -37,6 +38,8 @@ Blender-Developer-Tools/
3738
snippets/<snippet-name>.py # 27 standalone Python snippets
3839
examples/<name>/ # 59 runnable smoke-gated examples (+ gallery.json)
3940
examples/gallery_framing.py # shared Layer 1 framing measurement (render path only)
41+
showcase/<name>/ # budget-conformance props (sibling of examples/)
42+
showcase/gallery.json # this tree's gallery index; merged into docs/gallery/
4043
scripts/build_gallery.py # generates docs/gallery/ (stdlib only)
4144
scripts/site/ # vendored landing-page build (build_site.py + template)
4245
docs/gallery/ # committed generated gallery pages + hero assets

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ rules/<rule-name>.mdc - Anti-pattern rules, 9 total
2222
templates/<template-name>/ - Starter projects, 3 total
2323
snippets/<snippet-name>.py - Standalone code patterns, 27 total
2424
examples/<name>/ - Runnable smoke-gated examples, 59 total (+ gallery.json)
25-
scripts/build_gallery.py - Regenerates docs/gallery/ from gallery.json (stdlib only)
25+
showcase/<name>/ - Budget-conformance props, 1 piece (sibling of examples/; see showcase/README.md)
26+
scripts/build_gallery.py - Regenerates docs/gallery/ from examples/gallery.json + showcase/gallery.json
2627
scripts/site/ - Vendored landing-page build (Jinja2)
2728
docs/gallery/ - Committed generated gallery pages + hero renders
2829
VERSION - Source of truth for the repo version

CONTRIBUTING.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,19 @@ templates/
3535
blender_manifest.toml
3636
__init__.py
3737
README.md
38+
showcase/
39+
README.md
40+
gallery.json
41+
<piece-name>/
42+
README.md
3843
```
3944

4045
- **`skills/`** - one directory per skill, each containing `SKILL.md` with YAML frontmatter (`name`, `description`, `standards-version`).
4146
- **`rules/`** - Cursor-style rules as `.mdc` files with YAML frontmatter (`description`, `alwaysApply`, `globs`, `standards-version`).
4247
- **`snippets/`** - small standalone `.py` files (5 to 50 lines) demonstrating a single canonical pattern.
4348
- **`templates/`** - copy-paste starting points; one directory per template.
49+
- **`showcase/`** - budget-conformance props, sibling of `examples/`. Not API
50+
contracts. Conventions: [`showcase/README.md`](showcase/README.md).
4451

4552
## Adding a Skill
4653

@@ -86,6 +93,21 @@ templates/
8693
1. Add a directory under `templates/`, e.g. `templates/headless-batch-script-template/`.
8794
2. Include all files needed for an immediate copy-paste starting point. For add-on templates, include `blender_manifest.toml`, `__init__.py`, and a brief `README.md`.
8895

96+
## Adding a Showcase Piece
97+
98+
Read [`showcase/README.md`](showcase/README.md) first. Showcase asserts
99+
budget conformance, never an API contract.
100+
101+
1. Add `showcase/<kebab-name>/` with a script, a README that includes an
102+
exit-code table, and a falsifier that breaks one pipeline stage so a
103+
**named** budget fails.
104+
2. List the directory in `.cursor-plugin/plugin.json` `"showcase"` and add a
105+
`tests/smoke/catalog.json` row. The runner takes opaque script paths.
106+
3. Add a `showcase/gallery.json` `pieces[]` entry, render a still, and run
107+
`python scripts/build_gallery.py`. Do not hand-edit `docs/gallery/` HTML.
108+
4. Update the README showcase-piece count. `validate-counts` checks it
109+
separately from the example total.
110+
89111
## Blender Version Targeting
90112

91113
Content targets **Blender 5.2 LTS** as primary, **Blender 5.1** as prior stable, and **Blender 4.5 LTS** as fallback. When the API differs, branch on `bpy.app.version` and document both paths. Example:
@@ -169,7 +191,7 @@ The drift-check workflow enforces these on every push and PR.
169191

170192
## Aggregate Counts
171193

172-
`README.md` declares aggregate counts (e.g. "8 skills, 4 rules, 1 template, and 10 snippets"). The `validate-counts` job in `.github/workflows/validate.yml` enforces these substrings against the filesystem on every push and PR. When you add or remove content, update the README counts in the same commit.
194+
`README.md` declares aggregate counts (e.g. "16 skills, 9 rules, 3 templates, 27 snippets, 59 examples, and 1 showcase piece"). 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.
173195

174196
## Pull Request Process
175197

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
</p>
1919

2020
<p align="center">
21-
<strong>16 skills</strong> &nbsp;&bull;&nbsp; <strong>9 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>27 snippets</strong> &nbsp;&bull;&nbsp; <strong>59 examples</strong>
21+
<strong>16 skills</strong> &nbsp;&bull;&nbsp; <strong>9 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>27 snippets</strong> &nbsp;&bull;&nbsp; <strong>59 examples</strong> &nbsp;&bull;&nbsp; <strong>1 showcase piece</strong>
2222
</p>
2323

2424
<p align="center">
2525
<a href="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/">Examples Gallery</a>
2626
&nbsp;&bull;&nbsp; <a href="#quick-start">Quick start</a>
27-
&nbsp;&bull;&nbsp; <a href="#examples">Examples</a>
27+
&nbsp;&bull;&nbsp; <a href="#examples">Examples</a>
28+
&nbsp;&bull;&nbsp; <a href="showcase/">Showcase</a>
2829
&nbsp;&bull;&nbsp; <a href="skills/">Skills</a>
2930
&nbsp;&bull;&nbsp; <a href="rules/">Rules</a>
3031
&nbsp;&bull;&nbsp; <a href="templates/">Templates</a>
@@ -36,7 +37,7 @@
3637

3738
## Overview
3839

39-
This repository ships **16 skills, 9 rules, 3 templates, 27 snippets, and 59 examples** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
40+
This repository ships **16 skills, 9 rules, 3 templates, 27 snippets, 59 examples, and 1 showcase piece** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
4041

4142
The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capable client) when working on Blender add-ons, geometry nodes scripts, batch pipelines, or animation tooling. There is no build step. Edit the markdown and Python files directly.
4243

@@ -45,7 +46,8 @@ The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capabl
4546
| **Skills** | Guided workflows: scaffolding, operators, panels, properties, mesh and bmesh, headless batch, slotted actions, geometry nodes, procedural materials, depsgraph queries, drivers and handlers, `bl_info` migration, video sequencer, imported-mesh cleanup, engine export presets |
4647
| **Rules** | Guardrails for the most common AI mistakes: ops-in-loops, bmesh leaks, legacy `bl_info` only, prop assignments, deprecated context-copy override, per-element loops over bulk mesh data, import without scale check, export without evaluated geometry, mixed glTF/FBX axis RNA |
4748
| **Templates** | A working Extensions Platform add-on starter, a headless batch script starter, and a GLB-in engine-ready asset pipeline |
48-
| **Snippets** | 24 small standalone Python files demonstrating canonical patterns |
49+
| **Snippets** | 27 small standalone Python files demonstrating canonical patterns |
50+
| **Showcase** | Budget-conformance props under [`showcase/`](showcase/). Not examples. Conventions: [`showcase/README.md`](showcase/README.md) |
4951

5052
## Quick start
5153

@@ -69,6 +71,14 @@ blender --background --python examples/bmesh-gear/bmesh_gear.py --
6971
| Blender 5.1 | Prior stable (weekly cron; PR via `needs-5.1` or manual dispatch) |
7072
| Blender 4.5 LTS | Fallback supported (skills show both code paths where 4.x and 5.x APIs diverge) |
7173

74+
## Showcase
75+
76+
Budget-conformance props. Not examples. Conventions: [`showcase/README.md`](showcase/README.md).
77+
78+
<a href="showcase/shipping-crate/"><img src="showcase/shipping-crate/preview.webp" alt="Shipping crate: a wooden slat crate with iron corner brackets on a dark studio floor, warm wedge on the back wall" /></a>
79+
80+
First piece: [`shipping-crate`](showcase/shipping-crate/) — procedural crate through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets. Falsifier `--skip-decimate` exits 9.
81+
7282
## Examples
7383

7484
Runnable, smoke-gated demos live in [`examples/`](examples/) — each is executed headless on

ROADMAP.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ Provider-agnostic GLB-in / engine-ready-out. This repo does not generate meshes.
106106

107107
Not committed; target list for the next content version. (v0.3.0 shipped the smoke-gated `examples/` track.)
108108

109+
- ~~Showcase sibling tree + shipping-crate pilot~~ **SHIPPED** as `showcase/shipping-crate/` — budget-conformance crate composing bake, LOD, collider, Unity glTF; `--skip-decimate` exits 9 on the LOD1 ratio band; DECIMATE COLLAPSE ratios diverge on 5.2 vs 4.5/5.1
110+
- Procedural terrain or landscape showcase using Geometry Nodes scatter
111+
- Hero prop with a more complex silhouette (lantern or treasure chest) as a second showcase piece
112+
- Small modular kit showcase on recognizable geometry (`modular-kit-snap` contract)
113+
109114
- ~~Custom ID-property delete witness~~ **SHIPPED** as `examples/cross-version-property-delete/` — IDs built via `bpy.data.objects.new` (not `active_object`); `property_unset` is TypeError and leaves the key; `del` removes it on 4.5 LTS and 5.x
110115
- ~~USD export evaluation_mode witness~~ **SHIPPED** as `examples/usd-export-evaluation-mode/` — probed `wm.usd_export` on CI Linux portables 5.2.1 (`9e2066aef7ef`) and 4.5.13 (`daeeeca98fb0`); TESSELLATE+VIEWPORT 26/24, TESSELLATE+RENDER 98/96; BEST_MATCH writes the 8-vert catmullClark cage so the mode is silent
111116
- ~~Repeat Zone / For Each Element pairing witness~~ **SHIPPED** as `examples/gn-zone-iterate/``pair_with_output` is load-bearing (unpaired evaluates 0 verts); Repeat `8×(1+N)` with X-centers at `k×STEP`; For Each `8×P` with Z-centers at `i×STEP`; For Each main Geometry is a passthrough (6 verts); count-only is insufficient (`--no-offset` still 32 verts, one X-center)
21.3 KB
Loading

docs/gallery/index.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ <h1>Examples Gallery</h1>
272272
autocomplete="off" spellcheck="false" aria-label="Search examples" />
273273
<button class="q-clear" id="qClear" type="button" aria-label="Clear search" hidden>&times;</button>
274274
</div>
275-
<span class="count" id="count" role="status" aria-live="polite">51 examples</span>
275+
<span class="count" id="count" role="status" aria-live="polite">52 examples</span>
276276
<div class="density" role="group" aria-label="Card density">
277277
<button class="density-btn" data-density="compact" type="button" aria-pressed="false">Compact</button>
278278
<button class="density-btn" data-density="detailed" type="button" aria-pressed="false">Detailed</button>
@@ -313,6 +313,7 @@ <h1>Examples Gallery</h1>
313313
<button class="chip" data-tag="rendering" type="button">rendering</button>
314314
<button class="chip" data-tag="sequencer" type="button">sequencer</button>
315315
<button class="chip" data-tag="shape-keys" type="button">shape-keys</button>
316+
<button class="chip" data-tag="showcase" type="button">showcase</button>
316317
<button class="chip" data-tag="sky" type="button">sky</button>
317318
<button class="chip" data-tag="transform" type="button">transform</button>
318319
<button class="chip" data-tag="transforms" type="button">transforms</button>
@@ -884,6 +885,17 @@ <h2><a href="gn-socket-rename/">gn-socket-rename</a></h2>
884885
<a class="card-link" href="gn-socket-rename/">View example <span aria-hidden="true">&rarr;</span></a>
885886
</div>
886887
</article>
888+
<article class="card" data-tags="mesh export showcase">
889+
<a class="card-media" href="shipping-crate/" aria-label="shipping-crate example detail page">
890+
<img src="assets/shipping-crate-hero.webp" alt="shipping-crate — A procedural shipping crate through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract." loading="lazy" decoding="async" />
891+
</a>
892+
<div class="card-body">
893+
<h2><a href="shipping-crate/">shipping-crate</a></h2>
894+
<p class="teaches">A procedural shipping crate through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
895+
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 552 tris, two materials, UVs in 0..1 with zero AABB overlap, outer AABB 1.256×0.856×0.748 m, LOD ratios in band (5.2 COLLAPSE more aggressive), convex collider 20 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
896+
<a class="card-link" href="shipping-crate/">View example <span aria-hidden="true">&rarr;</span></a>
897+
</div>
898+
</article>
887899
</div>
888900
<p class="noresults" id="noResults" hidden>No examples match the current filters.
889901
<button class="chip" id="resetFilters" type="button">Clear search and tags</button></p>

0 commit comments

Comments
 (0)