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>
This commit is contained in:
2026-09-23 19:55:46 +02:00
co-authored by Claude Opus 5.5
parent ddce4441a9
commit 26cc8de7f3
72 changed files with 609 additions and 188 deletions
+2 -2
View File
@@ -92,13 +92,13 @@ invented.
| `ledger` | the economics pipeline, named for the UI component that will aggregate it | ✅ ported (T-1289). `economy-db/` → `domains/ledger/` (`economy_import/` kept by name; the entrypoint became `service.py`), `schema_version.py` with it. `reach ledger import`; `make regen-db` survives as a one-line delegate, `make economy-db` retired. `generated_brands.toml` byte-identical across the move |
| `wiki` | wiki sync and content maintenance | ✅ ported (T-1290): `reach wiki stats`, `reach wiki gttr-hook` (both output-identical to the originals). `wiki_sync.py` moved whole, but its renderer and importer are NOT verbs — re-rendering deletes ~10,700 lines of committed pages (T-1292). The seven one-shots (`assign-astro-ids`, `migrate-s-to-gj`, `patch-core-sector`, `fill-missing-globes`, `generate-stubs`/`find-stubs`, `backfill_cultural_corridor`) and the destructive `process-wiki-system-changes` went to `archive/wiki-bootstrap/` |
| `assets` | connectors to the tower-of-joy generators | ✅ ported (T-1290): `reach assets {audio,image,trellis} …` + `synth-ui`. The four audio bash wrappers and `trellis-batch.sh` retired; connectors call each other instead of spawning each other; `tooling/db/` is gone. Synth WAVs byte-identical, ffmpeg pipeline decode-identical; network paths pinned by `test_assets.py` against a fake Gradio. Unreachable reads as OFF (VRAM, D-17), never "restart it" |
| `character` | bodies, garments, GLB handling | `garment-fit/make_logo.py`, `garment-qa/analyze_captures.py`, `convert_outfit.py`, `glb_strip_utility_nodes.py`, `inspect_glb.py`, `check_hair_symmetry.py`, `check_icosphere.py`, `render_quaternius_test.py`, `setup_clothing_metadata.py` — **note this is far smaller than `garment-fit/`'s file count suggests; 22 of its 23 files are Blender payloads and belong to the carve-out** |
| `character` | bodies, garments, GLB handling | ✅ ported (T-1290): `reach character {logo,strip-glb,qa,qa-analyze}` — logo PNG, stripped GLB and QA report byte-identical; `run-garment-qa` (bash) rewritten with its decisions pinned by `test_character.py`. **Map correction:** six of the ten files assigned here (`convert_outfit`, `inspect_glb`, `check_hair_symmetry`, `check_icosphere`, `render_quaternius_test`, `test_quaternius_raw`) import `bpy` — they joined the carve-out as `blender_*` payloads. `setup_clothing_metadata` and `wipe-bodies.sh` archived; `segment_reference_distribution.md` moved to `docs/assets/visual/` |
| `visual` | screenshot and render comparison | `visual-diff`, `visual-thumbnail`, `visual-blank-check` |
| `godot` | Godot parse and cold-start checks | ✅ ported (T-1283); the two bash originals were left beside the port and retired in T-1290 |
| `generate` | content generators not owned elsewhere | ✅ ported (T-1286). `generate-brands` + `generate-corporations` collapsed into `core.process.cargo_binary` — they were the same 24 lines of bash a third time |
| `dev` | developer environment and workflow | ✅ ported (T-1286). The three environment scripts split decision from performing — `godot_plan`/`worktree_plan` are pure and pinned by `test_environment.py` |
| `pr` | the PR/review loop | ✅ ported (T-1286). `watchlist-diff` now reads the watched set from `generator_sources.py` instead of restating it |
| `blender` | **carve-out** — payloads run by Blender | ✅ done (T-1273). 35 payloads in `tooling/scripts/blender/`, excluded from package scope and from importability; the bash wrapper's install-resolution and path-absolutising became `domains/blender/service.py`, pinned by `test_blender.py` |
| `blender` | **carve-out** — payloads run by Blender | ✅ done (T-1273). 35 payloads in `tooling/scripts/blender/` (41 after T-1290 found six more among the `character` files), excluded from package scope and from importability; the bash wrapper's install-resolution and path-absolutising became `domains/blender/service.py`, pinned by `test_blender.py` |
## Judgment calls, with reasons