docs+tools: working-file policy, lane pruner, and a human MD explainer

Two pieces of work.

1. Keep milestones, not steps. Staged NN_*.py lanes were saving a full
   ~80 MB .blend per attempt, so Lena's lane reached 3.0 GB of which 2.2 GB
   was 30 .blend files -- five snapshots to land one crotch fix, five more
   for the bra. The .py recipes are the real history; the blends are cache.

   - .agents/rules/working-files.md: four-tier policy (KEEP / MASTER /
     SCRATCH / UNKNOWN) + how to work a lane.
   - tools/prune_lane.py: classifies a lane and prunes the scratch tier.
     Dry-run by default. Reads a per-lane .lanekeep manifest, flags
     binaries byte-identical to a registered original (canonical name
     always survives a duplicate pair), and never auto-deletes a .blend
     with no step script beside it -- those cannot be rebuilt.
   - .gitignore: scratch patterns can never be committed.

   Dry run on characters/female/lena_nude reports 2.3 GB reclaimable.
   Not applied -- that lane had a live Blender session at the time.

2. .humans/marvelous-designer.html: how we author garments in Marvelous
   Designer, written for people rather than agents -- the six-step process,
   what has been made, the traps that cost hours, and what is still
   unsolved. Matches the .humans/ HTML convention in ariki-game.

Also committing the docs the AGENTS.md knowledge map and the new page
reference, so they are not dangling: the clothing-lane architecture page,
the marvelous-designer skill, and the two screenshots the page embeds.

characters/ is deliberately untracked and stays that way -- it holds GBs of
blends and GLBs, and .gitattributes does not LFS-track .blend.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 12:05:46 -07:00
parent 854dbb5ce4
commit d1030def4e
13 changed files with 1729 additions and 6 deletions
@@ -0,0 +1,214 @@
# Deconstruction — taking a reference apart like a tailor
Companion to `../SKILL.md`. That file is drafting/draping doctrine; **this file is
the step before it**: how to read a clothing image (or description) and take the
garment apart into pieces our MD lane can actually build. Skipping this step is
how garments shipped 414 cm off their own targets — the drafting was fine, the
*analysis* had never been written down.
The output is a **worksheet of numbers**, not geometry. Panel point lists come
last, and mostly from `tools/tailor/blocks.py`, not by hand.
---
## 0. The worksheet (output contract)
Fill this in full **before** asking for a bridge session. Every number the drape
and QC stages use traces back to a row here.
| # | Item | Feeds |
|---|---|---|
| 1 | Slot split — which separate garments is this outfit? | one config per garment |
| 2 | Per garment: class + block choice | `blocks.py` function |
| 3 | Anchoring — what holds it up | construction + arrangement |
| 4 | Placement targets — each edge's z ± tol vs landmarks | `expect.bands` (gate G1) |
| 5 | Edge circumference + ease table | panel widths, taper |
| 6 | Piece list + seam plan | `md.panels` / `md.seams` |
| 7 | Textile plan — what is texture, not geometry | texture generation |
| 8 | Fabric read — weight/stiffness | `.zfab` choice + sim recipe |
| 9 | Risks — unsolved/untested features | scope call before starting |
`blocks.py` turns rows 25 into a config skeleton (`md` block + `expect.bands`)
in one call. If no block fits, hand-draft under `tooling.md` §3 rules (named
landmarks, computed line indices) — never freehand a point list.
## 1. Split the outfit into garments by slot
The game wears **per-slot parts** (Body, Legs, …) and MD drapes **one garment per
scene** — a second garment, even frozen, grabs and inverts the new one. So a
"dress over leggings with a belt" reference is *three* worksheets, three configs,
three drape sessions. Decide the split first:
- One garment per clothing slot it occupies. A dress is one garment (Body slot,
or whatever slot spans it) even though it covers both regions.
- Belts, sashes, armbands: texture if flat against the host garment; separate
garment only if they hang or swing.
- Outfit photos are assembled at the end from finished `.zpac`s (SKILL.md
"Outfit assembly") — never co-draped.
## 2. Anchoring — decide what holds it up before what it looks like
Anchoring determines construction more than silhouette does. MD materialises
cloth at the arrangement point and does **not** simulate donning, so nothing
"slides into place".
| What the image shows | Anchor | Construction consequence |
|---|---|---|
| Straps, sleeves, or a shoulder line | shoulder-hung | bodice block; straps reach `shoulder_z` or the garment settles at the underbust |
| Strapless top | (unreliable) | **add straps anyway** — a tube slides to the narrowest catch; straps also cover the body's baked-in bra straps |
| Skirt/trousers at waist or hip | waist tension | cut the waist **smaller** than the hips (0.900.92 × hip circ) and let stretch hold it; elastic only mid-settle, never frame 0 |
| Light/gappy garment (strands, fringe, open weave) | arrangement itself | arrangement height **is** the placement — a light band stays exactly where arranged |
Always check what the anchor must *cover*: the target body has baked-in
underwear, and any neckline or scoop that drops below it reads as the garment
failing. Check the **back** neckline explicitly — the reference photo almost
never shows it, `ExportSnapshot3D` cannot show it (`tooling.md` §1.1), and that
blind spot has shipped defects. If the image doesn't show the back, *decide* the
back (scoop depth, coverage) and write it on the worksheet rather than letting it
default.
## 3. Read the seams, then discard most of them
A real tailor's deconstruction finds every seam. Ours finds them and then
**collapses almost all of them**, because the MD lane sews whole edges only, has
no darts, and garment identity for our targets is mostly textiles (SKILL.md).
Translation table:
| Feature in the image | Our move |
|---|---|
| Side seams, shoulder seams | keep — these are the block's real seams |
| Darts (bust, waist) | **no darts.** Taper the panel side edges + stretch ease carries the shaping |
| Princess seams, yokes | collapse into the panel; if visible, draw them in the texture |
| Waistband | merge into the panel top; separate band only for strand/fringe skirts |
| Set-in sleeves | **untested** — no sleeve block yet; treat as scope risk (row 9) |
| Collars, hoods | **untested/unsolved** — same |
| Trousers/shorts crotch | **unsolved** (failure catalogue); next approach is 4 panels on the per-leg points |
| Plackets, buttons, zips, pockets, topstitching | texture, always |
| Gathers, pleats, ruffles | texture unless the *silhouette* depends on them |
| Fringe / strands / fur edge | teeth cut into the panel outline (comb panel), never partial seams |
| Belt/sash flat against the garment | texture |
The test for "geometry or texture?": does it change the **silhouette** or the
**edge positions**? If not, it's texture.
## 4. Placement targets — numbers before points
Extract where every garment edge sits **before drafting anything**. This is the
step that was skipped when shipped garments landed 3.914 cm off targets that
existed only as prose.
Landmark card for the current body (`tools/tailor/lena_measurements.json`,
regenerate per body with `measure_body.py`; metres, floor = 0):
| Landmark | z | Landmark | z |
|---|---|---|---|
| top of head | 1.777 | hip (widest) / crotch | 0.946 |
| neck base | 1.457 | thigh | 0.865 |
| shoulder | 1.397 | knee | 0.517 |
| chest (bust) | 1.264 | ankle | 0.106 |
| waist | 1.089 | | |
Method, per garment edge (top of band, hem, waistline…):
1. Find the edge in the image relative to the two nearest **visible** landmarks
(e.g. "hem lands mid-thigh, about ⅓ of the knee→hip span above the knee").
2. Interpolate a z from the card. The body is stylized — always place against
*these* landmarks, never against real-world garment-length conventions.
3. Assign a tolerance: ±3 cm for fitted edges, ±4 cm for free-hanging hems.
4. Write the rows into `expect.bands` — gate G1 measures every drape against
them from then on.
Sanity anchor (proven): a pari band 1.31→1.05 m and piupiu 1.05→0.45 m came from
exactly this read of the kapa haka reference.
## 5. Ease — how much bigger than the body
For each **horizontal** edge, the panel width comes from the body circumference
at that z plus ease. Interpolate circumference linearly between the card's
(z, circ) pairs — neck 0.392, chest 1.083, waist 0.675, hip 1.095 — and **never
interpolate below the hip** (the legs bifurcate; a slice there measures nonsense).
| Fit read from the image | Total ease | Proven case |
|---|---|---|
| Snug / bandeau / activewear | +1520 mm | pari: 1100 vs 1083 bust |
| Regular fitted top | +90100 mm | tee: 1180 vs 1083 bust |
| Loose / drapey | +150 mm and up | (untested above ~150) |
| Bottoms waist edge | **negative**: 0.900.92 × hip circ | piupiu: 1000 vs 1095 hip |
Bottoms are the inversion to internalise: the waist edge is cut *smaller* than
the hips it must pass over, because tension is the anchor (§2).
## 6. Vertical spans are 1:1
Pattern millimetres map 1:1 to world metres — drape shrinkage is negligible for
our fabrics. So:
- panel cloth height = `(top_z hem_z) × 1000`
- a shoulder-hung garment's straps must reach the shoulder: total panel height
= `(shoulder_z hem_z) × 1000`, and the front scoop depth is what's left
between strap top and the visible band top: `scoop = panel_h (band_top_z
hem_z) × 1000`.
Proof this math is the real one: it reproduces all three shipped garments —
tee 480 (shoulder 1.397 → hip-ish hem 0.917), pari 350/scoop 105 (shoulder →
waist 1.05, band top 1.30), piupiu 600 (1.05 → 0.45).
## 7. Blocks — what we can build today
| Garment class | Block | Status |
|---|---|---|
| Tank / tee / fitted bodice / bandeau-with-straps | `blocks.fitted_top` | **proven** (tee, pari) |
| A-line / straight skirt | `blocks.aline_skirt` | **proven** (skirt, piupiu v2) |
| Strand / fringe skirt (piupiu, hula, fur trim) | `blocks.strand_skirt` | proven construction (comb outline), parameters per garment |
| Dress | `fitted_top` + skirt geometry in one panel pair | **untested** — try taper-through-waist first |
| Trousers / shorts | — | **unsolved**; next: 4 panels on `Leg_Front_L/R`, `Leg_Back_L/R` |
| Sleeves, collars, hoods | — | untested; scope risk |
| Capes / cloaks / rectangles (traditional wear) | plain panels | rectangles + blocks; identity is the textile |
Every block bakes in the expensive discoveries — seam parity for
identical-offset panels, the arrangement-x rules per point family, bodice taper,
tension waists, strengthen/relax sim recipes — so **prefer a block over a
hand-typed point list even when the block needs post-editing**. Generate, then
edit the config, not the other way round.
## 8. Textiles carry the identity
For traditional wear especially (kapa haka, Mexica, Pacific), the garment reads
as *itself* because of the textile, not the cut. Spend the effort there:
generate the pattern procedurally (PIL), set physical size via DPI
(`dpi = px / (mm / 25.4)`), and keep the generator script — motifs recur.
Model the shape as simply as the silhouette allows.
## 9. Fabric read → sim plan
From the image, judge weight and stiffness, then pick levers (`tooling.md` §4):
- **Stiffness ladder** (`.zfab` presets): `V2_Woven_Canvas_1` <
`V2_Woven_Denim_1` < `V2_Non-Fabric_Tyvek_1`. `fabric_api` has no physics
setters — the `.zfab` *is* the stiffness choice.
- Fully-sewn sides → strengthen through the whole settle, relax at the end.
Under-constrained (half-sewn sides, thin straps) → **don't** strengthen
(it rotates the garment); remove surplus cloth instead.
- Light/gappy → `SetParticleDistanceOfPattern` ~20 mm and remember §2:
arrangement height is placement.
- Skin-tight → `SetAddlThicknessCollision` a few mm before the settle.
## 10. Worked example — the kapa haka reference, as this method
What was done by trial and error in 2026-07-30/31, restated as the worksheet:
| Row | Pari (bodice) | Piupiu (skirt) |
|---|---|---|
| Slot | Body | Legs |
| Class/block | bandeau → `fitted_top` | strand skirt → `strand_skirt` (v2 shipped as `aline_skirt`) |
| Anchoring | photo shows strapless → **straps anyway** (covers bra straps, fixes height) | waist tension, 1000 vs 1095 hip; light strands → arrangement = placement |
| Targets | band top 1.31 ±0.03, hem 1.05 ±0.03 | waist 1.05 ±0.03, hem 0.45 ±0.04 (below knee) |
| Ease | snug: +17 mm over bust | waist 0.91 × hip |
| Seam plan | shoulders + sides; scoop/armholes in outline | band side edges only; strands are outline teeth |
| Textile | tāniko band → generated `taniko.png`, DPI-sized to span once | flax strands + geometric banding → `piupiu.png` |
| Fabric | default sim fabric, strengthen full settle | default, strengthen + (v3) elastic mid-settle |
| Risks | back neckline never visible in photo — decided, then verified via turntable | hem vs leg-swing envelope (skirt bones downstream) |
The lesson the example carries: **nothing in the finished configs is a guess.**
Every number is a worksheet row, and every worksheet row is checkable — by G1
against the render, or by `GetLineLength` against the drafted panel.