# Errores obvios que nos perjudicaron, y cómo los evitamos ahora

> Compañero de `PARIDAD-ISAAC-UNITY-INDICE.md`. Cada entrada: qué pasó, qué costó, la regla que salió.
> Solo errores que se repitieron o costaron horas; el catálogo completo está en `DECISIONES-DOLL.md`.
> Estado al 2026-09-17.

## A. Errores de rótulo (decir que algo es lo que no es)

| # | Qué pasó | Qué costó | Cómo lo evitamos |
|---|---|---|---|
| A1 | Se llamó "entorno" a un lector de contrato y luego a un lazo sin recompensa. | Dos ciclos de trabajo sobre algo que no ejecutaba reset ni step. | Un artefacto se rotula por lo que un raw del motor demuestra que ejecutó. "Listo para X" exige el raw de X. |
| A2 | Un literal hardcodeado (`physicalResetNoExtraSimStep: True`) viajó en el raw como si fuera medición durante muchos ciclos. Al medir, había 3 ticks, y en otra función. | Tres afirmaciones sin medir; discusión de causas sobre un dato inexistente. | Nada que afirme un hecho físico va al raw sin un contador o readback. Los literales se clasifican y se marcan. |
| A3 | El video de Isaac rotulaba "objetivo 39/59/115/155 (validación)" y corrió un solo objetivo de entrenamiento cuatro veces. | Un video no comparable entregado; re-captura. | El rótulo se lee del motor, no del argumento. Detector: cuatro mínimos idénticos a 18 decimales; una coincidencia imposible es tan informativa como una discrepancia. |
| A4 | El descriptor de salida decía "miembros RUN resueltos" y era copia byte a byte del de entrada con cinco `PENDING`. | Un artefacto de identidad que no identifica. | La identidad de la corrida es el raw. Ningún consumidor lee el descriptor de salida hasta que se escriba de verdad. |
| A5 | Campos de procedencia heredados de la corrida anterior ("corrida corta, 50 updates") en la referencia P0 de la corrida larga, y en el auditor de Unity. | Rótulos falsos que viajan entre equipos. | Los consumidores deciden por el SHA del archivo en disco, nunca por un rótulo del JSON. Cada corrida regenera su procedencia. |

## B. Errores de medición (números que no medían lo que decían)

| # | Qué pasó | Qué costó | Cómo lo evitamos |
|---|---|---|---|
| B1 | Umbrales absolutos (1e-6 m) para comparar poses: rechazaban lo exacto a 33 m y aceptaban corrupción a 1.5 m. | Tres rondas de compuertas inútiles. | Presupuestos por representación (ULP) y compuertas que predicen la palabra exacta. |
| B2 | "≤ 1 ULP" como compuerta: una corrida legítima dio 4 ULP. | Falsos negativos. | Predicción del valor por el mecanismo (viaje por el centro de masa), no tolerancia genérica. |
| B3 | 250.84 tomado como retorno inicial R₀: era el retorno tras 10 updates (el campo vecino `updateIndex` no se miró). Fue a un descriptor sellado y desplegado. | Un umbral mal derivado; versión de paquete extra. | R₀ se mide en el update 0 de la propia corrida, antes del primer aprendizaje. Una cita verifica que el valor está, no que signifique lo que se dijo. |
| B4 | "Ganancia 3.96, ×4 por paso": era una secante entre amplitudes; la incremental decae a 0.23. | Justificación falsa publicada y difundida. | Una magnitud de amplitud no es sensibilidad. La sensibilidad se mide con Δ pequeño sobre datos reales (salió p50 4.27). |
| B5 | Prueba de lotes con observación sintética y pesos que no eran los de la corrida: la monotonía "4→8→12 ULP" era artefacto. | Una conclusión ("1024 es el peor caso") que era falsa: el peor caso es lote 100. | Las pruebas de la red usan observaciones reales y los pesos exactos de la corrida (por SHA). |
| B6 | Ratios de sensibilidad por componente de hasta 3.7e4: el denominador era el ULP de un valor cercano a cero. | Casi se publicó. | Solo se citan cocientes con denominador homogéneo; los por componente ordenan, no acotan. |
| B7 | El criterio de parada miró el retorno (saturado contra su techo) mientras la métrica de tarea seguía mejorando a buen ritmo. | La corrida paró en el primer punto posible con pendiente. | El criterio de la segunda corrida mira la métrica de tarea sostenida, no solo el retorno. Se instrumentó C2 (mejor error por episodio) antes de la corrida: sin ella se habría leído "no aprendió". |

## C. Errores de instrumento (compuertas que no podían fallar)

| # | Qué pasó | Qué costó | Cómo lo evitamos |
|---|---|---|---|
| C1 | Un comparador escribía "IDÉNTICOS" al comparar `None` con `None` cuando las sondas fallaban. | Un falso positivo en la compuerta que habilitaba el lanzamiento. | Toda compuerta se prueba contra una entrada que debe hacerla fallar, antes de usarla. |
| C2 | Un generador de controles negativos tenía hardcodeados la fila y el límite viejos: el negativo "pasó" y no acreditaba nada. | Un control que no controlaba. | El elemento y el límite del negativo se derivan del raw que se audita. |
| C3 | Cuatro entornos con el mismo input tratados como cuatro observaciones. | Determinismo que no acreditaba nada. | El control debe variar la variable (sigue al valor / sigue al eje). |
| C4 | El vigilante mataba "todo python salvo ComfyUI" y se incluyó a sí mismo. | No escribió la medición post-cierre. | Una lista por negación protege lo que nombra y nada más: excluir el propio PID y el árbol propio. |
| C5 | El validador de esquema dio "0 errores" sobre un YAML con claves repetidas (YAML se queda con la última sin avisar): se perdió un campo en silencio. | Un valor superado apareciendo junto a una razón que hablaba de otro. | Un validador acredita lo que comprueba, no el archivo entero. Se busca claves duplicadas y se lee el raw, no lo que uno cree haber escrito. |
| C6 | La observación del paso 0 de cada evaluación era la cacheada del último paso de entrenamiento, no la del objetivo asignado. | Una acción de 240 por episodio calculada sobre otro estado, en las 11 evaluaciones. | Tras asignar objetivos se reconstruye la observación desde el estado. Detector: dos corridas "idénticas" que no dieron bit a bit. |

## D. Errores de física / API que costaron construcciones

| # | Qué pasó | Qué costó | Cómo lo evitamos |
|---|---|---|---|
| D1 | Reescribir q/qdot con sus propios valores dejaba los bits iguales pero cambiaba la evolución. | El bit a bit del reset se rompió dos veces. | El reset escribe solo las filas de los entornos hechos, por índices, en los cinco tensores. |
| D2 | `app.close` de Kit no retorna: el proceso queda vivo con la GPU tomada. | Corridas que "no terminaban" y mediciones post-cierre imposibles. | El raw se sella antes de `app.close`; un vigilante externo mata el huérfano después del sello y escribe el sidecar. |
| D3 | Importar onnxruntime dentro de Kit produce violación de acceso. | Dos construcciones perdidas. | La traza P1 va en dos etapas: torch dentro de Kit, onnxruntime en proceso aparte, comparadas por fila. |
| D4 | Dos construcciones de 1024 entornos perdidas por un `IndexError` que N=4 habría mostrado en segundos. | Dos horas de GPU. | Nada corre a 1024 sin pasar de punta a punta a N=4 con el mismo script. |
| D5 | En Unity, escrituras a la raíz antes de activar el cuerpo se perdían (iteraciones, profundidad de penetración, inmóvil). | Configuración que no se aplicaba. | Todo ajuste de raíz se aplica y se verifica después de la activación. |

## E. Errores de proceso (los más caros por repetidos)

| # | Qué pasó | Qué costó | Cómo lo evitamos |
|---|---|---|---|
| E1 | El ejecutor terminaba su turno "ocioso" con trabajo pendiente ("Sigo…" y nada más). Ocho veces. | Ciclos de 20 minutos perdidos; la corrida larga corrió dos horas sin ningún retorno y el R₀ lo leyó otro del disco. | Un turno no termina con trabajo pendiente salvo bloqueo contractual o corrida en vuelo con vigía. Encargos de un solo entregable. La excepción "puede quedar ocioso entre R1 y R3" no incluye R1 ni R3. |
| E2 | Mensajes pequeños en cola se reordenaban y duplicaban; un mensaje decía una cosa y el siguiente la corregía. | Confusión en tres chats. | Un mensaje consolidado por chat y por ciclo, con visto bueno humano. Tope de 40 líneas. Sin acuses. |
| E3 | Hashes de git (`hash-object`) publicados sin rótulo junto a SHA-256 crudos. | Casi se leyó como diferencia real entre local y remoto. | Cada SHA se publica con la pregunta contra la que se lo comparó. |
| E4 | Dos copias del paquete en la máquina remota; una quedó vencida y se llamaba "el desplegado". | Riesgo de lanzar contra la copia equivocada. | Las órdenes citan rutas completas. Un nombre en singular que apunta a varios objetos es una trampa. |
| E5 | Contratos y consultas antes de correr lo simple: el contrato P2 tuvo cuatro versiones antes de ejecutar nada; consultas al Crítico por redacción. | Horas de proceso sin medición nueva. | Correr primero lo que ya tiene insumos (lazo abierto). Crítico solo en compuertas de fase. Registro de decisiones y defectos, no de lecciones. |
| E6 | Defectos conocidos acumulados como "pendientes" en lugar de corregirse juntos. | Cada medición nueva tropezaba con uno viejo. | Una versión de paquete con todos los defectos conocidos en un solo delta, antes de la siguiente fase. |

## F. Lo que funcionó y hay que conservar

- Medir antes de diseñar: la medición invalidó dos contratos antes de que costaran GPU.
- Predicción sellada antes de correr (mtime verificable) en vez de retrodicción.
- Controles negativos reales y verificación cruzada entre dos máquinas y dos operadores.
- Un Crítico adversarial con la tabla completa: retiró argumentos del Arquitecto, del coordinador y del ejecutor.
- Sellar lo que se consumió y verificar consumido = sellado antes de cada veredicto.
- Un fallo bien reportado valió más que un pase forzado, todas las veces.
