Skip to content

Commit db3ecd2

Browse files
committed
feat: add asset-quality gate for gallery examples
'Designed, not placeholder' becomes a measured gate: three floors in examples/gallery_asset_quality.py (naming, material variation scoped to assembled heroes, right-angle edge fraction <= 0.75), a pinned reference bar (CLAUDE.md canonical membership: collision-hull-proxy, custom-normals-shade, vertex-weight-limit, lod-decimate-chain), and the asset sheet (hero alone, neutral 3/4 studio, composited beside the reference set, committed under docs/gallery/asset-sheets/). Floors calibrated empirically against the gallery's best and weakest; three candidates dropped with evidence: part count (vertex-weight-limit's skinned mech arm is one mesh and reference-quality), silhouette compactness (soccer-ball-goldberg 10.0, bmesh-gear 18.3 are good simple subjects), dominant-material share (jerry can and rocket carry accents in per-face assignment, both measure 1.0). Floor falsification: raw cube and single-material assembly exit 11, designed restore exits 0. Goodhart warning written into VISUAL-STYLE: greebles for the metric's sake do not count. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
1 parent b7080aa commit db3ecd2

3 files changed

Lines changed: 393 additions & 0 deletions

File tree

‎CLAUDE.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ Stage with **explicit paths only** — never `git add -A` or `git add .`. Cursor
124124
- `docs/VISUAL-STYLE.md` is the **binding** render standard; deviations are defects.
125125
- **Framing gate:** Layer 1 framing is measured, not eyeballed — the example's `--output` render path calls the shared helper `examples/gallery_framing.py` (`check_framing`, exit 10 on violation) before writing the still: hero fill 0.70–0.90 of frame in at least one axis, every element that matters clearing all four edges by ≥ 0.02. The check-only path never invokes it, so smoke runtimes are unaffected. Documented framing deviations (VISUAL-STYLE Layer 1) call `check_framing(..., deviation="reason")` — the helper reports instead of enforcing, and the reason string is required.
126126
- **Contact-sheet gate:** composite the candidate hero beside the pinned calibration set — currently `armature-bend`, `damped-track-aim`, `bmesh-gear` — commit the composite under `docs/gallery/contact-sheets/`, link it in the PR body, and report per-criterion verdicts (stage darkness, wedge warmth, subject fill, saturation, thumbnail legibility) including mean luminance versus the calibration images. A claim without the committed composite is not acceptable evidence. **This list is the canonical home of the pinned set** — update it here when a new example outclasses a member; `docs/new-example-prompt.md` points here rather than naming members. The longer "calibration references" list in `docs/VISUAL-STYLE.md` is a style reference, not this contact-sheet set.
127+
- **Asset-sheet gate (asset-type examples — game props/kits):** composite the hero asset rendered alone (neutral three-quarter view, plain studio lighting, no staging tricks, no labels, no comparison props) beside the pinned asset-quality reference set — currently `collision-hull-proxy`, `custom-normals-shade`, `vertex-weight-limit`, `lod-decimate-chain` — rendered the same way; commit under `docs/gallery/asset-sheets/`, link it in the PR body, and report a verdict. The asset ships only if it is not identifiable as the least-designed object in that lineup — a strong scene can carry a weak model; this gate removes the scene. **This list is the canonical home of the reference set** — update it here when a new asset outclasses a member. The measurable floors behind the gate (naming, material variation, edge treatment) live in `examples/gallery_asset_quality.py` — render path only, same call pattern as `gallery_framing`, exit 11 on violation — with the calibration table and dropped-floor evidence in `docs/VISUAL-STYLE.md` § Asset quality.
127128
- **Falsification:** every check must be proven to fail once — break the contract, observe the non-zero exit, restore — with the probe and the measured error reported in the PR body. An assertion that cannot fail witnesses nothing.
128129
- **After gallery regeneration** (`python scripts/build_gallery.py`), read the **generated HTML** character by character — the `<img alt>` text and witnesses callouts in `docs/gallery/index.html` and `docs/gallery/<name>/index.html` — not just `examples/gallery.json`. Precedent: the `teaches.split(".")[0]` bug truncated 14/21 card alts at dotted API paths like `bmesh.ops` while the source JSON looked fine (fixed in PR #68).
129130
- **Playwright gallery captures:** gallery `<img>` tags lazy-load, and Chromium scroll-anchoring defeats `scrollTo(0, 0)` after images load — take full-page captures and crop locally, not scroll-then-shoot.

‎docs/VISUAL-STYLE.md‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,85 @@ the default stage.
103103
(`vse-cut-list` on a monitor, `image-pixels-testcard` on a TV) rather than
104104
recreating them.
105105

106+
## Asset quality (measured gate)
107+
108+
"Modeled with intent" is measured, not a vibe. Three parts: measurable
109+
floors, a named reference bar, and the asset sheet. The floors are the
110+
cheap filter that catches the obvious failures; the asset sheet is what
111+
actually decides. Applies to asset-type examples (game props, kits) —
112+
not to world, field, art, or media subjects.
113+
114+
### Floors
115+
116+
Computed by `examples/gallery_asset_quality.py` (render path only, same
117+
call pattern as `gallery_framing`; prints measured values; exit 11 on
118+
violation; never imported by a check-only path):
119+
120+
- **Naming** — no default datablock names on hero parts (Cube, Sphere,
121+
Torus, Suzanne, …). A designed asset names its parts.
122+
- **Material variation** — a hero assembled from ≥ 2 parts carries ≥ 2
123+
distinct materials. A single flat Principled slot across an entire prop
124+
is this gallery's most reliable placeholder predictor. Single-part
125+
subjects are exempt: one honest material is their truth (a brass gear).
126+
- **Edge treatment** — at most **75 %** of a hero's manifold edges may be
127+
right angles (90° ± 2°). Unbroken 90° corners across the board means no
128+
bevel, no chamfer, no shading break. Honest machined teeth pass
129+
(`bmesh-gear` measures 0.667); raw boxes (1.0) fail.
130+
131+
Calibrated empirically against the gallery's own assets, exactly as the
132+
0.02 margin floor was:
133+
134+
| asset | parts | materials | edge90 | compactness (info only) |
135+
| --- | --- | --- | --- | --- |
136+
| collision-hull-proxy (reference) | 16 | 6 | 0.044 | 65.9 |
137+
| custom-normals-shade (reference) | 39 | 2 | 0.288 | 31.0 |
138+
| vertex-weight-limit (reference) | 1 | 4 | 0.150 | 34.8 |
139+
| lod-decimate-chain (reference) | 3 | 4 | 0.019 | 79.1 |
140+
| depsgraph-export (weak) | 2 | 1 | 1.000 | 23.9 |
141+
| text-version-stamp (weak) | 1 | 1 | 1.000 | 405.3 |
142+
| bmesh-gear (simple, honest) | 1 | 1 | 0.667 | 18.3 |
143+
| soccer-ball-goldberg (simple, honest) | 1 | 2 | 0.000 | 10.0 |
144+
145+
Dropped floors, with the evidence that killed them — a floor that cannot
146+
be set without failing a genuinely good asset is not a floor, and a floor
147+
hand-tuned to pass everything is not one either:
148+
149+
- **Part count** — `vertex-weight-limit`'s mech arm is a single skinned
150+
mesh and is reference-quality. Skinned deform meshes are one mesh by
151+
design. The naming half survives.
152+
- **Silhouette compactness** — `soccer-ball-goldberg` scores 10.0,
153+
`bmesh-gear` 18.3, `turntable` 19.1: genuinely good simple subjects
154+
with low-complexity silhouettes. Measured and printed as information.
155+
- **Dominant-material share** — `custom-normals-shade`'s jerry can and
156+
`lod-decimate-chain`'s rocket carry their accents in per-face
157+
assignment, invisible to a per-part share (both measure 1.0 dominant).
158+
Printed as information only.
159+
160+
### The Goodhart warning
161+
162+
These floors are necessary, not sufficient. Bolting meaningless greebles
163+
onto a box to raise silhouette complexity satisfies every number above
164+
and still fails. Detail must serve the object's plausibility: fixtures
165+
where fixtures belong, wear where contact happens, structure where
166+
structure carries load. The asset sheet exists because metrics can be
167+
gamed; a lineup of peers cannot.
168+
169+
### Reference bar
170+
171+
The asset-quality reference set is pinned in `CLAUDE.md` § Quality Gates
172+
for Example Runs — canonical membership lives there, not here.
173+
174+
### Asset sheet gate
175+
176+
For every new asset-type example: render the hero alone — neutral
177+
three-quarter view, plain studio lighting, no staging tricks, no labels,
178+
no comparison props — composite it beside the reference assets rendered
179+
the same way, commit under `docs/gallery/asset-sheets/`, and link it in
180+
the PR body with a verdict. The asset ships only if it is not
181+
identifiable as the least-designed object in that lineup. Isolating the
182+
asset from its staging is the point: a strong scene can carry a weak
183+
model, and this gate removes the scene.
184+
106185
## Output
107186

108187
- Render 1280×720 PNG (`taa_render_samples`/`cycles.samples` 32–64).

0 commit comments

Comments
 (0)