3ba86b2ea8
Bulk import of the working lanes that were living untracked on the PC. Content: - characters/ Lena/male body lanes, bakes, texture work, run logs - clothing/ garment pipeline, configs, gates, contract docs - garments/ MD-authored garment sources (.zprj/.zpac) - UAL-Lib/ Universal Animation Library 2 source (.blend/.fbx/.glb) - tools/ blender_bridge, iclone_bridge, md_bridge, tailor, glm_agent - docs/, plans/, dev/, .agents/plans/ Repo hygiene: - .gitattributes: LFS now covers .blend, .zprj, .zpac, .obj, .npy and the Reallusion .iAvatar/.ccAvatar/.ccRestore containers. Without this the ~3.8 GB in this commit would land as raw blobs. .png/.jpg are left out on purpose — ~250 are already tracked raw and converting them would rewrite every one without shrinking history. - .gitignore: exclude /accurig/ (~1 GB AccuRig program files, redistributable from Reallusion, nothing authored here) and /dev/null/ (git-lfs hook copies dropped by a `>/dev/null` redirect on Windows). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
393 lines
24 KiB
Markdown
393 lines
24 KiB
Markdown
# Plan: Clothing-pipeline unification — one entry point, QC gates at every seam
|
||
|
||
**Status:** Implemented 2026-07-31 (phases 1–4 built; catalog JSON still deferred) · **Author:** Fable 5 session 2026-07-31 · **Implementer:** 5-agent parallel build + integration pass, 2026-07-31 — see §8
|
||
|
||
## 0. Context (you have no other context — read this fully)
|
||
|
||
The clothing lane turns a garment idea into an outfit part an ariki-game character wears.
|
||
It has three halves that work but do not talk to each other:
|
||
|
||
1. **MD upstream** (this repo): Marvelous Designer 2024, driven over a TCP bridge
|
||
(`tools/md_bridge.py`, port 18900). Per-garment scripts in `tools/tailor/` draft
|
||
panels, sew, drape on the Lena avatar FBX, texture, and snapshot. Session start is a
|
||
human click (Plugin > TinqsMDBridge); MD's UI freezes while a script runs.
|
||
2. **Blender downstream** (this repo): `clothing/garment_pipeline.py` — seven
|
||
config-driven headless stages (`census prepare fit reduce bake skin export`), each
|
||
checkpointing a `.blend` + QA renders into `work/<name>/`, exporting per-slot GLBs
|
||
onto the shared 65-bone Quaternius skeleton into
|
||
`ariki-game/assets/quaternius/outfits/<set>/`.
|
||
3. **Game side** (`ariki-game`, read-only for this repo's tooling by convention):
|
||
hand-edit `src/Character/OutfitCatalog.cs` to register the item, force a Godot
|
||
reimport, spawn `ClothingTestBed` via `tools/game.sh`, click through Idle/Walk/Dance,
|
||
and eyeball the result.
|
||
|
||
The lane is PROVEN — the downloaded-dress pilot (2026-07-30) and the MD-authored kapa
|
||
haka set (pari + piupiu, 2026-07-31) are in game. But both kapa haka pieces shipped with
|
||
**pose-dependent defects invisible to every QA artifact the pipeline produces**: the pari
|
||
neckline gapes open in walk/dance, and a thigh punches through the piupiu skirt. All
|
||
pipeline QA renders are rest pose — the one pose that cannot fail. A separate agent owns
|
||
those two weight fixes; **this document is the pipeline-level response**: an assessment,
|
||
a design that combines the three halves into one entry point, and the QC gates that
|
||
would have caught both bugs before the game.
|
||
|
||
Source of truth for how the lane works *today*: `.agents/wiki/architecture/clothing-lane.md`
|
||
and `clothing/README.md`. This document is a proposal for how it *should* work; on
|
||
adoption, fold the outcome into `clothing-lane.md`.
|
||
|
||
---
|
||
|
||
## 1. Assessment
|
||
|
||
### 1.1 What already works (keep it)
|
||
|
||
- **Config-driven downstream.** New garment = new JSON in `clothing/configs/`, not new
|
||
code. The schema already expresses island→part mapping, bodyshell parts, alignment,
|
||
fit masks, weld bands, proxies, weight modes, textures, per-part tri budgets.
|
||
- **Checkpointed stages.** `00_census.blend … 50_skin.blend` mean any stage can be
|
||
re-run in isolation; resume semantics exist for free.
|
||
- **Determinism fingerprint.** `export` prints a SHA1 over all garment vertices
|
||
(`garment_pipeline.py:984-989`).
|
||
- **The seam is crossed.** MD-authored garments arrive at game budget, pre-fitted to
|
||
`Ariki_Female_QuatSkin.glb` — `reduce` is only hard for downloaded meshes.
|
||
- **All the automation hooks exist on the game side.** `game.sh spawn` (multi-instance,
|
||
`SPAWN_BUILD=1`, `MOCK_ONLY=1`), agent API (`/health /screenshot /navigate /state
|
||
/scene /ui /command /input /console`), `game.sh clean-import`, `tools/asset_pipeline.py
|
||
check`.
|
||
|
||
### 1.2 What costs time (the streamlining targets)
|
||
|
||
| Cost | Evidence |
|
||
|---|---|
|
||
| 4× duplicated MD garment scripts | `md_pari.py`, `md_piupiu.py`, `md_tee_v1.py`, `md_skirt_v1.py` share a ~100% identical shell (NewProject → ImportFBX at `op.scale=10.0` → fabric → arrange → simulate → snapshot); only panels/seams differ |
|
||
| Config copy-paste | `piupiu.json` vs `piupiu_sb.json`: 48 lines, differing in exactly 4 values (`name`, `body`, `weights`, `export.set`) + a note |
|
||
| Six manual CLI invocations per garment | one `blender --background … --stage <s>` per stage, run by hand in order |
|
||
| Hand-authored configs | read `census.json`, guess island→part mapping, trial-and-error alignment against QA renders |
|
||
| Code-edit registration | 2 `Add()` lines + possibly a `BaseDirFor` case in `OutfitCatalog.cs`, then a C# rebuild |
|
||
| Blunt reimport | `.bin`-only changes are invisible to Godot's `.gltf` hash; today's fix is `clean-import` (wipes ALL of `.godot/imported/`) or hand-deleting entries |
|
||
| Eyeball sign-off | spawn test bed, click Idle/Walk/Dance, look |
|
||
|
||
### 1.3 What is structurally unsafe
|
||
|
||
1. **Rest-pose-only QA.** Every `qa_*.png` the pipeline writes is the fitting pose.
|
||
Both shipped bugs were invisible in it. (`clothing/README.md` "QA renders only prove
|
||
the pose that cannot fail".)
|
||
2. **Silent catalog fallback.** `OutfitCatalog.BaseDirFor` (`OutfitCatalog.cs:88-98`)
|
||
takes a bare-string `setId` and its `_ =>` arm silently resolves to the Fantasy pack
|
||
folder — a typo'd set name mis-resolves without error. Missing assets are warn-only
|
||
(`OutfitCatalog.cs:71-78`): the character just stays naked/previous.
|
||
3. **`.bin` staleness.** `GLTF_SEPARATE` export keeps geometry/UVs in the sidecar
|
||
`.bin`; Godot hashes only the `.gltf`, so a vertex-only re-export silently ships the
|
||
old mesh.
|
||
|
||
### 1.4 Corrections to the record (verified 2026-07-31)
|
||
|
||
Earlier session notes contained four errors that materially change the design:
|
||
|
||
| Prior claim | Truth | Consequence |
|
||
|---|---|---|
|
||
| "FBX export from MD is manual" | `export_api.ExportFBX(path, op)` (+ `ExportOBJ/ExportZPrj/ExportZPac`) is scriptable — exact flags in `.claude/skills/marvelous-designer/SKILL.md:182-190` (`op.bExportGarment=True; op.bExportAvatar=False`); the 4 garment scripts simply never call it | The **entire MD half can run unattended** after the one session click |
|
||
| `ExportSnapshot3D` is the mesh export | It is the viewport **PNG** (the vision-QC loop); mesh export is `ExportFBX` | Don't build on the wrong call |
|
||
| "5 duplicated MD scripts" | 4 garment scripts; `md_recon.py` is an API-introspection tool and stays standalone | Template scope |
|
||
| `tools/vision-compare.py` exists (referenced by `ariki-game/tools/visual-qa.sh`) | **It was never written** | The in-game gate has no judge today; VLM-vs-pixel-diff is a real decision, not wiring |
|
||
|
||
One permanent constraint, confirmed: **MD exposes no mesh introspection to Python**
|
||
(`GetClothPositions()` stays empty). Every upstream drape judgement is image-based,
|
||
by necessity, until CLO exposes more.
|
||
|
||
Two enabling facts: the config's `export` block already carries `gender`/`set`/`out_dir`
|
||
and each part carries `slot`, so the catalog's exact `{Gender}_{Set}_{Slot}.gltf` path is
|
||
derivable from the config today; and the agent API's `GET /console` surfaces
|
||
`ClothingTestBed`'s per-slot load-failure prints, so **outfit load failure is
|
||
machine-detectable without vision**.
|
||
|
||
---
|
||
|
||
## 2. The unified pipeline
|
||
|
||
### 2.1 One config, additive blocks
|
||
|
||
Keep `clothing/configs/<garment>.json` as the single per-garment artifact and **grow it**
|
||
— the existing Blender schema stays byte-compatible, so `garment_pipeline.py` needs no
|
||
migration:
|
||
|
||
```jsonc
|
||
{
|
||
"extends": "piupiu.json", // NEW: overlay inheritance
|
||
"name": "...", "source": "...", "body": "...",
|
||
|
||
"md": { // NEW: upstream draft params (what the 4 scripts differ on)
|
||
"avatar_fbx": "...", "zfab": "...", "texture": "...",
|
||
"panels": [...], "seams": [...], "arrangements": [...],
|
||
"sim_frames": 300, "strengthen": [...],
|
||
"export_basename": "lena_<garment>_v<N>"
|
||
},
|
||
|
||
"align": {...}, "parts": {...}, // UNCHANGED: existing Blender schema
|
||
|
||
"export": { "gender": "Female", "set": "Kapahaka", "out_dir": "..." }, // exists
|
||
|
||
"catalog": { // NEW: what Add() needs that nothing else holds
|
||
"id": "kapahaka_legs_f", "displayName": "Piupiu",
|
||
"charisma": 0.05, "workSpeed": 0.04, "category": "casual"
|
||
},
|
||
|
||
"expect": {...} // NEW: QC thresholds (§3)
|
||
}
|
||
```
|
||
|
||
- **`extends` is the cheapest real win**: `piupiu_sb.json` becomes ~8 lines. Overlay =
|
||
deep-merge child over parent, arrays replaced whole.
|
||
- **`md` block + one template script** `tools/tailor/draft_garment.py` replaces the 4
|
||
copy-paste scripts. The identical shell becomes the template; panels/seams/arrangements
|
||
come from the config. Hard-won MD lessons (mm units, +y up in 2D, whole-edge seam
|
||
indices, look up arrangement points by name, `SetArrangement` then
|
||
`ResetClothArrangement`) live in ONE place instead of four.
|
||
- **`catalog` block** carries only what the C# `Add(id, displayName, slot, slotSuffix,
|
||
charisma, workSpeed, category, set:, gender:)` signature needs and the pipeline has
|
||
never held; `slot`/`set`/`gender` derive from existing blocks.
|
||
|
||
### 2.2 One orchestrator
|
||
|
||
`clothing/garment.py <config> [--from <stage>] [--to <stage>] [--only <stage>]`
|
||
over one ordered stage list spanning all three worlds:
|
||
|
||
```
|
||
draft drape publish │ census prepare fit reduce bake skin export │ register import verify
|
||
└──── MD bridge ──────┘└──────── Blender headless ────────────────┘└──── ariki-game ─────┘
|
||
```
|
||
|
||
**A driver, not a rewrite.** Each stage shells out to the tool that already owns it:
|
||
|
||
| Stage group | Shells to | Notes |
|
||
|---|---|---|
|
||
| `draft/drape/publish` | `python tools/md_bridge.py --file <generated>` | Script generated from the `md` block via `draft_garment.py`. `--ping` first; if no session, fail fast printing the human instruction ("click Plugin > TinqsMDBridge"). Raise the idle timeout (~1 min per 300 sim frames). `publish` = scripted `ExportZPrj` + `ExportFBX` + `ExportZPac` (§1.4). |
|
||
| `census … export` | `blender --background --python garment_pipeline.py -- --config X --stage Y` | **Unchanged CLI**; `--from/--to` is a loop. Standalone stage runs keep working exactly as today. |
|
||
| `register/import/verify` | `bash ariki-game/tools/game.sh …` + agent API | §2.4 and gates G6/G7/G8. Decided: orchestrator lives in `tinqs/animation/clothing/`, cross-repo shell-out to `game.sh` is acceptable. |
|
||
|
||
Checkpoint numbering (`00_ … 50_`) already gives resume: `--from fit` loads
|
||
`10_prepare.blend` exactly as today.
|
||
|
||
### 2.3 The automation boundary (permanent, human-only)
|
||
|
||
1. **Session start is a click** — Plugin > TinqsMDBridge, once per MD session (not per
|
||
garment). The orchestrator detects (`--ping`) and instructs; it can never perform it.
|
||
2. **MD's UI freezes during a blocking call** — no mid-drape supervision; results are
|
||
visible only after `Simulate` returns.
|
||
3. **No mesh introspection in MD** — upstream QC is image-based forever (§1.4).
|
||
|
||
Everything else — including the FBX export previously believed manual — is scriptable.
|
||
That is the headline of this assessment.
|
||
|
||
### 2.4 Registration: emit, don't edit (catalog JSON deferred)
|
||
|
||
Decided 2026-07-31: **defer** the data-driven OutfitCatalog refactor.
|
||
|
||
- Cost today is 2 `Add()` lines per set (+1 `BaseDirFor` case when a new folder
|
||
appears); the refactor is not small (`Initialize()` is all hardcoded calls;
|
||
`ClothingItem` has 10 init-only properties to round-trip). Revisit at ~10 sets.
|
||
- The real risk is not typing, it is the **silent** `setId` fallback (§1.3) — gate G8
|
||
(a ~30-line lint) buys that safety without the refactor.
|
||
- The `register` stage therefore **emits** the exact `Add(...)` line(s) and any needed
|
||
`BaseDirFor` case to stdout + `work/<name>/register.cs.txt` for paste-in.
|
||
Codegen-as-text, not codegen-as-edit — honest about who owns `src/`.
|
||
|
||
---
|
||
|
||
## 3. QC gates (where agents/checks slot in)
|
||
|
||
Ranked by value = known-pain-caught ÷ effort. Both shipped bugs were pose-dependent, so
|
||
the ranking is driven by *how early a pose-dependent failure can be caught*. Thresholds
|
||
live in the config's `expect` block.
|
||
|
||
| # | Gate | After stage | Kind |
|
||
|---|---|---|---|
|
||
| **G5** | **Posed penetration + gape sweep — the centrepiece** | `skin` | scriptable |
|
||
| G6 | In-game motion QC | `import` | agent-with-vision (hard part scriptable) |
|
||
| G3 | Rest-pose penetration | `fit` | scriptable |
|
||
| G2 | Census sanity | `census` | scriptable |
|
||
| G8 | Catalog ↔ asset lint | `register` | scriptable |
|
||
| G7 | Targeted reimport verify | `import` | scriptable |
|
||
| G4 | Reduce budget + silhouette | `reduce` | scriptable |
|
||
| G1 | Drape placement | `drape` | hybrid |
|
||
|
||
**G5 — posed penetration + gape sweep.** Headless Blender loads `50_skin.blend` (it
|
||
already contains `BODY`, `RIG`, and the skinned `GARM_*` parts). Apply real clip poses —
|
||
the UAL animation packs (`ariki-game/assets/quaternius/anim/UAL1.glb` etc.) are authored
|
||
on the same 65-bone skeleton — or synthetic extremes (arm raise, deep step, torso twist).
|
||
Per sampled frame, evaluate the armature-deformed meshes and BVH-test: (i) garment verts
|
||
inside the body, (ii) body verts exposed inside a part's declared coverage band.
|
||
Pass/fail: `expect.max_penetrating_verts` (e.g. 0 verts >1 mm inside) and
|
||
`expect.max_gape_verts` across **all** frames. This is the only gate that would have
|
||
caught BOTH shipped bugs — deterministically, headless, pre-game, re-runnable. One risk
|
||
to verify during implementation: bone-name compatibility between the UAL pack armature
|
||
and the derived-body `RIG` (retarget or pose-copy if names drift).
|
||
|
||
**G6 — in-game motion QC.** `SCENE=clothing_test_bed MOCK_ONLY=1 game.sh spawn` →
|
||
`POST /navigate {"button":"Idle"|"Walk"|"Dance"}` → `GET /screenshot` per clip → judge.
|
||
Two signals: **hard** = `GET /console` contains no outfit load error (machine-checkable
|
||
today); **soft** = visual verdict on poke-through/gaping/texture. Decided 2026-07-31:
|
||
**VLM/agent judge for a new garment's first sign-off, then bless those frames as the
|
||
baseline so re-runs become a deterministic pixel diff.** This also finally gives
|
||
`visual-qa.sh` the comparator it references but never had (§1.4). Small `ariki-game`
|
||
enabler needed (approved): `ClothingTestBed` hardcodes its opening set
|
||
(`DefaultBodyF = "kapahaka_body_f"` etc., `ClothingTestBed.cs:81-84`) — add an env-var/CLI
|
||
override so a new set is directly reachable instead of blind `Cycle Body` presses.
|
||
|
||
**G3 — rest-pose penetration.** The same BVH test as G5, rest pose only, run right after
|
||
`fit` — a cheap early-fail subset that saves reduce/bake/skin time on a bad fit.
|
||
|
||
**G2 — census sanity.** `census.json` already records per-island verts/tris/z/x/centroid.
|
||
Check against `expect.islands` (count, per-mapped-island vert count and z-span
|
||
tolerance). Catches the silent killer: an MD re-export reorders islands and the config
|
||
maps the wrong ones.
|
||
|
||
**G8 — catalog ↔ asset lint.** Every catalog entry's resolved
|
||
`{baseDir}/{Gender}_{Set}_{Slot}.gltf` exists on disk, and every outfit GLB on disk has a
|
||
catalog entry. Zero unmatched either way. ~30 lines; kills the silent `BaseDirFor`
|
||
fallback footgun (§1.3) without the JSON refactor.
|
||
|
||
**G7 — targeted reimport verify.** Replace blunt `clean-import` in the garment loop:
|
||
delete only that asset's two `.godot/imported/` entries (the README already prescribes
|
||
exactly this by hand), run `--import`, then confirm via `/console` + refreshed
|
||
`.gltf.import`. Closes the `.bin`-staleness hole (§1.3) without nuking the whole cache.
|
||
|
||
**G4 — reduce budget + silhouette.** Tris vs `parts[].tris`; pixel IoU of the HI vs
|
||
reduced QA renders ≥ threshold. Low urgency: MD-authored garments arrive at budget (both
|
||
kapa haka pieces never hit their ceilings) — this gate mostly guards downloaded meshes.
|
||
|
||
**G1 — drape placement.** The thresholds already exist as prose — `md_pari.py:4` reads
|
||
"QC targets: band top ~1.31 m (above bust), hem ~1.05 m (waist) ±3 cm". Move them into
|
||
`expect.bands`, upgrade `tools/tailor/qc_placement.py` to read them (instead of its
|
||
hardcoded landmark table), and add a vision pass on the `ExportSnapshot3D` PNG. That one
|
||
move takes drape QC from eyeballed to gated.
|
||
|
||
---
|
||
|
||
## 4. Phased adoption
|
||
|
||
| Phase | Content | Effort | Why this order |
|
||
|---|---|---|---|
|
||
| **1** | `extends` overlay + `expect` block + **G2** + **G8** + orchestrator skeleton driving *Blender stages only* | ~½ day | Zero new subsystems, no MD, no game, no human in the loop. Immediately kills the 48-line config copy and the six-call ritual. |
|
||
| **2** | `draft_garment.py` template + `md` block + scripted `ExportZPrj/FBX/ZPac` in the same bridge call + **G1** | ~1 day | Collapses 4 scripts to 1; MD half becomes unattended after the single session click. |
|
||
| **3** | **G5** posed sweep (+ **G3** as its rest-pose subset) | ~1 day | **The money phase** — catches the class of bug that has actually shipped, before the game. |
|
||
| **4** | **G7** targeted reimport + ClothingTestBed set override + **G6** motion QC + baseline blessing | ~1 day | Real shaders/import/AnimationTree; only meaningful after 1–3. Needs the small approved `ariki-game` edits. |
|
||
| **5** | Data-driven `OutfitCatalog` JSON | deferred | Revisit ~10 sets; G8 buys the safety now. |
|
||
|
||
G4 rides along whenever convenient (it rarely bites for MD-authored garments) rather
|
||
than blocking any phase.
|
||
|
||
---
|
||
|
||
## 5. Resolved decisions (Jeremy, 2026-07-31)
|
||
|
||
1. **Orchestrator home:** `tinqs/animation/clothing/garment.py`; game stages shell out
|
||
cross-repo to `ariki-game/tools/game.sh`.
|
||
2. **G6 judge:** VLM/agent vision judge for first sign-off; bless those frames; re-runs
|
||
are deterministic pixel diffs.
|
||
3. **OutfitCatalog:** stay code-edit; pipeline emits `Add()` lines; add the G8 lint;
|
||
JSON refactor deferred (~10 sets).
|
||
4. **`ariki-game/src` edits:** small enablers approved (ClothingTestBed set override,
|
||
later testbed hooks). The pari/piupiu weight-fix agent owns `garment_pipeline.py`'s
|
||
skin stage + those two configs — coordinate before touching either.
|
||
|
||
## 6. Leave alone
|
||
|
||
- `garment_pipeline.py`'s `--stage` CLI and config schema — additive only; the
|
||
orchestrator wraps, never rewrites.
|
||
- The `skin` stage internals and `pari.json`/`piupiu.json` weight fields — owned by the
|
||
weight-fix agent right now.
|
||
- `md_recon.py` — introspection tool, not a garment script; does not fold into the template.
|
||
- `.agents/wiki/architecture/clothing-lane.md` — cite it; fold outcomes in on adoption;
|
||
never fork its content.
|
||
|
||
## 7. Appendix — file/line references
|
||
|
||
| Fact | Where |
|
||
|---|---|
|
||
| Stage implementations | `clothing/garment_pipeline.py`: census 224–248, prepare 295–505, fit 542–598, reduce 653–706, bake 712–768, skin 908–957 (weight modes 920–944, skirt_bones 836–905), export 964–989 (vertex SHA1 984–989) |
|
||
| Config parse / work dir / CLI | `garment_pipeline.py:1006`, `:36-39`, `:994-1008` |
|
||
| MD bridge protocol/session | `tools/md_bridge.py` (port/env `:88`, `:22`; run/ping `:58-68`), `docs/md-bridge.md:8-94` |
|
||
| Scriptable MD exports (flags) | `.claude/skills/marvelous-designer/SKILL.md:182-190` |
|
||
| MD API lessons | `tools/tailor/md_tee_v1.py:12-28`, `md_skirt_v1.py:6-25` |
|
||
| Drape QC thresholds as prose | `tools/tailor/md_pari.py:4`; classifier `tools/tailor/qc_placement.py:24-34`, bands `:51-80` |
|
||
| Config diff piupiu vs piupiu_sb | `clothing/configs/piupiu.json` / `piupiu_sb.json` — `name`, `body` (`_SkirtRig`), `parts.Piupiu.weights`, `export.set` |
|
||
| Catalog path resolution + silent fallback | `ariki-game/src/Character/OutfitCatalog.cs:57-98` (warn-only 71–78, `BaseDirFor` `_ =>` 96–97), `Add()` pattern in `Initialize()` 116+ |
|
||
| Test bed buttons / defaults / load-fail HUD | `ariki-game/src/Testing/ClothingTestBed.cs:449-516`, `:81-84`, `:88-89` + `:279-281` |
|
||
| Spawn / engine / import / clean-import | `ariki-game/tools/game.sh:633-749`, `:39-70`, `:832-841` (+ asset check `:848`), `:478-482` |
|
||
| Agent API endpoints | `game.sh:215-242` (`/console` via AgentServer) |
|
||
| Visual QA harness (judge missing) | `ariki-game/tools/visual-qa.sh:1-77` — calls `tools/vision-compare.py`, which does not exist |
|
||
| Reimport gotcha (hand fix prescribed) | `clothing/README.md:143-147` |
|
||
| Reference logic in older converters | `ariki-game/tools/cc_clothing_to_quaternius.py` (skeleton-frame alignment 66–73, KDTree copy 110–127, A-pose check 198–205), `make_islander_outfits.py` (region cuts 103–149) |
|
||
|
||
## 8. Build outcome (2026-07-31)
|
||
|
||
Built in one session by five parallel agents plus an integration pass. As-built
|
||
interfaces are documented in `clothing/PIPELINE-CONTRACT.md` (marked AS-BUILT); the
|
||
one-command usage and gate table are in `clothing/README.md`.
|
||
|
||
| Lane | Delivered |
|
||
|---|---|
|
||
| A | `clothing/garment.py` — the orchestrator: `extends` resolution, deep-merge, path absolutization, 13 stages, gate scheduling, `register` codegen, `--selftest` |
|
||
| B | `gates/g2_census.py` (island census sanity), `gates/g8_catalog_lint.py` (catalog↔asset lint, `--all` / `--require-registered`) |
|
||
| C | `gates/g5_posed_sweep.py` — posed penetration + gape sweep; G3 is its `--rest` mode |
|
||
| D | `tools/tailor/draft_garment.py` (MD script generator), `tools/tailor/qc_placement.py`, `gates/g1_drape.py` |
|
||
| E | `ariki-game/tools/targeted_reimport.sh` (G7), `ariki-game/tools/clothing_motion_qa.sh` (G6), `ClothingTestBed.ReportLoad` |
|
||
|
||
### Acceptance test
|
||
|
||
`configs/tests/piupiu_sb_test.json` (scratch export target, set `KapahakaSBTest`) run
|
||
end to end, `census..export`, `--no-gate-stop`:
|
||
|
||
| Stage | Time | Gate |
|
||
|---|---|---|
|
||
| census | 8.0 s | **G2 PASS** — 1 island, 3620 v / 7104 tri, matched `expect.islands` |
|
||
| prepare | 4.7 s | — |
|
||
| fit | 3.3 s | **G3 FAIL** — 1 vert, 8.0 mm deep, at rest |
|
||
| reduce | 3.1 s | — |
|
||
| skin | 3.4 s | **G5 FAIL** — see below |
|
||
| export | 3.3 s | — |
|
||
|
||
**G5 reproduced the shipped piupiu bug headlessly**, in 14.9 s, from the real game
|
||
clips (`Idle_Loop` from UAL1, `Walk_Fwd_Loop` from UAL2; 65/65 pack bones matched
|
||
the RIG, the 16 skirt bones ride their parents):
|
||
|
||
```
|
||
rest 1 penetrating vert (max 8.0 mm)
|
||
Idle @0/12/25/38/50/62 229 217 216 231 216 216 (max 75.2 – 85.2 mm)
|
||
Walk @0/7/13/20/27/33 169 134 61 90 128 63 (max 70.0 – 87.9 mm)
|
||
```
|
||
|
||
That is the thigh punching through the skirt: ~1 vert visible in the pose the whole
|
||
pipeline used to sign off on, and **216–231 verts up to 8.5 cm deep** the moment the
|
||
character stands still and breathes. 13 failing frames, with front + closeup QA renders
|
||
for six of them. The gate is worth its cost.
|
||
|
||
The **pari neckline gape did NOT reproduce** in the Blender checkpoint — it is not
|
||
present in the skinned mesh G5 measures. Its origin is therefore in-game (attach,
|
||
material, or LOD), which makes it G6 territory, not G5. Worth stating plainly because
|
||
the pre-build assumption was that both shipped defects were the same class of bug.
|
||
|
||
### Real findings the gates produced
|
||
|
||
- **G8** found an *uncommitted* `OutfitCatalog` registration hunk in the working tree —
|
||
a garment registered locally and never pushed, which the lint surfaced immediately.
|
||
On the test set it correctly reports `KapahakaSBTest` has no `BaseDirFor` arm and so
|
||
would silently resolve into the Fantasy pack folder.
|
||
- **G1** measured the *shipped* garments against their own written QC targets and found
|
||
both off: pari's hem sits **+3.9 cm** high, and the piupiu's bands **+8 cm / +14 cm**.
|
||
Those targets had lived as prose in the garment scripts' headers since they were
|
||
written; nobody had ever checked them against a render.
|
||
- **`/console` did not surface load failures at all** until `ClothingTestBed.ReportLoad`
|
||
was added — `GD.Print` does not feed the agent API's ring buffer. Every "the bed looks
|
||
fine" sign-off before that fix was reading an empty channel.
|
||
- **G6's pixel diff has a measured floor**: same outfit re-run moves ~0.002 of the frame,
|
||
an entirely different outfit moves 0.014, and by frame 3 phase drift alone (0.0095)
|
||
matches the signal. So G6 catches gross regressions (garment vanished, failed to load,
|
||
swapped) and nothing subtler. **G5 owns the subtle bug class** — which is the right
|
||
split, because G5 is headless, deterministic and 15 s.
|
||
|
||
### Still deferred
|
||
|
||
Catalog-as-JSON (§2.4) — `register` still emits `work/<name>/register.cs.txt` for a human
|
||
to paste, by design. Nothing in the build depends on changing that.
|