---
name: visual-quality-gate
description: "An art-quality gate before delivery: pin down a Design Spec (≤3 colours / ≤2 typefaces / 8pt grid) → anchor a reference → pixel-level machine checks (dimensions, dominant colour count, whitespace, edge crowding, contrast, highlights / shadows / skin tone / grain) → list the '3 ugliest spots' and fix until they pass. Use when the user says 'this cover / interface looks bad', 'improve the look', 'the layout is messy', 'the colours are ugly', 'not premium enough', 'check it before delivery', or whenever any image, cover, card image, photograph, AI-generated image, PPT, docx or page needs an aesthetic acceptance check before delivery; also read references/style-cards.md first when you need to set a visual style direction for a piece of content. Not for the topic and the copy themselves."
---

> Original skill · commissioned by the user · English edition, 2026-11-12

# Visual Quality Gate

**One sentence: aesthetics are constrained into existence, not thought into existence. Pin the rules down first, find the reference first, and accept with pixels and a checklist at the end.**

## When to run

- Any bitmap / cover / card image / PPT / document / page, at the second "before delivery".
- When the user says 'ugly / not premium / messy / no design sense': run the checks before changing anything, do not guess.
- Together with the platform asset skills: wechat-material, xiaohongshu-material, koubo-video, office are the production side; this skill is the release side.

## Six steps (the order cannot be skipped)

| Step | Action | Criterion |
|---|---|---|
| S1 fix the medium | aspect ratio, platform, final pixel dimensions | pixel-for-pixel identical to the platform spec table |
| S2 write the Spec | colours ≤3 + neutrals, typefaces ≤2, spacing on an 8pt grid, ≤5 type sizes, 1 focal point | written down, using the references/design-spec.md template; to set a visual direction read references/style-cards.md first |
| S3 anchor a reference | pick one source per references/sources.md and write "which single thing you learn from it" | you can name one specific thing (whitespace / contrast / restraint), not "it feels premium"; mixing styles across libraries fails outright |
| S4 produce | follow the Spec strictly | no fourth colour may be added mid-way |
| S5 machine check | run scripts/vqg_check.py (layout) + `--tokens-src` (source); for photographs / AI-generated images also run scripts/style_metrics.py (highlights / shadows / skin tone / grain) | FAIL must be fixed, WARN must have a written reason |
| S5.5 aesthetic score | run scripts/aesthetic_score.py to get the 5 machine axes → add the 5 judgement axes with anchors per references/aesthetic-rubric.md (every axis with evidence) → `--judge` combines them and writes the ledger | a missing judgement axis = not done (exit code 2); any axis <2 or a total <65 = not released |
| S6 defect list | after screenshotting, write "the 3 ugliest spots" → fix → re-run | re-running is mandatory; one round of fixes is not enough |

## Hard gates (veto)

- dimensions do not match the spec table
- colours >3 (black, white and grey do not count)
- typefaces >2 families
- content touches the edge (the edge band shows content)
- body copy contrast <4.5:1
- **bare font sizes / bare colour values appear in the renderer** (size, colour and spacing must come from design tokens)
- no check run, no defect list → **it is not a delivery**

## Check commands

```powershell
$py = (load_workspace_dependencies).python   # or the local python
& $py scripts\vqg_check.py image.png --size 900x383 --max-colors 6 --min-whitespace 20
# add the source token check (mandatory once the renderer is tokenised):
& $py scripts\vqg_check.py image.png --tokens-src ..\tools\wechat\cover.py
# validate the design tokens themselves: does spacing land on the 4pt grid
& $py scripts\vqg_check.py image.png --tokens-file ..\tools\wechat\design_tokens.json
# full-bleed photo / full-gradient layouts: declare bleed, and edge crowding drops from FAIL to WARN (still check by eye whether text touches the edge)
& $py scripts\vqg_check.py image.png --size 900x383 --allow-bleed
& $py scripts\vqg_check.py image.png --allow-bleed top bottom
# multiple files: just list them side by side; add --json for machine results
```

For photographs / AI-generated images / graded output, additionally run the metric probes (criteria in references/image-metrics.md):

```powershell
# clipped highlights, crushed shadows, saturation (single image only)
& $py scripts\style_metrics.py image.jpg
# grain + skin tone compared with the reference (--style picks the thresholds: film/cyber/vhs/anime/clay/minimal/newcn)
& $py scripts\style_metrics.py image.jpg --ref reference.jpg --grain --style film
```

`style_metrics.py` exit codes: 0 all PASS / 1 some WARN / 2 some FAIL. It only covers "are texture and colour out of control"; layout (dimensions, colour count, whitespace, edge crowding, contrast) still belongs to `vqg_check.py`.

**Aesthetic scoring (S5.5, judging "does it look good" rather than "is it compliant")**:

```powershell
[Console]::OutputEncoding=[Text.Encoding]::UTF8   # without this, non-ASCII output is garbled
# 1) machine axes (focus / tonal range / subject-ground contrast / colour relationships / restraint in detail), exit code 2 = judgement axes still to fill
& $py scripts\aesthetic_score.py image.png --use cover
# 2) emit the fill-in card, then fill the 5 judgement axes with the anchors in references/aesthetic-rubric.md (score + evidence of ≥6 characters)
& $py scripts\aesthetic_score.py image.png --use cover --emit-judge judge.json
# 3) combine and write to the ledger (within one batch, sort by label and compare totals)
& $py scripts\aesthetic_score.py image.png --use cover --judge judge.json --ledger ..\..\art-ledger\scores.jsonl --label cover-lead
```

`--use` has four weight profiles: `cover` / `poster` / `gallery` / `ui` (the same image genuinely deserves a different score for a different use).
**Machine axes must not be hand-edited**; a judgement axis with a score but no evidence is invalid, and the script treats it as not done (exit code 2).

Items checked: dimensions / dominant colour count / approximate whitespace ratio / crowding on all four edges / contrast of specified colour pairs; `--tokens-src` additionally catches bare font sizes `font(FONT_x, N)`, bare `fill=(r,g,b)` and bare hex colours (`(0,0,0)` masks and inline `token-ok` comments are exempt); `--tokens-file` validates whether spacing fields align to the 4pt grid.

> There is precedent for tokenising an existing renderer: the four renderers in `tools/wechat/` plus `design_tokens.json` (v4: a constrained type scale + 4pt spacing grid, pixel regression over 11 fixtures). The method is to extract the existing numbers into JSON as-is, run `tests/regress.py` to confirm zero visual change, and only then tighten the scale and the grid.

## Platform spec quick reference

| Platform | Asset | Size |
|---|---|---|
| WeChat Official Account | lead cover | 900×383 |
| WeChat Official Account | secondary cover | 500×500 |
| WeChat Official Account | body image / card image | 1080×1440 (card images exported at 2x) |
| Xiaohongshu | image carousel | 1080×1440 |
| Douyin | final cut / cover | 1080×1920 |

## Delivery report (written into 00-delivery-notes.md)

```
Art gate: PASS (dimensions / colour count / whitespace / edge crowding / contrast all pass)
Texture metrics: PASS (highlights 0.4% / shadows 1.1% / skin-tone shift 6° / grain 0.41%)
Aesthetic score: 89.7/100 PASS (weakest axis A2 tonal range 3.0; axis vetoes: none)
Fixed: card image 03 text touching the edge → inset 48px; dominant colours 4 → 3
```

## References

- `references/design-spec.md` — Spec template + colour / type / spacing values
- `references/checklist.md` — the complete checklist per medium (images / interfaces / PPT / documents / video frames)
- `references/style-cards.md` — **choosing a direction**: 10 standing styles (parameters / suited subject matter / where not to use it / naming red lines), read before S2
- `references/image-metrics.md` — **quantified acceptance**: the six hard metrics for skin tone / highlights / shadows / grain / whitespace, with the source of each threshold
- `references/aesthetic-rubric.md` — **aesthetic scoring**: 10-axis anchored scoring (5 machine axes + 5 judgement axes) and the jsonl ledger
- `scripts/style_metrics.py` — the metric probes above (Pillow, no external dependencies)
- `scripts/aesthetic_score.py` — the 10-axis scorecard machine axes + ledger writes
- `references/sources.md` — the S3 sourcing list and sourcing discipline
