|
| 1 | +# Blender Developer Tools — Technical Audit & Roadmap |
| 2 | + |
| 3 | +_Audit date: 2026-06-20 · Repo @ v0.5.0 · Auditor: Principal Eng review_ |
| 4 | + |
| 5 | +## 0. Important reframing |
| 6 | + |
| 7 | +The audit brief assumed a runnable add-on (operators, UI panels, a socket/file-watcher |
| 8 | +execution model). **That is not what this repo is.** This is a **content / knowledge-pack |
| 9 | +repository** — an AI "skill pack" of `SKILL.md` workflows, `.mdc` anti-pattern rules, copy-paste |
| 10 | +`snippets/`, starter `templates/`, and self-verifying `examples/` consumed by Cursor and Claude |
| 11 | +Code. The only executable code is (a) the templates/examples/snippets, which are *teaching |
| 12 | +artifacts*, and (b) the CI tooling (`tests/smoke/`, `scripts/build_gallery.py`). |
| 13 | + |
| 14 | +So "core operator logic" and "UI panels" exist only as *demonstration* code in |
| 15 | +`templates/extension-addon-template/__init__.py` and the example scripts. The architectural |
| 16 | +gaps that matter are about **content correctness, validation coverage, and distribution**, not |
| 17 | +runtime robustness of a live tool. |
| 18 | + |
| 19 | +## 1. Architecture map |
| 20 | + |
| 21 | +| Layer | What it is | Notable detail | |
| 22 | +| --- | --- | --- | |
| 23 | +| `skills/` (12) | Markdown workflows w/ YAML frontmatter | `name` must match dir; `standards-version` gated in CI | |
| 24 | +| `rules/` (6) | `.mdc` anti-pattern rules | `alwaysApply` / scope globs for Cursor | |
| 25 | +| `snippets/` (17) | Standalone `.py` patterns | `py_compile`-checked only | |
| 26 | +| `templates/` (2) | extension add-on + headless batch | real, registerable add-on code | |
| 27 | +| `examples/` (4) | self-verifying demo scripts | run inside real Blender in CI, exit non-zero on failure | |
| 28 | +| `tests/smoke/` | headless Blender harness | **already exists** — runs on 5.1 + 4.5 matrix | |
| 29 | +| `scripts/build_gallery.py` | stdlib HTML generator | builds `docs/gallery/` Pages site | |
| 30 | +| `.github/workflows/` | 8 workflows | validate, blender-smoke, drift-check, release, pages, label-sync, stale, dependabot | |
| 31 | +| `.cursor-plugin/plugin.json` | distribution manifest | **stale: v0.2.3, missing arrays** | |
| 32 | + |
| 33 | +**Architectural style:** static content pack + automated content-integrity CI. Distribution is |
| 34 | +"copy `skills/`+`rules/` into the workspace" or reference the checkout directly. **No MCP server** |
| 35 | +(explicitly noted in CLAUDE.md/AGENTS.md). |
| 36 | + |
| 37 | +**Genuine strengths** (the brief assumed these were missing — they are not): |
| 38 | +- A real **headless Blender testing harness** already exists. `tests/smoke/run_smoke.py` asserts |
| 39 | + *content*, not just "no exception" (e.g. EEVEE engine-id polarity, slotted-actions boundary, |
| 40 | + driver `id_type` fix, SDF `GridToMesh` link validity, render-non-black). It deliberately |
| 41 | + **copies** content rather than importing it, to catch drift in shipped files. |
| 42 | +- `blender-smoke.yml` runs that harness on both the current stable and active LTS, plus runs the |
| 43 | + shipped templates and examples end-to-end (glTF magic-bytes check, exit-code-2 no-mesh path). |
| 44 | +- Conventional-commit-driven release automation with doc-sync; count-integrity gate in CI. |
| 45 | + |
| 46 | +## 2. Gap analysis |
| 47 | + |
| 48 | +### G1 — Distribution manifest drift (concrete bug) |
| 49 | +`.cursor-plugin/plugin.json` is frozen at `version: 0.2.3`, enumerates only `skills` (12) and |
| 50 | +`rules` (6), and has **no `snippets`, `templates`, or `examples` arrays**. Nothing in |
| 51 | +`validate.yml` checks the manifest against the filesystem, so it rotted silently while the repo |
| 52 | +reached v0.5.0. Consumers reading the manifest see a v0.2.0-era subset. |
| 53 | + |
| 54 | +### G2 — Validation coverage holes |
| 55 | +`validate.yml` enforces structure, frontmatter, name=dir, `py_compile`, and README counts. It does |
| 56 | +**not**: (a) lint/run snippets beyond byte-compilation (no Blender import, no `ruff`); (b) verify |
| 57 | +every snippet/skill/template is referenced from README/ROADMAP/manifest (orphan detection); |
| 58 | +(c) check internal/external doc links; (d) validate `gallery.json` against the on-disk examples. |
| 59 | +The smoke harness covers a hand-picked subset, so a *new* snippet can ship un-exercised. |
| 60 | + |
| 61 | +### G3 — Smoke harness selectivity (silent coverage cap) |
| 62 | +The harness exercises ~6 headline behaviors and 4 examples. 17 snippets + 12 skills are largely |
| 63 | +un-run. There is no manifest of "what is covered vs. not," so coverage erosion is invisible — a |
| 64 | +classic silent-truncation smell. No per-snippet smoke and no coverage report. |
| 65 | + |
| 66 | +### G4 — No MCP / programmatic surface |
| 67 | +The brief's strongest forward-looking point. Today content is human/AI-read-only. There is no |
| 68 | +Model Context Protocol server exposing skills/rules/snippets as retrievable resources or tools |
| 69 | +(e.g. `get_skill`, `search_patterns`, `lint_blender_py`). This is the natural next-gen evolution |
| 70 | +and the cleanest way to make the pack first-class in MCP-aware clients beyond Cursor/Claude Code. |
| 71 | + |
| 72 | +### G5 — Performance/dense-data guidance is pattern-level only |
| 73 | +Rules flag `bpy.ops`-in-loops and per-element loops, and snippets show `foreach_set/get`. But |
| 74 | +there is no skill on **profiling / scaling to dense meshes** (e.g. numpy buffer interop, |
| 75 | +`foreach_get` into preallocated numpy arrays, attribute-domain bulk ops, depsgraph cost). For a |
| 76 | +tool whose whole pitch is "do bulk work the fast way," this is a content gap. |
| 77 | + |
| 78 | +### G6 — LTS/version sweep is manual and unpinned |
| 79 | +5.2 LTS lands ~mid-2026. The smoke matrix hard-codes `5.1`/`4.5`. There is no scheduled job to |
| 80 | +detect a new stable/LTS series and open a sweep issue, so the "5.2 sweep" depends on a human |
| 81 | +remembering. |
| 82 | + |
| 83 | +## 3. Roadmap |
| 84 | + |
| 85 | +### Stage 1 — Short-term / Quick Wins (days) |
| 86 | +- **Fix + gate the Cursor manifest** (G1). Regenerate `plugin.json` from the filesystem and add a |
| 87 | + CI check so it can't drift again. |
| 88 | +- **Orphan + link audit job** (G2). Fail CI if any snippet/skill/template/example is unreferenced |
| 89 | + by README/ROADMAP/manifest, or if a markdown link 404s. |
| 90 | +- **`gallery.json` ↔ disk validator** (G2). Assert every example dir + hero image referenced |
| 91 | + exists and vice-versa. |
| 92 | +- **Coverage manifest for smoke** (G3). Emit a printed "covered / not covered" table so gaps are |
| 93 | + visible in the CI log. |
| 94 | + |
| 95 | +### Stage 2 — Mid-term / Core Enhancements (weeks) |
| 96 | +- **Per-snippet smoke harness** (G3): a generic runner that imports/execs each snippet in headless |
| 97 | + Blender behind a small `# smoke: skip` opt-out, closing the un-run-content gap. |
| 98 | +- **`ruff` + import-time lint** of all Python content with a Blender-aware config. |
| 99 | +- **New skill: `performance-and-dense-data`** (G5) — numpy `foreach_get`/`foreach_set` buffers, |
| 100 | + attribute domains, when bmesh beats data API, profiling with `time`/`cProfile` headless. |
| 101 | +- **New skills from the committed pool**: `modal-operators`, `mathutils-patterns`, `usd-pipelines`. |
| 102 | +- **Automated LTS-series watcher** (G6): scheduled job that diffs `download.blender.org` series |
| 103 | + against the matrix and opens a sweep issue. |
| 104 | + |
| 105 | +### Stage 3 — Long-term / Next-Gen Capabilities (quarters) |
| 106 | +- **MCP server** (G4) exposing the pack as MCP resources + tools: `list_skills`, `get_skill`, |
| 107 | + `search_patterns`, and a `lint_blender_python` tool wrapping the `.mdc` rules as executable |
| 108 | + checks. Ships as an optional adjacent package so the content repo stays a pure pack. |
| 109 | +- **Rules-as-linter**: compile the 6 `.mdc` anti-patterns into an actual AST linter |
| 110 | + (`libcst`/`ast`) that runs in CI and is reusable by the MCP `lint` tool and by consumers. |
| 111 | +- **5.2 LTS parity sweep** once released; bump templates' `blender_version_min` where 5.2 APIs are |
| 112 | + used. |
| 113 | +- **Fleet Pages examples support** (already in ROADMAP) — lift `gallery.json` into the shared |
| 114 | + template, retire the local generator. |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +## 4. GitHub Issues (copy-paste ready) |
| 119 | + |
| 120 | +### Issue 1 — Fix and CI-gate the stale `.cursor-plugin/plugin.json` manifest |
| 121 | + |
| 122 | +**Labels:** `bug`, `ci`, `distribution` |
| 123 | + |
| 124 | +**Context / Problem Statement** |
| 125 | +`.cursor-plugin/plugin.json` is pinned at `"version": "0.2.3"` while the repo is at v0.5.0 |
| 126 | +(`VERSION`). It lists only the `skills` (12) and `rules` (6) arrays and omits `snippets`, |
| 127 | +`templates`, and `examples` entirely. No job in `.github/workflows/validate.yml` checks the |
| 128 | +manifest against the filesystem, so it has drifted silently. Consumers that read the manifest see a |
| 129 | +v0.2.0-era view of the pack. |
| 130 | + |
| 131 | +**Proposed Solution / Implementation Steps** |
| 132 | +1. Decide the manifest schema for `snippets`/`templates`/`examples` (mirror the existing |
| 133 | + relative-path-array style used for `skills`/`rules`). If the upstream `cursor-plugin` schema |
| 134 | + only supports skills+rules, document that and instead just fix `version` + add the gate. |
| 135 | +2. Update `.cursor-plugin/plugin.json`: bump `version` to match `VERSION`, and add the missing |
| 136 | + arrays enumerating files under `snippets/`, `templates/`, `examples/`. |
| 137 | +3. Add a `validate-manifest` job to `validate.yml` (a `python3` heredoc like the existing |
| 138 | + `validate-counts` job) that: |
| 139 | + - loads `plugin.json`, |
| 140 | + - asserts every path in each array exists on disk, |
| 141 | + - asserts every `skills/*/SKILL.md` and `rules/*.mdc` on disk is present in the manifest, |
| 142 | + - asserts `plugin.json` `version` equals `VERSION` (or is intentionally decoupled — pick one and |
| 143 | + enforce it). |
| 144 | +4. If `version` should track releases, add it to the `release-doc-sync` owned-lines list so the |
| 145 | + release pipeline rewrites it; otherwise document why it is independent in `AGENTS.md`. |
| 146 | + |
| 147 | +**Definition of Done** |
| 148 | +- [ ] `plugin.json` enumerates all current skills, rules, snippets, templates, examples. |
| 149 | +- [ ] `plugin.json` `version` reconciled with `VERSION` (synced or documented as independent). |
| 150 | +- [ ] New CI job fails on any manifest↔filesystem mismatch (proven by a deliberately broken commit). |
| 151 | +- [ ] `AGENTS.md` "CI/CD workflows" section documents the new job. |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +### Issue 2 — Add orphan-content and dead-link validation to CI |
| 156 | + |
| 157 | +**Labels:** `ci`, `content-integrity` |
| 158 | + |
| 159 | +**Context / Problem Statement** |
| 160 | +`validate.yml` checks structure, frontmatter, name=dir, `py_compile`, and README aggregate counts, |
| 161 | +but nothing detects a snippet/skill/template/example that ships **without being referenced** by |
| 162 | +`README.md`, `ROADMAP.md`, or the manifest, nor does anything catch broken markdown links. New |
| 163 | +content can land orphaned (discoverable only by directory listing), and doc links can rot. |
| 164 | + |
| 165 | +**Proposed Solution / Implementation Steps** |
| 166 | +1. Add an `orphan-check` job to `validate.yml`: enumerate files under `snippets/`, `skills/`, |
| 167 | + `templates/`, `examples/`; for each, grep `README.md` + `ROADMAP.md` for its name; fail listing |
| 168 | + any unreferenced item. |
| 169 | +2. Add a lightweight link check: collect markdown links from `*.md`, validate that relative targets |
| 170 | + exist on disk; optionally check external `https://` links with a `--allow-fail` soft mode to |
| 171 | + avoid flakiness (or gate external checks behind the weekly `schedule`, not PRs). |
| 172 | +3. Keep it stdlib-only (consistent with `build_gallery.py`) so no new CI deps are needed. |
| 173 | + |
| 174 | +**Definition of Done** |
| 175 | +- [ ] CI fails when a snippet/skill/template/example is unreferenced in README/ROADMAP. |
| 176 | +- [ ] CI fails on a broken **relative** markdown link. |
| 177 | +- [ ] External link checks run on the weekly schedule, not on every PR (no PR flakiness). |
| 178 | +- [ ] A deliberately orphaned test file proves the gate fires, then is removed. |
| 179 | + |
| 180 | +--- |
| 181 | + |
| 182 | +### Issue 3 — Generalize the smoke harness to per-snippet coverage + emit a coverage table |
| 183 | + |
| 184 | +**Labels:** `testing`, `enhancement` |
| 185 | + |
| 186 | +**Context / Problem Statement** |
| 187 | +`tests/smoke/run_smoke.py` is excellent but exercises only ~6 hand-picked headline behaviors and |
| 188 | +`blender-smoke.yml` runs 4 examples. The 17 snippets and 12 skills are largely **un-run** in real |
| 189 | +Blender, and there is no record of what is covered. A new snippet can ship having only passed |
| 190 | +`py_compile`, and coverage can erode invisibly (silent-truncation risk). |
| 191 | + |
| 192 | +**Proposed Solution / Implementation Steps** |
| 193 | +1. Add `tests/smoke/run_snippets.py`: iterate `snippets/*.py`, `exec` each inside a `reset()` |
| 194 | + factory-empty scene in headless Blender, treating any raised exception as failure (mirror the |
| 195 | + try/except wrapper already in `run_smoke.py`). |
| 196 | +2. Support a `# smoke: skip <reason>` first-line pragma for snippets that need scene state they |
| 197 | + can't self-provide; **print the skipped list** so skips are visible, never silent. |
| 198 | +3. Emit a coverage table at the end: `N snippets run, M skipped (reasons), K skills with a smoke |
| 199 | + check`. Print it to the CI log. |
| 200 | +4. Wire a new step into `blender-smoke.yml` after the existing smoke driver, on the same 5.1/4.5 |
| 201 | + matrix, `xvfb-run`-wrapped. |
| 202 | + |
| 203 | +**Definition of Done** |
| 204 | +- [ ] Every snippet is either executed in headless Blender or explicitly `# smoke: skip`-ged with a |
| 205 | + reason. |
| 206 | +- [ ] CI log prints a run/skip/coverage summary table. |
| 207 | +- [ ] A snippet that raises in Blender fails the job (proven once). |
| 208 | +- [ ] Runs green on both 5.1 and 4.5 matrix legs. |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +### Issue 4 — New skill: `performance-and-dense-data` (numpy buffer interop + profiling) |
| 213 | + |
| 214 | +**Labels:** `content`, `skill` |
| 215 | + |
| 216 | +**Context / Problem Statement** |
| 217 | +The pack's core thesis is "do bulk Blender work the fast way," and rules |
| 218 | +(`prefer-data-over-ops-in-loops`, `use-foreach-set-for-bulk-data`) plus the `foreach-*` snippets |
| 219 | +point that direction. But there is **no skill** that teaches scaling to dense meshes: numpy |
| 220 | +buffer interop with `foreach_get`/`foreach_set`, attribute-domain bulk operations, the |
| 221 | +bmesh-vs-data-API decision under load, and how to profile headless. AI agents currently get the |
| 222 | +"don't do it slow" rules without a positive "here is the fast, measured pattern" skill. |
| 223 | + |
| 224 | +**Proposed Solution / Implementation Steps** |
| 225 | +1. Create `skills/performance-and-dense-data/SKILL.md` with the standard YAML frontmatter |
| 226 | + (`name: performance-and-dense-data`, one-line `description`, `standards-version: 1.10.0`). |
| 227 | +2. Cover: preallocated `numpy` arrays with `foreach_get`/`foreach_set` (`np.empty(n*3)`), reshaping |
| 228 | + conventions, attribute API bulk read/write across domains, when bmesh wins, and a headless |
| 229 | + `cProfile`/`time.perf_counter` measurement pattern. Show 5.1 and 4.5 paths where they diverge. |
| 230 | +3. Add 1–2 supporting snippets (e.g. `numpy-foreach-vertices.py`) and a self-verifying assertion in |
| 231 | + the smoke harness (roundtrip equality + a coarse timing sanity check). |
| 232 | +4. Update `README.md` aggregate counts, `ROADMAP.md` candidate pool, `AGENTS.md`, and |
| 233 | + `plugin.json`; commit with `feat:`. |
| 234 | + |
| 235 | +**Definition of Done** |
| 236 | +- [ ] New skill passes frontmatter + name=dir CI checks. |
| 237 | +- [ ] Shows numpy buffer interop and at least one profiling pattern, with 4.5/5.1 branches where |
| 238 | + relevant. |
| 239 | +- [ ] Supporting snippet(s) added and smoke-exercised. |
| 240 | +- [ ] README counts, ROADMAP, AGENTS, and manifest updated (counts gate green). |
| 241 | + |
| 242 | +--- |
| 243 | + |
| 244 | +### Issue 5 — Prototype an MCP server exposing the pack as resources + a Blender-Python lint tool |
| 245 | + |
| 246 | +**Labels:** `enhancement`, `mcp`, `next-gen` |
| 247 | + |
| 248 | +**Context / Problem Statement** |
| 249 | +CLAUDE.md and AGENTS.md both note "there is no MCP server." Today the content is consumable only by |
| 250 | +clients that natively read `skills/`+`rules/` (Cursor, Claude Code). An MCP server would make the |
| 251 | +pack first-class in any MCP-aware client and turn the 6 `.mdc` anti-pattern rules into an |
| 252 | +executable lint tool — a far stronger guarantee than prose rules. |
| 253 | + |
| 254 | +**Proposed Solution / Implementation Steps** |
| 255 | +1. Add an **optional adjacent** package (e.g. `mcp-server/`) so the content repo stays a pure pack; |
| 256 | + the server reads `skills/`, `rules/`, `snippets/` from the repo at runtime. |
| 257 | +2. Expose MCP **resources**: `list_skills`, `get_skill(name)`, `list_snippets`, `get_snippet(name)`, |
| 258 | + `search_patterns(query)`. |
| 259 | +3. Expose an MCP **tool** `lint_blender_python(source)` implementing the rules as AST checks: |
| 260 | + `bpy.ops.*` inside loops, `bmesh.new()` without paired `free()`, prop assignment vs annotation, |
| 261 | + `bpy.context.copy()` override, per-element bulk loops. Reuse this checker in CI (see the |
| 262 | + rules-as-linter long-term item). |
| 263 | +4. Document install/usage in a `mcp-server/README.md`; keep it out of the count/validate gates |
| 264 | + (it's tooling, not pack content) but add its own minimal test job. |
| 265 | + |
| 266 | +**Definition of Done** |
| 267 | +- [ ] MCP server lists and serves skills/rules/snippets as resources. |
| 268 | +- [ ] `lint_blender_python` flags all 6 anti-patterns on a fixture file and passes clean code. |
| 269 | +- [ ] Server runs against the live repo checkout with documented setup. |
| 270 | +- [ ] CI runs the server's own tests without touching the content-count gates. |
0 commit comments