wardrobegod/STATE_AND_PLAN.md
type-two b1381d22d2 Phase 0+1: unblock the bench, then make it dress and move
Phase 0 — the fixes that were blocking every tier:
· /api/blender had no allowed() guard, unlike every other path-taking endpoint. Under the
  documented WG_HOST=0.0.0.0 that was an unauthenticated arbitrary-file -> Blender import +
  library-write for any tailnet peer. Now gated on allowed() + isfile.
· scan() listed garments as models only, but /api/hangable writes .png there — so every
  cut-out the generator produced was invisible in its own UI and the whole 2D paper-doll
  route dead-ended. garments now lists models AND images.
· mb_download() wrote json.dumps(data) whenever the farm response wasn't bytes, and mb_req
  parses any JSON-200 into a dict — so a farm error body got written *into* the target
  .png/.glb and the job was still marked done. Now raises instead.
· MB_TOKEN is read off disk from backnforth/.env, so the farm tiers (RMBG-2.0, image-edit
  try-on, trellis) stop silently dying when a launch forgets to export it.
· glb_of() keyed its cache on the basename alone, so two sources that slug the same served
  each other's geometry; the key now carries a hash of the full path.
· JOBS grew forever and glb_of minted an orphan job on every cache miss inside GET /glb.
  run_blender() now tolerates jid=None and finished jobs get reaped.

Phase 1 — a dressed, animated character on screen:
· clip transport: the mixer used to blind-play animations[0] with no way to reach the rest.
  Clip dropdown + play/pause + scrub + speed, with the scrub head following playback.
· rigid attach stopped being a lie. It cloned into three.js and POSTed nothing ("preview
  only"), so every hat placement died on reload. Attachments now carry their bone/offset/
  tint and persist via /api/outfit -> library/outfits/<name>.json, reloadable from a chip.
· colourway swatches retint the last attached item (materials are cloned per instance so a
  tint doesn't bleed), giving free variants with no re-generation.
· flat cut-out garments get a 2D preview pane — the paper-doll tier had no way to be seen.

Verified end-to-end in the browser: tradie_at_wanwalk (22-bone mixamorig) walks wearing a
red-tinted felt hat on mixamorigHead, saved, page reloaded, restored from disk.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 15:43:02 +10:00

26 KiB
Raw Blame History

WARDROBEGOD — state of play + how to improve it heaps

Recon 2026-07-24 across JING5 / ultra / m3ultra. Every fleet claim below was spot-verified live (see §B).

TL;DR

The bench works, the wardrobe is empty, and one bug is dangerous. The three-column three.js dress-up shell runs, the local Blender pipeline (convert / scale / decimate-keep-weights / fit / assemble) works, and the Cloudflare flux generator works — but library/ holds 2 bodies + 1 garment PNG (which is invisible to its own UI), out/ and jobs/ are empty, the trellis tier is dead (no MB_TOKEN), and /api/blender is an unauthenticated arbitrary-file → Blender primitive.

Meanwhile the fleet already contains everything wardrobegod is trying to become, several times over:

  • NPCFACTORY (ultra) — a further-along dress-up bench: 17 finished rigged, walk-animated NPC GLBs
    • a completed reskin engine (paint clothes onto a body's own UV atlas).
  • 90sDJsim (ultra) — a shipped $0 2D wardrobe: 441 authored transparent-PNG garment sprites + a self-healing doll compositor.
  • MODELBEAST (m3ultra :8777, UP, 26 live operators) — free local virtual try-on (mflux_image_edit = "Edit Image (local, Qwen)": "put the shirt from image 2 on the person in image 1"), flux, RMBG-2.0, TRELLIS.2, Hunyuan3D, SF3D, layered-RGBA cutouts, MoMask text→motion.
  • character_kit_modular (ultra) — a socket-swap costume engine on rigged mixamorig bases.
  • MIRPAMO (everywhere) — a local, offline Mixamo (auto-rig → 22-bone mixamorig + retarget).
  • ~2,394 + 2,548 + ~163 animation clips already on disk.

The decision: stop treating wardrobegod as a Blender-op bench and make it the one thing the fleet lacks — the canonical garment/outfit LIBRARY + a dressing-room authoring UI + a multi-consumer EXPORT HUB. The dressing engines exist five times over; the connective tissue exists zero times. Graft the fastest proven engines onto that spine (reskin, try-on, the 2D doll, donor-harvest).


A. Where it's at — honest state of play

Works today

  • server.py (stdlib ThreadingHTTPServer :8150): /api/lib, /glb?p= (Blender convert-cache), /file?p=, /api/upload, /api/blender (op dispatch), /api/gen (CF flux), /api/rmbg, /api/to3d, /api/hangable. No build step.
  • blender_ops.py (166 lines): convert / scale / decimate-keep-weights / fit / assemble. The fit op (data_transfer VGROUP_WEIGHTS POLYINTERP_NEAREST + armature parent, blender_ops.py:128-132) is the proven wardrobe mechanic — wrap it, don't rewrite it.
  • web/index.html (316 lines): OrbitControls + GLTFLoader stage, 3 tabs, body/garment lists, mixer that auto-plays animations[0].
  • CF flux /api/gen@cf/black-forest-labs/flux-1-schnell works with only the CF creds in .env.
  • Blender 5.1.2 + PIL present on JING5 — the whole local pipeline runs with no farm dependency.

Content truth (the real problem)

library/bodies/    phrtt2.glb (1.67M), droidte2-18k.glb (7.97M)
library/gen/       brown-corduroy-jacket.png, -cut.png
library/garments/  brown-corduroy-jacket-cut.png   ← INVISIBLE to the UI (bug #2)
out/  jobs/        EMPTY
.env               CLOUDFLARE_* only — NO MB_TOKEN

Zero garments have ever been fitted to any body.

The bodies are not the consistent A-pose set the brief assumed

The 6 ~/Documents/anatomy FBX (verified by headless render): 2 A-pose (dudescholng, 2_views_3d), 2 T-pose (phrtt2, droidte2), 2 arms-down/contrapposto. 5 of 6 are unrigged; only droidte2 has a skeleton (41-bone reduced mixamo) and it's already clothed (shorts+boots), 482k-tri raw. Each is an independent TRELLIS reconstruction — no shared topology, own UV atlas. → One fitted garment can't be reused across bodies; each needs its own per-body fit, and only shrinkwrap/proximity works (no vertex correspondence).

The real bugs (file:line)

  1. SECURITY — /api/blender is unguarded. allowed() guards /file /glb /api/rmbg /api/to3d /api/hangable but not /api/blenderfx() passes a['path']/a['body']/a['garment']/ a['garments'] straight into glb_of()run_blender() (server.py:238-265). Under the documented WG_HOST=0.0.0.0 (server.py:7) that's an unauthenticated arbitrary-file → Blender import + library-write for any tailnet peer. allowed() itself is sound (server.py:58-61) — it's just not called here. Fix before any 0.0.0.0 exposure.
  2. scan() hides PNG garments. server.py:67 uses MODEL_EXT for garments, but /api/hangable writes a .png there (server.py:374). Every hanging-texture / doll cutout is silently filtered out — the whole 2D route dead-ends in an unshowable directory. One-line fix, prerequisite for 2D.
  3. glb_of cache-key collision. server.py:96 keys on slug(basename(path)) only, single-source mtime check (:97). Same-basename files in different dirs collide → serve wrong geometry.
  4. Silent corruption in mb_download. server.py:156-157 writes json.dumps(data) when the farm response isn't bytes; a JSON error body gets written into the target .png/.glb and the job is marked done. Affects /api/gen /api/rmbg /api/to3d.
  5. JOBS leaks unbounded (server.py:50), and glb_of spawns a convert job on every cache miss even inside synchronous GET /glb (server.py:98), orphaning records.
  6. Local rmbg keyer is crude — iterative border flood-fill through near-white (>232), binary alpha, server.py:329-344. On a non-white bg it never seeds → silent no-op opaque copy.
  7. Rigid-attach is ephemeralweb/index.html:215-225 clones into three.js and POSTs nothing; the toast admits "preview only." Attach transforms live only in browser memory.
  8. fit: picks body as max(meshes, key=len(vertex_groups)) → multi-part body donates weights from one mesh only (blender_ops.py:115-117); no matrix_parent_inverse (:131).
  9. assemble trusts an unenforced bone-name contract (blender_ops.py:151-161).
  10. No auth / CORS / rate-limit / delete / thumbnails / search; gen filenames overwrite silently.

Not a bug (verified, don't touch): the suspected decimate modifier_move_up infinite-loop / 30-min hang does not reproduce on Blender 5.1.2 — the operator returns {'FINISHED'}, loop exits in one iteration. Code smell only.

Convention drift

  • three.js from unpkg CDN (index.html:133-136) — house style is vendored r175 (breaks offline / un-shippable into a game build).
  • README frames the generator as MODELBEAST-only; commit 6f32c08 quietly made CF the primary flux backend — a cloud path outside the LOCAL-FIRST doctrine.
  • "Only dressed outfits ship into games" is one unenforced README line (README.md:45) — no code gate.

B. What the fleet already gives us (verified live 2026-07-24)

MODELBEAST — http://100.89.131.57:8777 (m3ultra), UP, 26 live operators

Bearer MB_TOKEN (on disk at ~/Documents/backnforth/.env, len 44 — never paste). Contract: POST /api/assets (multipart) → POST /api/jobs {operator, asset_ids|asset_id, params} → poll GET /api/jobs/{id} → outputs = assets with parent_job==idGET /api/assets/{id}/file. main.py:290-293 already accepts an asset_ids array → multi-image try-on is submittable today.

Wardrobe-critical live operators (display name → slug):

Display name Slug Use
Edit Image (local, Qwen) mflux_image_edit virtual try-on — 1-3 imgs, "put the shirt from image 2 on the person in image 1". The key unused capability.
Qwen-Image-Layered (local) qwen_layered_local image → N editable RGBA layers = the "paper cut-out layers" rep, generated
FLUX (local, MLX) flux_local flux2-klein-4b, ~40s, ungated
Remove Background (local) bg_remove_local RMBG-2.0 matting (better than the local keyer)
SD1.5 + anatomy LoRAs / SD+LoRA (ComfyUI) sd_local OpenPose T-pose ControlNet + IP-Adapter identity lock
TRELLIS.2 (SOTA) / TRELLIS.2 MLX trellis* image → GLB (~2-5 min) — single-image only
Hunyuan3D 2.1 (local MLX) hunyuan3d_mlx image → GLB, no HF login
SF3D (fast draft) sf3d image → textured GLB, seconds — good for quick garment blockouts
Motion (MoMask) text → BVH motion (+mp4) — text-to-animation
Upscale (SeedVR2), Brush 3DGS Train polish / gaussian-splat

Copyable stdlib CLI: ~/Documents/MODELBEAST/mb./mb run flux_local -p prompt=... --wait --download out/. Launch pattern: export MB_TOKEN=$(grep '^MB_TOKEN=' ~/Documents/backnforth/.env | cut -d= -f2); python3 server.py.

NPCFACTORY (ultra ~/Documents/NPCFACTORY, 410M) — the further-along sibling ✓17 banks confirmed

  • Same stdlib + vendored three.js house style; already has orbit stage + clip dropdown + speed/height-normalise, click-a-part bone-attach dress-up, recipe JSON, in-UI Blender bank-builder.
  • 17 finished rigged, walk-animated NPC GLBs in library/banks/ (tradie/cop/goth/nanna/postie/ busdriver/bizdad… 7-25MB each) — instant dressed+animated characters to load.
  • The reskin trio (E6 COMPLETE, all 3 confirmed on disk): tools/render_plates.py (bind-pose front/back EEVEE ortho plates, 1024, transparent), tools/align_plate.py, tools/bake_skin.py (Cycles DIFFUSE projection bake onto the rig's own UVs, front/back via Normal.Y<0). archetype_batch.py = batch factory template.
  • Constraint: reskin only works on single-mesh / single-UVMap / single-material bodies — John's nude TRELLIS-2 figures are exactly this class. (droidte2 is disqualified: multi-part, clothed.)

90sDJsim (ultra ~/Documents/90sDJsim) — the 2D wardrobe, already shipped ✓441 sprites confirmed

  • web/world/media/doll/ = 441 transparent-PNG garment layers, <slot>_i_<slug>.png. Runtime compositor doll.js (704×1408, ORDER base→bottom→shoes→top→hat, bag last). doll_layout.json nudges.
  • Self-healing loader server.py:167-178: _art_slug = re.sub(r'[^a-z0-9]+','_',name.lower()).strip('_'). Drop a PNG + restart = that garment wears its own art, zero code change.
  • $0 generator: tools/{gen_wardrobe,doll_composite,doll_stage,build_wardrobe_manifest}.py — prompt → flux_local on solid greenbg_remove_local → stage onto 704×1408 via measured SLOT_DEF anchors. Prompt template + 321 authored items + 432 reference JPGs exist.
  • Caveat: the full doll pipeline is ONLY on ultra; the JING5 checkout is stripped → export-to-djsim must rsync to ultra.

character_kit + character_kit_modular (ultra canonical ~/Documents/character_kit/)

  • Rigged mixamorig bases (65-bone) + already-rigged wearable props (rigged/{w_beanie,w_felthat}.glb, props/modular/{beanie,buckethat,felthat,boombox,camera}.glb).
  • scripts/merge_anims.py — base rig + N anim-only FBX → one multi-clip GLB (NLA tracks).
  • modular/scripts/: cut_parts.py (cut a mixamorig char into swappable socket parts), assemble.py (attach_part, fit_part cross-rig auto-scale, attach_attachment bbox-fit+bone-parent), texture_variants.py (numpy hue-mask recolour). parts_library/library.json = the garment-registry schema to copy.

Animation banks

  • ultra ~/Documents/MOTIONLIB/green/mixamo_full = 2,394 anim-only mixamorig FBX (1.3G, shippable w/ attribution) + cmu_bvh 2,548 BVH + 100style/accad/kaykit/quaternius.
  • ~163 local mixamorig clips already on JING5+ultra — enough for a showcase set now, no downloads.

Garment donors + bodies

  • ultra ~/Documents/3D=models/characters/people-normal/ = ~40 clothed, mixamorig-rigged FBX (man_suit_01, man_dj_streetwear_01, woman_raver_01, man_worker_hivis_01…). Exclude actorcore-unreal/ (RED, native-only).
  • Clean multi-LOD nude base: m3ultra ~/Documents/trellis2-bench/anatomy_{2000,8000,30000,50000,800k}.glb.
  • ultra 3d-from-m5/AUDIT.md: of 4278 files, 43 are rigged + game-budget (≤10k tris).
  • fluxgod (JING5) — mb.py/a1111.py/openrouter.py multi-lane client with restart-safe queue + autocrop. Copy wholesale rather than re-implement CF-only.

Two honest fleet limits (do NOT build against these)

  • TRELLIS.2 is single-image — do NOT build a "4-view sheet → TRELLIS" bridge; multi-view is only for reskin plates / QC.
  • No live vision model (m4pro Ollama = text qwen2.5:7b + nomic-embed only) → auto-tagging garments is NOT an existing capability (needs a qwen2.5-vl pull or paid OpenRouter). MODELBEAST has a local text LLM (Qwen3-30B) but not a captioner.
  • No garment/clothing LoRA exists — styled-garment fidelity is prompt-only until brush_train makes one.

C. The plan — phased, each ending in a demo

Guiding principle: the data model (§D) is cheap stdlib JSON — it goes in Phase 1 and every phase populates it. Fast/proven engines first (reskin, try-on, 2D doll), quality-core cloth mid (donor harvest), UI polish last.

Phase 0 — Unblock (½ day)

  1. Bug #2 (server.py:67): add .png to garments scan exts → cutouts/doll layers become visible.
  2. Bug #1 (server.py:238-265): call allowed() on the blender-op paths before any 0.0.0.0 exposure.
  3. Export MB_TOKEN at launch (unlocks try-on, RMBG-2.0, trellis) — LaunchAgent or launcher script.
  4. Bug #4 (server.py:156-157): check content-type/magic bytes before writing a farm response; mark the job error on a JSON body.

Phase 1 — "It dresses and it moves" (1 day) → dressed, animated character on screen

  • Track 1 — the stage moves: add a clip dropdown + play/pause/scrub (the mixer exists at index.html:191-194; there's no selector). Build clips via character_kit/scripts/merge_anims.py (base rig + ~10 local clips → one multi-clip GLB, NLA tracks) → rsync to JING5.
  • Track 2 — it dresses: load a rigged mixamorig base; rsync one NPCFACTORY bank NPC (already dressed+animated) as the guaranteed floor. Attach a rigid hat (character_kit/rigged/w_felthat.glb) via the existing bone-attach, but make it persist (POST an outfit.attach record, not just an in-browser clone). Add runtime material.color colourway tinting (free).
  • Demo: a rigged character walks on screen wearing an attached hat, clip dropdown switches animations, a colour swatch retints it.
  • Stretch (starts the real nude-figure track): kick off the first reskin bake on phrtt2 (T-pose, single-mesh/UV — ideal reskin class): mirpamo rig phrtt2.glbrender_plates → repaint → bake_skin.

Phase 2 — Reskin engine: paint clothes onto nude bodies (2-3 days)

Copy the NPCFACTORY trio (137 lines). New POST /api/reskin, mirroring run_blender+job_thread:

1. blender -b -P render_plates.py -- body_rigged.glb plates/      → front.png, back.png
2. MB job mflux_image_edit  asset_ids=[front.png, garment_cut.png]
   "put the garment from image 2 onto the person in image 1, keep exact pose+silhouette, plain bg"
3. blender -b -P align_plate.py  -- edited_front.png front.png aligned_front.png
4. blender -b -P bake_skin.py    -- body_rigged.glb aligned_front.png aligned_back.png costume.png
5. three.js: material.map = TextureLoader().load(costume.png); needsUpdate=true

A costume dropdown swaps material.map at runtime — instant, free, reversible toggling between pre-baked outfits.

  • Demo: pick a nude TRELLIS body → pick/generate a garment → walking clothed character; flip a dropdown to change costumes instantly.
  • Ceiling (say it out loud): the nude silhouette never changes — sells tight/printed garments & uniforms & mid-distance figures, but a jacket reads as a jacket-print bodysuit. Hero silhouette garments need Phase 4.

Phase 3 — The 2D paper-doll product + first export contract (2-3 days)

Adopt 90sDJsim/tools/{gen_wardrobe,doll_composite,doll_stage,build_wardrobe_manifest}.py wholesale. Emit media/doll/<slot>_i_<slug>.png honoring _art_slug exactly. Wire qwen_layered_local / bg_remove_local for matting-grade cutouts (upgrading the crude local keyer). Import the 441 sprites as a starter pack. POST /api/export {target:"djsim", outfit} writing over rsync to ultra.

  • Demo: generate a garment → it appears as a doll layer → drops into 90sDJsim with zero consumer code change (self-healing DOLL_ART). Highest-ROI, lowest-risk value on the board.

Phase 4 — Quality-core: real 3D cloth via donor harvest + hide-masks (8-9 days)

New blender_ops.py op harvest: import a clothed mixamorig donor → bpy.ops.mesh.separate(type= 'MATERIAL') (Blender copies all vertex groups onto the separated garment automatically — the whole trick) → purge empty groups → attach_part<donor>__<material>-harvest.glb. /api/wear runs assemble.py fit_part(anchor=mixamorig:Hips). License-gate: exclude actorcore-unreal/. Batch the ~40 donors → ~80-150 pre-weighted staple garments at $0.

  • Graft from proxy-standard: author hide_torso/hide_upperarm/hide_thigh/hide_shin/hide_foot/ hide_scalp vertex groups once, apply an inverted MASK modifier per garment's hides list — replacing the 3mm DISPLACE "puff" hack (blender_ops.py:122-124) — so nude skin stops poking through.
  • Demo: a real jacket (collar/cuff volume) on a mixamorig body bending correctly through a walk clip, no skin poke-through. The only route that looks like a person in actual clothes.
  • Ceiling: garments captive to donor proportions (clip/gap on dissimilar bodies), wardrobe donor-bounded, 65-bone donor vs 22-bone MIRPAMO leaves cuff verts under-driven.

Phase 5 — Dressing Room UI + full export hub (1 week)

Rebuild web/index.html as a real dressing room: vendor three.js r175 (drop the CDN), thumbnail garment grid with slot tabs + tag search + license filter, live click-to-swap composing by reference (never re-bake per click), a 2D-doll preview toggle (reuse doll.js verbatim), save/load/randomize outfits, and an export dropdown (djsim | thriftgod | procity | not-tonight | glb). Add a render_thumb Blender op (EEVEE 256×256 A-pose). Gate every export on license != "red" — the only actual enforcement of "only dressed/green ships."


D. The data model (actual JSON — stdlib files, no DB)

library/index.json written on every mutation, read once by the UI. Replaces scan() (which returns only {name,path,kb,home} and drops PNGs).

Slots & layers

slot  ∈  head | torso | legs | feet | hand_l | hand_r | accessory | bag
layer (draw/stack int; drives 3D normal-push AND 2D doll ORDER):
   skin=0  underwear=10  legs=20  feet=25  torso=30  outer=40  head=50  accessory=60  bag=70

Garment record (library/garments/<slug>.json)

{
  "slug": "coogi-multi-knit", "title": "Coogi multicolour knit",
  "slot": "torso", "layer": 30, "skeleton": "mixamorig",
  "hides": ["hide_torso","hide_upperarm"],
  "tags": ["knit","90s","multicolour","oversized"], "brand": "coogi-parody",
  "license": "green",
  "colorway": { "base": "#8a2b6d", "tintable": true },
  "reps": {
    "flat_png":   "garments/coogi-multi-knit.png",
    "doll_layer": "garments/coogi-multi-knit.doll.png",
    "sprite":     "garments/coogi-multi-knit.sprite.png",
    "glb":        "garments/coogi-multi-knit-harvest.glb",
    "skin_tex":   "garments/coogi-multi-knit.reskin.png",
    "thumb":      "garments/coogi-multi-knit.thumb.png"
  },
  "source": { "prompt": "...", "backend": "flux_local", "seed": 42 },
  "provenance": { "created": "2026-07-24T...", "donor": null }
}

Outfit record (library/outfits/<name>.json)

{
  "name": "raver-fit-01", "body": "bodies/phrtt2_rigged.glb",
  "slots":     { "torso":"coogi-multi-knit","legs":"baggy-jnco","feet":"doc-8eye","head":"bucket-hat" },
  "layerOrder":[ "legs","feet","torso","head","bag" ],
  "anim": "wan_walk",
  "attach": { "bucket-hat": { "bone":"mixamorig:Head","scale":1.0,"y":0.0,"z":0.0 } }
}

The attach block bakes the currently-ephemeral rigid-attach state (index.html:215-225) so hats/props survive reload + export.

Export contracts (verified against each consumer's real loader)

Target Contract Slot map
90sDJsim / thriftgod doll write reps.doll_layer90sDJsim/web/world/media/doll/<slot>_i_<slug>.png; <slug> = re.sub(r'[^a-z0-9]+','_',name.lower()).strip('_'). Self-healing DOLL_ART = drop file + restart. Rsync to ultra. torso→top, legs→bottom, feet→shoes, head→hat, bag→bag
thriftgod / procity racks emit reps.flat_png as fittings.js garment({image}) plane texture / PROCITY StockSlot{kind:'garment'} slot → rack kind:'garment'
not-tonight emit reps.sprite (32×48, quantized to the 14-entry outfits.ts PALETTE) head→hair/accessory, torso→top, outer→outer, legs→legs, feet→shoes
3D GLB (hero/games) assemble(body,{torso:garment}) or fitout/<outfit>.glb → 3GOD depot native mixamorig

Schema is a superset of character_kit_modular/parts_library/library.json → parts flow between benches (anti-silo, not a new competing format).


E. The 2D paper-doll track (first-class, not a fallback)

John explicitly has "2D paper cut-out" garments, they're the cheapest content, and GODVERSE_MAP.md already recommends "clothes cutouts on a hanger card OVER 3D garment mesh." Mostly adoption, not building:

  • Generator exists & is $0: 90sDJsim/tools/gen_wardrobe.py — prompt → flux_local on solid greenbg_remove_localdoll_stage.py onto 704×1408 via measured SLOT_DEF anchors.
  • Battle-tested prompt (build_wardrobe_manifest.py): "paper doll clothing item for a dress-up game, <desc>, 1990 australia, <view>, floating centered on a plain pure white background, painterly stylized 3d game illustration, full item visible, no body, no person, no mannequin, no hanger, no text, no logos." Lift verbatim, incl. per-slot VIEW phrasing.
  • Content on hand: 441 sprites + 321 items + 432 reference JPGs.
  • Upgrade: qwen_layered_local → RGBA layers = the literal "layered paper cut-out" rep, cleaner than the keyer.
  • Prerequisite: fix bug #2 or none of this is ever visible.
  • Honest scope: the djsim doll dresses one fixed 704×1408 front-view silhouette — it cannot dress arbitrary 3D bodies. The dressing room is two products sharing one library: a 2D doll authoring view (djsim/not-tonight) and a 3D stage (reskin/harvest/socket). Toggle honestly; never pretend a flat sprite is dressing the 3D body.

F. Open questions (only John can answer)

  1. wardrobegod vs NPCFACTORY — keep wardrobegod as the JING5 library/authoring/export hub and borrow NPCFACTORY's stage+banks (plan assumes this), or physically merge the two?
  2. Canonical nude base + consent — rig/reskin target: mint from trellis2-bench/anatomy_8000.glb, use phrtt2 (ideal reskin class), or the 43 rigged game-budget set? Hard license!="red" export gate?
  3. MB_TOKEN persistence — OK to add a LaunchAgent / boot-time read from backnforth/.env?
  4. Where the canonical library physically lives — JING5 (author), ultra (doll pipeline runs), or 3GOD depot?
  5. First export target — 90sDJsim (biggest existing 2D consumer, zero-code drop-in) or 3D-in-thriftgod?
  6. Hero garments — accept painted-on reskin for crowd/mid, donor-harvest for staples; invest in socket-torso swap for true hero couture (bigger build, not in this plan)?
  7. Garment LoRA — curate a set + brush_train for on-brand 90s Aussie op-shop garments, or prompt-only for now?

G. Risks & what will look bad (no sugar-coating)

  • Reskin is a painted-on illusion — zero added geometry; unchanged nude silhouette + a jacket print is a dead giveaway at close range; can't layer, can't drape; re-bake to change (runtime swap only toggles already-baked costumes). Single-mesh/UV bodies only.
  • Virtual try-on is a 2D stillmflux_image_edit gives a believable clothed image in ~2 min (great for browsing) but doesn't animate and drifts identity/pose. Preview surface, not hero render.
  • Donor cloth is proportion-captive — weights transfer perfectly, but a suit molded to man_suit_01 floats/clips on a slim TRELLIS figure (no cloth sim). Wardrobe is donor-bounded. 65-bone vs 22-bone leaves cuffs under-driven. Unknown fraction of donors are single-atlas (skin+cloth fused) → won't separate cleanly — real number unknown until triage runs.
  • 3D fit on unstructured topology is per-body & clips — no shared topology → fitted garment doesn't transfer across bodies; expect clipping/sleeve mismatch on non-A-pose bodies; MIRPAMO can candy-wrap joints on non-manifold soup.
  • The 2D doll can't dress the real 3D bodies — one fixed front-view silhouette only.
  • Generation fidelity is prompt-only (no garment LoRA) — exotic garments hit-or-miss.
  • Export writes into live game dirs — needs dry-run + license!="red" gate + write only <slot>_i_<slug>.png (never overwrite generic fallbacks).
  • Overlap with NPCFACTORY is real — build this as the library/export layer NPCFACTORY lacks, not a third isolated dress-up stage.
  • Auto-tagging is not available today (no live vision model) — treat as manual or paid-OpenRouter.
  • Farm GPU = 1 job/node — batch gen queues; push flux/RMBG to the 2nd MODELBEAST node on ultra.

One-liner for John: by end of week you get a real "change clothes + walk" button (reskin + rigid attach + clip playback) and a best-in-class 2D wardrobe that drops straight into 90sDJsim; good-looking real-cloth 3D comes in Phase 4 from harvesting donors you already own; and the thing you'll use every day — the dressing-room library + export hub — is the spine that turns every engine into value for five existing consumers.