AGENTS.md · diff
git:20261002.c3d778d to git:20261005.cf76bff
37 added, 26 removed. Audit A to A.
<!-- standards-version: 1.10.0 -->
# AGENTS.md
Guidance for AI coding agents working on the Blender Developer Tools repository.
**Division of labor:** this file carries fleet-standard governance and workflow
rules — branching, commits, merge and CI-evidence policy, release automation,
authoring standards. `CLAUDE.md` carries repo-specific operational facts an
agent needs at runtime — content inventory, Blender runtime discovery, git
staging hazards, and the example-shipping quality gates. Read both; neither
repeats the other.
## Repository overview
Skills, rules, snippets, starter templates, and runnable smoke-gated examples
for Blender Python development. The repo targets **Blender 5.2 LTS** (current
stable) with a **Blender 4.5 LTS** fallback. **Blender 5.1** is prior stable. There is no MCP server. It ships
a `.cursor-plugin/plugin.json` manifest so the ecosystem drift checker
classifies it as a `cursor-plugin`. This is content the AI loads when the user
asks Blender questions or works on Blender add-ons in Cursor or Claude Code.
- The content base is 16 skills, 9 rules, 3 templates, 27 snippets, 62
- examples, and 74 showcase pieces (counts are CI-enforced against README.md)
+ The content base is 16 skills, 9 rules, 3 templates, 27 snippets, 64
+ examples, and 76 showcase pieces (counts are CI-enforced against README.md)
and the manifest). The full inventory tables and per-item purposes live in
`CLAUDE.md`. Example anatomy and authoring rules: copy `examples/bmesh-gear/`;
showcase conventions: `showcase/README.md`. The render look is specified
in `docs/VISUAL-STYLE.md`; the canonical run prompt is
`docs/new-example-prompt.md`.
## Repository structure
```
Blender-Developer-Tools/
skills/<skill-name>/SKILL.md # 16 skill files
rules/<rule-name>.mdc # 9 rule files
templates/<template-name>/ # 3 starter templates
snippets/<snippet-name>.py # 27 standalone Python snippets
- examples/<name>/ # 62 runnable smoke-gated examples (+ gallery.json)
+ examples/<name>/ # 64 runnable smoke-gated examples (+ gallery.json)
examples/gallery_framing.py # shared Layer 1 framing measurement (render path only)
showcase/<name>/ # budget-conformance props (sibling of examples/)
showcase/gallery.json # this tree's gallery index; merged into docs/gallery/
scripts/build_gallery.py # generates docs/gallery/ (stdlib only)
scripts/site/ # vendored landing-page build (build_site.py + template)
docs/gallery/ # committed generated gallery pages + hero assets
docs/new-example-prompt.md # canonical example-creation prompt
.github/workflows/ # validate, blender-smoke, drift-check, release, pages, label-sync, stale
.github/dependabot.yml
AGENTS.md, CLAUDE.md, README.md, ROADMAP.md, CHANGELOG.md
CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md
VERSION # source of truth for the repo version
- LICENSE # CC-BY-NC-ND-4.0
+ LICENSE # CC-BY-NC-ND-4.0 (snippets/ and templates/ carry their own MIT LICENSE)
```
## Branching and commit model
- Single `main` branch. No develop or release branches. Work on a focused
feature branch off an up-to-date `main`. Never force-push or bypass hooks.
- Conventional commits drive the auto-release workflow. It scans the commit subjects since
the last tag and releases only when at least one is release-worthy:
- `feat:` triggers a minor bump
- `fix:` triggers a patch bump
- `feat!:` / `fix!:` / `BREAKING CHANGE` triggers a major bump
- Other types (`chore:`, `docs:`, `ci:`, `refactor:`, etc.) do **not** cut a release: the
workflow runs, decides there is nothing to release, and exits without a tag or version
bump. A mixed push still releases if any commit in range is a `feat:`/`fix:`.
- `[skip ci]` in the head commit still bypasses the workflow entirely. With the commit-type
gate above it is now an optional override, not a requirement for non-release commits.
- Commit messages should describe the why, not the what, and carry a DCO
`Signed-off-by:` trailer matching the commit author (see CONTRIBUTING.md).
- Stage with explicit paths only — never `git add -A` or `git add .`. The
reason is a repo-specific hazard documented in `CLAUDE.md` § Git staging.
## Merge policy and CI evidence
- **Squash-merge with branch deletion is the standard.** The PR becomes one
commit on `main`; delete the remote feature branch after merge, then
fast-forward local `main`.
- - **Smoke jobs do not re-run on the merge SHA.** `blender-smoke.yml` triggers
- on `pull_request` (plus a weekly schedule and manual dispatch) — there is no
- `push` trigger. The correct post-merge evidence for example changes is:
- both Blender smoke jobs (5.2 LTS and 4.5 LTS) passed on the PR head SHA that
- became the sole squash-merged commit, with the actual binary versions
- confirmed in the job logs.
+ - **Smoke runs on the PR head and again on `main`.** `blender-smoke.yml`
+ triggers on `pull_request`, on `push` to `main` (both with the same
+ `paths-ignore`: `**.md`, `docs/**`, `assets/**`), weekly, and on manual
+ dispatch. The merge evidence for example changes is still both Blender smoke
+ jobs (5.2 LTS and 4.5 LTS) passing on the PR head SHA that became the sole
+ squash-merged commit, with the actual binary versions confirmed in the job
+ logs. The push run is what `release-gate.sh` waits for on a direct push to
+ `main`; for a PR merge the gate reads the PR's own checks.
- **Post-merge, verify green on `main`:** Release (`release.yml`), Validate
(`validate.yml`), Ecosystem drift check (`drift-check.yml`), and Deploy
GitHub Pages (`pages.yml`; paths-filtered, so it does not trigger for every
change).
- **Socket Security checks** ("Socket Security: Project Report" and "Socket
Security: Pull Request Alerts") run on PRs via the Socket GitHub App. No
override policy has been decided: a pending or failing Socket check is
unresolved — wait before merging.
- **Release-owned fields are never hand-edited:** `VERSION`, `CHANGELOG.md`,
the CLAUDE.md `**Version:**` line, the ROADMAP.md `**Current:**` line, and
- the manifest `"version"` in `.cursor-plugin/plugin.json`. Generated gallery
+ the manifest `"version"` in `.cursor-plugin/plugin.json`,
+ `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`. Generated gallery
pages under `docs/gallery/` are regenerated via `scripts/build_gallery.py`,
never hand-edited.
- **Evidence over assertion:** PR bodies must label what was proven by live
run versus established by inspection only.
## Blender version targeting
- Primary: **Blender 5.2 LTS** (current stable). All examples assume 5.2
unless otherwise stated.
- Prior stable: **Blender 5.1**. Skills document 5.1-only contracts where they
still matter; weekly smoke keeps a 5.1 leg.
- Fallback: **Blender 4.5 LTS**. Skills and the extension template note 4.5
compatibility where it matters (slotted actions bridge, property delete,
manifest fields, NodesModifier dict inputs).
When a 4.x and 5.x API genuinely diverge, skills must show both code paths,
not just the 5.x one. The `slotted-actions-animation` skill is the load-bearing
example. Local binary discovery and version-reporting rules are in
`CLAUDE.md` § Blender runtime discovery.
## Skills
Each skill lives at `skills/<skill-name>/SKILL.md`. Frontmatter is YAML:
```yaml
---
name: <kebab-case-skill-name>
- description: <one-line, under 200 chars>
+ description: "<what it covers>. Use when <the situations, API names and error strings that should load it>." # <= 1024 chars, enforced
standards-version: 1.10.0
---
```
`name` must match the directory name. `description` is what the AI sees when
deciding whether to load the skill.
Skills should cite Blender API doc URLs where they reference specific RNA
classes, operators, or modules. Avoid encyclopedic API tours; the goal is the
canonical pattern plus the common AI mistakes.
## Rules
Rules are `.mdc` files in `rules/`. Frontmatter:
```yaml
---
description: <one-line>
alwaysApply: false
globs:
- "**/*.py"
standards-version: 1.10.0
---
```
`alwaysApply: false` plus `globs` is the scoping contract: the rule loads only
when a matching file is in context. Setting `alwaysApply: true` makes `globs`
decorative and applies the rule everywhere, so the documented scope stops
describing behavior. Choose globs that cover every file the rule guards.
Rules encode anti-patterns. Each rule should show the wrong way, the right
way, and a one-paragraph rationale. 30 to 80 lines is the right size.
## CI/CD workflows
- `validate.yml` runs file structure checks plus a `validate-counts` job that
asserts the README aggregate counts (skills, rules, templates, snippets,
examples, and showcase pieces) match filesystem reality. The counts language in `README.md`
is load-bearing: the job greps for it.
- `validate.yml` also runs a `validate-manifest` job that checks
- `.cursor-plugin/plugin.json` against reality: every listed path must exist,
- every skill, rule, snippet, template, example, and showcase piece on disk must be listed,
- and the manifest `version` must equal `VERSION`. The release pipeline owns
+ `.cursor-plugin/plugin.json` against Cursor's plugin schema (no keys outside
+ it; Cursor rejects unknown ones) and against reality: every listed skill
+ and rule exists and every one on disk is listed, `marketplace.json` lists
+ this plugin, and the manifest `version` must equal `VERSION`. The release pipeline owns
the manifest `version` line (see `release.yml` below) — never hand-edit it.
- `blender-smoke.yml` executes every shipped example (check-only, no render)
plus snippet/template smoke tests inside REAL headless Blender, on
5.2 LTS and 4.5 LTS for every PR. 5.1 is weekly cron, the opt-in
`needs-5.1` PR label (`pull_request` types include `labeled`), or
`workflow_dispatch` with a `series` input. Auto-label does not apply
- `needs-5.1`. Contributor-facing notes for the smoke lever, the deliberate
- lack of a `push` trigger, Pages path filters, and the three-role exit-code
+ `needs-5.1`. It also runs on `push` to `main` (same `paths-ignore`), which
+ is what the release gate waits for on a direct push. Contributor-facing
+ notes for the smoke lever, Pages path filters, and the three-role exit-code
convention live in CONTRIBUTING.md. Examples run
through `tests/smoke/run_example.py` (catalog: `tests/smoke/catalog.json`).
SKIP is exit 77 plus a `SMOKE_SKIP:` reason, and only when `--min-version`
is above this Blender; exit 0 with that marker is a vacuous pass and fails.
Post-exit sidecars are opt-in (`--expect-sidecar`). A leg with zero PASSes
is red. A new example is not shipped until it has a catalog row.
- `drift-check.yml` consumes `Developer-Tools-Directory/.github/actions/
- drift-check@v1.15` to enforce ecosystem standards-version markers.
- - `release.yml` auto-bumps the version, tags, force-updates floating tags
- `v0` and `v0.1`, and runs `release-doc-sync@v1` to rewrite CHANGELOG.md,
- CLAUDE.md `**Version:**`, and ROADMAP.md `**Current:**`. It also rewrites
- the `"version"` line in `.cursor-plugin/plugin.json` so the manifest tracks
- each release. Triggered on push to `main` for content-changing paths only.
- - `label-sync.yml` self-heals labels via `gh label create --force` per
- label, then applies them to the PR.
+ drift-check`, pinned by SHA (the pin's comment names the tag), to enforce
+ ecosystem standards-version markers.
+ - `release.yml` runs on every push to `main` (no path filter). It releases
+ only when a commit subject in range is `feat:`/`fix:`/breaking, and only
+ after `.github/scripts/release-gate.sh` sees green CI for the SHA. It then
+ bumps the version, tags `vX.Y.Z`, force-updates the floating tags
+ `v<major>` and `v<major>.<minor>` (currently `v0` and `v0.143`), runs
+ `release-doc-sync` to rewrite CHANGELOG.md, CLAUDE.md `**Version:**`, and
+ ROADMAP.md `**Current:**`, rewrites the `"version"` in
+ `.cursor-plugin/plugin.json`, `.claude-plugin/plugin.json` and
+ `.claude-plugin/marketplace.json`, and force-pushes the slim plugin build
+ to the `plugin-dist` branch.
+ - `label-sync.yml` creates any missing label and applies path-based labels
+ to same-repo PRs.
- `pages.yml` builds the landing page from the **locally vendored** template
at `scripts/site/` (originally scaffolded from Developer-Tools-Directory's
site-template, now owned by this repo — the fleet template only scaffolds
new tools) plus the examples gallery via `scripts/build_gallery.py`, then
deploys `docs/`. `docs/index.html`, `docs/fonts/`, and `docs/assets/` are
build outputs and gitignored; `docs/gallery/` is committed.
## Where to look for canonical references
- Blender 5.2 LTS Python API: https://docs.blender.org/api/current/
- Blender 5.1 Python API: https://docs.blender.org/api/5.1/
- Blender 4.5 LTS Python API: https://docs.blender.org/api/4.5/
- Extensions Platform reference: https://docs.blender.org/manual/en/latest/advanced/extensions/index.html
- Release notes (`developer.blender.org`): https://developer.blender.org/
- Live MCP session vs headless harness policy: `CLAUDE.md` § "Live MCP vs Headless Harness" — headless is the only source of truth for evidence.
When information conflicts, prefer the docs over Stack Overflow or older
add-on source. The 2.x to 4.x to 5.x churn around Actions, Extensions, and
property handling has invalidated a lot of community content.
## License
- CC-BY-NC-ND-4.0. See `LICENSE`.
+ CC-BY-NC-ND-4.0 (see `LICENSE`), except `snippets/` and `templates/`, which are MIT (see the `LICENSE` file in each directory) so the documented copy-and-adapt use is lawful.