# 04 — Decisiones

Por qué se hizo lo que se hizo. Para no volver a discutirlo cada seis meses.

## Del prompt

| Decisión | Por qué |
|---|---|
| Tramos separados por `BREAK` | Aísla la pose de la identidad. Sin `BREAK` el bloque compite con el pelo y la ropa del personaje y la postura se pierde |
| El hombre se describe poco | La especificación larga lo deformaba. Cuanto menos, mejor |
| Sin rasgos masculinos | Se intentó hasta `(faceless male:1.4)` y se aceptó: "con que no se identifiquen ojos o rasgos muy marcados sirve" |
| En POV el hombre es `1boy, muscular body` | `(faceless male:1.4)` llegaba a crear un segundo hombre en POV |
| `1boy` una sola vez | Repetido lo lee como dos hombres. Confirmado a mano |
| Fuera `age up, mature female` | Forzaba busto maduro, incompatible con personajes como Furina |
| Fuera los tags de cuerpo completo | Al alejar la cámara el detalle cae mucho. **Contradice** una medición anterior: el freno mejoró el encuadre en Raiden y Yelan |
| `nude` forzado si el bloque no trae prenda | Algunos LoRA traen ropa horneada (Yelan en bikini) |
| La desnudez va con la chica | Es su estado de ropa, no de la pose |
| `solo` fuera en escena de pareja | Pelea con la pose y hace ignorar al hombre |
| La escena se queda dentro del bloque | Partirlo exigiría decidir qué tag es "escena" y cuál "pose". El `BREAK` de escena queda libre |

## De los bloques

| Decisión | Por qué |
|---|---|
| Fuera identidad y TODA la ropa | El outfit lo pone la línea del personaje. Ropa por color (`green_skirt`) no; prenda genérica tampoco |
| Se conserva la anatomía | `pussy`, `breasts`, `ass` hacen falta para que la pose sea la que es |
| Se conservan los conteos | El MASTER los necesita para las poses de dos personas |
| Severidad por tags, no por carpeta | Hay spicy en `NSFW/` y explicit en `SFW/` |
| Un bloque = una línea = una opción de wildcard | No se trocea ni se recombina. Se prueba entero |
| `all_fours` fuera, sustituida por contexto | Empuja la pelvis. `doggystyle` si hay penetración, `doggy` si no |
| `spread_ass` a 0.6 | Es el tag que de verdad parte la pelvis, no `ass_focus` como se creía |
| Descartar por nombre de `.txt` de origen, no por índice | El índice cambia según el pool que mires; el nombre no |
| Lo pendiente no se tira | Va a `bloques_descartados.txt` con `[PENDIENTE]` y su motivo |

## De la limpieza de personajes base (accesorios/ropa de cuerpo)

| Decisión | Por qué |
|---|---|
| Accesorios de **cabeza = identidad**, no se tocan | Sombrero, lazos, cintas, diademas, aureolas, orejas, aretes identifican al personaje (Belle conserva `letter hairclip` + `earrings` y sale limpia) |
| Solo se limpia la ropa/accesorio de **cuerpo** | El outfit lo pone el bloque de pose; el personaje base nace desnudo |
| Prenda **en la línea** → quitar el tag (`clean_accesorios.py`) | Belle / Mina / Delia / Domina salieron limpios solo limpiando la línea (16-10-2026) |
| Prenda **horneada en el LoRA** → negative dirigido + LoRA a ~0.6-0.7 | Borrar el tag no basta: Venti seguía con el traje de Arconte. Con negative dirigido y `<lora:VentiIL:0.6>` salió desnudo y mantuvo identidad |
| Peso **por tanteo, de décima en décima; el más alto que ya sale limpio** | training_8576657: `1`→armadura, `0.8`→queda gorget, `0.6`→limpio pero pierde el adorno de cabeza; `0.7` = desnuda **con** identidad intacta |
| Accesorio de cuerpo **inseparable**: si bajar el peso degrada la identidad o es firma del personaje, se **conserva** | Furina (chistera), Angewomon (sash rosa + collar) y Ellen Joe (collar de pinchos): persisten a 0.4-0.6; se fija el peso mínimo que mantiene la identidad y se documenta |
| **Revisar tags + LoRA ANTES de generar** (pre-limpiado por tags) | Si la ropa ya se lee en la línea, quitar el tag basta: se ahorra el render "antes" en la GPU. Una sola corrida `--nude` confirma (tanda 8) |
| Pendientes por **heurística regex + confirmación visual** | `pendientes_cuerpo.py` propone candidatos; al 17-10-2026 **0 de 216** (73 curados, tras cerrar A/B/C a mano). La imagen decide si el tag es de cuerpo o de cabeza |
| Accesorio "relleno" que aparece **solo con piel** (no horneado) | Sparkle: la campanilla sale en desnudo/bikini pero **NO** con uniforme escolar (medido con `--outfit`). No se repele con negative ni bajando peso → se conserva como **excepción de identidad** |

## Del proceso

| Decisión | Por qué |
|---|---|
| Regenerar en `REGEN/`, no sobre `APROBADO/` | Poder comparar la versión buena con la nueva antes de decidir |
| El regenerador usa el mismo ensamblador que el generador | Manipular el prompt viejo con regex ya dio dos bugs; así es idéntico por construcción |
| Auditar a ojo, no con script | Un bloque no se aprueba por tener los tags correctos, sino porque la imagen sale bien |
| Aviso por **Telegram por tanda, deduplicado**, + cierre final | Mejor supervisión sin spam: `avisar_tanda()` (`test_accesorios.py`) avisa 1 vez por tanda y **no repite en recambios** (centinela `Output/.tandas_avisadas`, dedupe por claves ya avisadas); al empezar tanda con claves nuevas vuelve a avisar. Cierre `send_cierre()` (`pendientes_cuerpo.py`) cuando quedan **0 auto-curables**, con `✅ Check listo` (centinela `Output/.cierre_enviado`). El aviso de **fallo** se mantiene. Token/id solo en `.env`; si falla el envío, no rompe la corrida |

## Del banco de poses y los batches (26-10-2026)

| Decisión | Por qué |
|---|---|
| El banco se amplía con **corpus Danbooru offline**, no inventando poses | 8.834 posts cacheados → 675 tags de pose distintos. Las 24 candidatas salieron de ahí y los 62 tags usados se validaron contra `Danbooru tags.csv` |
| **Una pose por imagen** en la calibración, **una semilla distinta por pose** | Con la misma semilla dos poses se comparan mal: el azar del seed se confunde con la calidad de la pose |
| Semilla `-1` (aleatoria) como forma general; `--seed-base` solo para reproducir | Forge devuelve la semilla real y se guarda en el nombre y el manifest: reproducible sin fijarla de antemano |
| **La pose no dicta la ropa** | `bare_shoulders` en `pb0003` desnudaba los hombros del outfit. El estado de ropa lo pone el outfit (`dress`), no la línea de pose |
| Todo outfit de torso lleva su **tag de manga explícito** | `sweater` sin `long sleeves` + `arms_up` = suéter sin mangas y espalda al aire. El modelo rellena el hueco que no le das |
| El reparto de poses del lote es **sin reemplazo y manual** | El contador de reutilización de `pack_builder` es por personaje, no global: dejándolo a él, dos personajes pueden repetir pose |
| **Formato vertical ≠ formato horizontal** | 5 poses correctas no se leen en 832×1216. Se aprueban en vertical y se mapean para un pase horizontal futuro; no se regeneran |
| Un defecto aceptado se **documenta**, no se esconde | `pb0009` (cowgirl sin hombre) se queda con su ficha y su arreglo mapeado. Regenerar sin cambiar la causa gasta GPU |

## Errores que conviene no repetir

- **`1girl` + `from_behind` sin `solo` = hombre intruso.** `pb0023` sacó un
  oficinista en una escena que debía ser individual. Afectaba a **6 poses**
  (`pb0019`, `pb0022`, `pb0023`, `pb0024`, `pb0028`, `pb0030`). Las que ya
  llevaban `solo` salían limpias. En cuanto una pose mira hacia atrás, `solo`
  deja de ser redundante.
- **`backless` no existe como tag Danbooru.** El negative dirigido se escribió
  con `bare_back`, `off_shoulder`, `sleeveless`, `strapless`, que sí existen.
  Un tag inventado en el negative no hace nada y **no avisa**.
- **El manifest combinado necesita el prefijo del personaje.** `generate()`
  escribe `file` como `img/<nombre>`; al fusionar en `manifest_all.json` hay
  que anteponer `<clave>/`. Sin eso, las hojas de contacto revientan con
  `FileNotFoundError` (bug real de `regen_batch_items.py`, ya corregido).
- **Un regen de un solo job cambia la cámara si no la restauras.** El path
  legacy asigna cámara por índice de job. Con un único job el índice es 0 y el
  pool devuelve otro ángulo: hay que reponer `job.camera` desde el registro.
- **Regenerar antes de corregir el dato.** `regen_batch_items.py` reconstruye
  el prompt desde la línea **actual** del banco y el outfit **actual** del tema.
  Si primero no se arregla el banco o `themes.json`, la imagen nueva repite el
  defecto con otra semilla.
- **`||` en un regex alternado.** Una alternativa vacía matchea *todo*. Pasó
  aquí: mandaba el 100% de los tags a `basura` sin error ni aviso. Síntoma:
  suddenly 0% conservado.
- **Un tag en dos listas.** El primer filtro que lo encuentra gana el resto.
  Pasó con `solo_focus` en `KEEP_COUNTS` y `DROP_CAMERA`.
- **Doble pipe al cortar un tramo de texto.** Se perdía la coma y quedaba
  `broad shouldersnude`. Passó al recortar el tramo del hombre.
- **Verificar los detectores antes de fiarse.** Dos falsos positivos seguidos:
  un regex con `\s*` que marcaba `, nude` normal, y contar `1girl` dentro de
  `1girl solo`.
- **Dos numeraciones distintas para lo mismo.** `bloques_indice.txt` da el índice
  **GLOBAL** (sobre `bloques.txt`), pero `bloques_{sev}.txt` está ordenado por
  **posición dentro de su severidad**. Indexar un pool de severidad con el
  índice global devuelve el bloque equivocado sin dar ningún error. Pasa dos
  veces en una misma sesión: primero en un script de comprobación y luego en el
  regenerador. **Enumera dentro del pool filtrado, no indexes por el global.**
- **El manifest guarda el bloque viejo.** `manifest_*.json` congela el bloque
  tal como estaba al generar la imagen. Si después se limpia el bloque (por
  ejemplo quitando `solo_focus`, que rompía la escena), regenerar desde el
  manifest **reintroduce el tag eliminado**. El bloque bueno está en el pool:
  `wd14_blocks.py` manda sobre el manifest.
- **`regen_aprobados.py --variations N`** añade sufijo `_v1`, `_v2` al nombre.
  Sin sufijo, la segunda variación pisa la primera.
- **Auditar la imagen con el prompt que la generó.** Una imagen antigua con un
  prompt antiguo no vale: se llegó a tener por "fallida" algo que ya se había
  corregido.
- **`no X` en el POSITIVE refuerza X** (09-10-2026, piloto de accesorios). Con
  Furina, `no thigh strap:1.3` en el positivo no quitó la banda del muslo y de
  paso le inventó un collar y dos gemas en el pezón. El text encoder no procesa
  la negación, solo lee el concepto. Movido al **negative prompt** (sin pesos:
  el negative ya es una instrucción) desaparecieron los tres. Regla general.
- **Regex de accesorios: `ribbon`/`bow`/`ascot` a secas no son decidibles.**
  Rem tenía `purple ribbon` (pelo) y `black ribbon` (cuello) en la misma línea;
  Furina, `hat bow`/`blue bow` (pelo) y `ascot`/`ribbon` (cuello). Un personaje
  no es "de bow" ni "de cuello": es ambos. **Por personaje y hardcodeado, con
  el preview `.jpg` citado como fuente.**
- **Los tags de `personajes.txt` usan espacios tanto como guiones bajos.**
  `bare shoulders`, `wrist cuffs`, `top hat`, `head wreath`. Un regex escrito
  solo con `_` no matchea nada y **falla en silencio**.
- **`sleeveless` es lo contrario de `sleeves`.** Limpiando ropa, borrarla haría
  aparecer mangas donde el personaje no las tiene. Igual que `headdress`, que es
  cabeza y no ropa.
- **Auditar con ropa esconde el problema.** Con `jeans, white blouse`,
  Seed parecía perfecta; forzada a desnudez se vio que `clover hair ornament`
  se colorea en piel (pezón y pubis). Para auditar una línea de personaje, la
  escena buena es `--nude`, no el outfit.
- **Un peso dentro del bloque no llega.** `assemble_prompt()` llama a
  `strip_weights_of()`, que borra los `(x:1.3)`. Los refuerzos que necesiten
  peso van en un tramo aparte o en el negative.