`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>
35 payloads move to tooling/scripts/blender/ and stay outside package scope.
They run under Blender's bundled Python, which cannot see the repo venv, so
they physically cannot import tooling.core — holding them to the D-263 contract
would either fail the gate forever or force the contract to be weakened for
everyone, and the second is how a gate stops meaning anything.
Count verified by import rather than filename: 33 import bpy/bmesh directly,
and the two that do not are still payloads per their own usage lines.
garment-fit/make_logo.py is the one genuine non-payload and stays for T-1290.
The bash wrapper is retired rather than kept. Keeping it would have put the
install-resolution logic in two places, which is the duplication T-1286 had
just finished collapsing three copies of. domains/blender/service.py owns the
decisions — resolve_blender (native beats flatpak, ordering preserved),
resolve_payload, absolutise — and only run_payload performs. test_blender.py
pins all of them without launching Blender, which matters here more than
usual: the thing being launched is a 200 MB GUI application that writes GLBs.
`reach blender run` takes a registered payload name OR a path to any script,
because the wrapper served both — the spikes and the glb-gen skill hand it
one-off scripts of their own. An unknown name enumerates all 35 and exits 2.
The exclusion now defends itself. check_carve_out_stays_carved fails if
`scripts` is added to PACKAGE_ROOTS, if the payload directory empties (an empty
exclusion proves nothing), or if an __init__.py appears there (which would make
the payloads importable — the coupling the carve-out exists to prevent). All
three arms mutation-proved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User review finding: sneakers/formal shoes/boots conformed to individual
toes (and toes poked the closed front). Root cause: the skin-conforming
clearance clamp ran AFTER toe smoothing and re-imprinted the original toe
bumps; boots also copied per-toe skin weights (ripple under flex). Fix:
shared base.convex_toe_box() — per-slice enclosing ellipse from the skin,
notch fill, projection onto the smooth cap (outside skin by construction),
extended rounded nose past the longest toe, uniform feathered ball-bone
binding so shoes flex rigidly at the ball joint. Style-parameterized
(sneakers roomy / formal sleek tapered / boots chunky). Re-authored x 11
bodies; QA all-green (worst 82px « 150 gate; residuals are collar/sole-edge
slivers, not toes). Lookbook shots re-rendered on the desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Twelve garments, per-body on all 11 bodies, chromakey-gated (worst clips:
parka 3px, boots 54, sweater/tank 83, sneakers 100 final-geometry, slides
123, swim trunks 132, cargo 138 — all under the 150px gate), previewed:
tank top, sweater (crew-neck via per-body ring-valley probe — first draft
read mock-neck, fixed by measured rim circularization), track jacket
(recolorable sleeve-stripe region), joggers (side-stripe region), cargo
pants, swim trunks, one-piece swimsuit, sneakers (prism-sole), formal
shoes, ankle boots (calf shaft), slides (open strap + sole), and the
hip-length thrds parka — the first canon-branded garment (Braemar
cold-weather cooperative), quilted, logo-capable.
Tops now hem into real hip geometry (the natural seg_torso bottom is a
9-14cm tooth ring — the sweater established the hem-into-hips practice).
Manifest merged by the lead: 24 clothing entries with region/default-tint
metadata. Full modern catalogue: 21 garments across tops/bottoms/feet/
full-body x casual/formal/sport/swim/outerwear.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Engine: tooling/garment-fit/blender_batch_fit_skinned.py (G1 — the skinned
Surface-Deform batch the old script couldn't produce; self-check green),
blender_author_offset_shell.py (route c: garment shells from OUR body
segments, weights inherited by construction, bone-plane cuts, procedural
RGBA region mask, UV2 chest channel), make_logo.py. Shader:
toon_garment.gdshader — channel-blended 4-region tint + UV2 logo composited
after tint / before toon shading. Proof: tshirt_modern fitted to the six
healthy bodies, manifest entry with style:modern + logo_capable, thrds
wordmark, 18-assertion test suite, 216-capture chromakey QA.
Key finding (Q-060 evidence): single-reference SD-fit of an offset-shell
degrades on girth-divergent bodies (muscular_m worst) — 24mm standoff
tripled headroom but the mechanism limits. Route guidance recorded on
T-1089: per-body shell authoring for offset-shell garments; SD-fit for
derived/hand-authored ones.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>