Mandatory review gate for any mesh or asset created or edited in Blender. Detects z-fighting (coplanar overlaps), floating/detached parts, inverted or inconsistent normals, non-manifold/open geometry, degenerate faces, duplicate verts, n-gons, unapplied scale, UV problems, bad skin weights, and renders multi-angle review sheets. Use before calling any modeling, export or delivery task done.
Installs into .claude/skills of the current project.
Are you the author of Blender Asset Qa?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/el4cteo-blender-asset-qa)
---
name: blender-asset-qa
description: Mandatory review gate for any mesh or asset created or edited in Blender. Detects z-fighting (coplanar overlaps), floating/detached parts, inverted or inconsistent normals, non-manifold/open geometry, degenerate faces, duplicate verts, n-gons, unapplied scale, UV problems, bad skin weights, and renders multi-angle review sheets. Use before calling any modeling, export or delivery task done.
license: MIT
metadata:
author: blender-agent-skills
version: "0.1.0"
---
# Asset QA (review gate)
Never assume a generated or edited mesh is correct. Check it with numbers, then look at it.
## Procedure
1. **Automated check**, passing one asset's objects together:
`scripts/qa_check.py -- --objects Body Head Hat --profile game`
Profiles: `game` (default), `render` (lenient), `print` (watertight required), `roblox` (20k tris/mesh, watertight, no n-gons, ≤4 influences).
2. **Fix every `errors` entry** (table below), then re-run until `"pass": true`. Each warning must be either fixed or explicitly accepted, with the reason reported to the user.
3. **Visual check, always**:
- Live: MCP `look` with `mode="angles"` on the targets, once in `material`/`solid` shading and once in `wireframe`. Use `xray` for internals.
- Headless: `scripts/review_shots.py -- --objects ... --mode random` (one color per object, which makes detached pieces obvious), then `--mode wire` for topology. Open the returned PNG.
4. Report: tris per object, issues fixed, and remaining accepted warnings.
## What each finding means → fix
| Finding | Cause | Fix |
|---|---|---|
| `zfighting` A<->B | two surfaces facing the same way within `--eps` (1 mm) that overlap | delete the hidden duplicate, or offset ≥ 2–5 mm along the normal, or boolean/merge them |
| `back_to_back` | faces touching with opposite normals (boxes glued face-to-face) | delete the hidden faces (wasted tris, bake artifacts) |
| `floating` | island/object not touching any other part within `--tol` | move it to make contact (snap), bridge it, or join and merge. If floating is intended (sparks, UI), check it separately |
| `enclosed` | part fully inside another, not touching (eyes, internal mechanics) | usually fine. Confirm it's intended |
| `inside_out_islands` | closed part with normals pointing in | `mesh.normals_make_consistent(inside=False)` on that part |
| `inconsistent_normals_edges` | neighbouring faces wound in opposite directions | recalc normals; if it persists, look for non-manifold seams |
| `nonmanifold_edges` / `wire_edges` | >2 faces per edge, or stray edges | dissolve/delete the extra faces; `delete_loose` |
| `boundary_edges` | open holes (bad for print/Roblox/bakes) | fill (`fill_holes`/`edge_face_add`) or cap |
| `degenerate_faces` / `zero_length_edges` | collapsed geometry | `dissolve_degenerate(threshold=1e-4)` |
| `duplicate_verts` | unwelded seams | `remove_doubles(threshold=1e-4)` (Merge by Distance); check UV seams after |
| `ngons` | faces with >4 sides | triangulate/quadrangulate locally; n-gons are fine only on flat hard-surface faces that never deform |
| unapplied/negative scale | object transform | `transform_apply(rotation=True, scale=True)`; negative scale flips normals on export |
| `uv_maps: 0` / zero-area UV / overlap | missing or bad unwrap | `blender-retopology-uv` |
| unweighted / >4 influences | skinning | `blender-character-rigging` (`weights_check.py`) |
| `ground_z` ≠ 0 | asset floats or sinks | move the origin to the bottom and set z = 0 (props) |
## Manual checks the script can't fully judge
- **Silhouette and proportions** at the target camera distance. Compare with references and real-world size (`ob.dimensions`).
- **Shading artifacts**: smoothing on hard edges (needs sharp edges/weighted normals), and pinching at poles on curved surfaces.
- **Intersections that look wrong** (a leg clipping a seat), even though they're "attached".
- **Interior/hidden geometry** that wastes tris (use `xray`).
- **Deformation**: pose the rig at extremes (elbow 140°, knee 150°, shoulder raised) and look for collapsing volume.
- **Material assignment**: every face has the intended material, with no default grey slots.
## Tuning
- `--eps` is the z-fight distance. Raise it to 0.005 for large environment scenes, and lower it to 0.0002 for tiny jewelry.
- `--tol` is the contact tolerance for detached parts. Raise it for chunky kitbash.
- `--no-modifiers` checks the base mesh. By default the evaluated (modifier) result is checked, which is what gets exported.
- Above `--max-tris` (300k), the z-fight and floating checks are skipped. Check high-poly sources per part instead.