Files
settled-reach/tooling/domains/character/GARMENT_QA.md
T
jpmschweitzerandClaude Opus 5.5 26cc8de7f3 refactor(tooling): T-1290 — the character domain, and six payloads the map misfiled
`reach character {logo, strip-glb, qa, qa-analyze}` replaces make_logo.py,
glb_strip_utility_nodes.py, analyze_captures.py and the run-garment-qa bash
driver. The QA configs and method doc move beside the domain (qa_configs/,
GARMENT_QA.md), and `qa` takes a config name (`reach character qa hoodie_modern`)
or a path.

Parity, from baselines taken before anything moved:

- the logo PNG is byte-identical
- a synthetic GLB with three real utility nodes strips to identical bytes
  (the committed bodies strip 0 nodes, so they proved nothing)
- re-analyzing a cached capture set gives a byte-identical report.json and
  summary

run-garment-qa is rewritten, not wrapped (D-263). Its decisions — which config,
which Godot ($GODOT, then ~/bin/godot4, then PATH), and whether xvfb-run is
needed — are capture_plan(), pinned by tooling/test_character.py without
launching Godot. The bash exit codes are kept: 2 for a missing config, 3 for
no Godot.

The T-1271 domain map was wrong about this domain. Six of its ten files import
bpy: convert_outfit, inspect_glb, check_hair_symmetry, check_icosphere,
render_quaternius_test and test_quaternius_raw. They are Blender payloads and
joined the carve-out as blender_* (41 payloads now). The 22 existing payloads'
docstrings still cited tooling/garment-fit/ from before T-1273; fixed.

Archived, with reasons in tooling/archive/README.md:

- setup_clothing_metadata.py wrote coverage data for five garments that no
  longer exist in the 24-garment wardrobe
- wipe-bodies.sh ran raw DELETEs on systems.db

segment_reference_distribution.md moved to docs/assets/visual/.

Behaviour changes:

- The QA analyzer exited 0 whatever it found, though its own README says
  clip-through "is the real defect and it gates". qa and qa-analyze now exit 1
  on clip-through, and the remedy names --min-pixels (Wave 1/2 were accepted
  at 150). The cached peasant set has 33 failures at the default 8 px.
- glb strip re-reported the same nodes as stripped on every re-run and
  rewrote an unchanged file: it left them as orphans and then found them
  again. Only nodes still linked into the graph count now, and a first pass
  writes the same bytes as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:55:46 +02:00

42 lines
2.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# garment-qa — chromakey garment-clipping QA (T-1089)
Automatically detects clothing clip-through with a **two-pass depth-proximity**
chromakey. Per view (clips × sampled frames × 4 camera yaws), the capture scene renders
the SAME frozen animation frame twice: **pass A** paints the body segments a garment
*claims to cover* (coverage.json `hides`) flat **unshaded magenta**, garment + head/hands
normal; **pass B** is identical except the garment is flat **cyan** and every garment
vertex is nudged `clip_epsilon_m` (default 3 cm) **toward the camera**. A body-key pixel
that is magenta in A but cyan in B means the ε-shifted cloth now covers it — the body sat
within ε *in front of* the cloth = poking through. This separates a true clip from a limb
merely crossing in front of the torso, an open collar, or a bare arm over background
(all of which stay magenta because the cloth behind them is > ε away).
**Run:** `reach character qa [config]` — a name from `qa_configs/` (`peasant`, the
default, `hoodie_modern`, …) or a path. It launches Godot (`$GODOT`, else `~/bin/godot4`,
`opengl3`, `xvfb-run` when headless) on the capture scene, then runs the analyzer.
Re-analyze existing captures without re-rendering: `reach character qa-analyze --config …`
(or `--dir`). **Both exit 1 when any capture clips through** — the gate the text below
describes; before T-1290 the analyzer exited 0 whatever it found.
**Config** (path passed to the scene via `GARMENT_QA_CONFIG`): `garments`
(`{item_id,slot}` catalogue items, or `{glb,covers[]}` for a raw WIP garment),
`body_types`, `clips`, `frames_per_clip`, `yaws`, `head_id/hair_id/eyebrow_id/skin_tone`,
`out_dir`.
**Read the report:** `<out_dir>/report.json` — the same summary prints to stdout. Two
metrics per capture:
- **`clip_through_pixels`** / `largest_clip_component` — key pixels the ε-shifted cloth
covers (skin ≤ ε in front of cloth). **This is the real defect and it gates:** a capture
`fail`s when its largest connected clip blob ≥ `--min-pixels` (default 8, tolerating AA
edges). `clip_through_failures` + the `by_group` `clipfail`/`worstClip` columns roll it up.
- **`exposed_skin_pixels`** — key pixels the shift did NOT cover (skin well in front of any
cloth: collar/sleeve/ankle gaps, a limb crossing the torso). Informational; does not gate.
Tune sensitivity with `clip_epsilon_m` in the config (smaller = only tighter pokes count)
and `--min-pixels` on the analyzer. Failing/flagged frames are copied to
`<out_dir>/failures/*_HL.png` with **exposed_skin lime**, **clip_through filled red**, and
each clip blob boxed.
**Note:** the Godot scene lives at `client/tools/garment_qa/chromakey_scene.gd` (not here)
because Godot `res://` paths cannot leave the client project root.