


Corrige la causa raíz en una base de código grande y mantén compatible la API pública
Úsalo cuando una IA modifique una base de código grande y en capas. Mantiene cortos los registros de compilación, rastrea los errores hasta su causa de origen, revisa su propio diff en busca de helpers que solo tapan problemas y escribe un pull request claro.
Para usar estas reglas de IA
## Mantén cortos los registros de compilación y ejecuta el formateador y las pruebas antes de cada commit
- Al compilar, redirige toda la salida de compilación al dispositivo nulo y vuelve a ejecutar el mismo comando en modo detallado solo si falla, de modo que una compilación exitosa no consuma tokens en registros. Por ejemplo: `build-command >/dev/null 2>&1 || build-command`.
- Prefiere las dependencias almacenadas localmente en caché antes que las descargas de red, y habilita una caché de compilación o del compilador cuando esté disponible, para acelerar las recompilaciones incrementales.
- Para compilaciones largas, desatendidas o dirigidas por un agente, desactiva en el momento de la configuración los cuadros de diálogo modales de aborto o de aserción, para que la ejecución no pueda quedar bloqueada esperando a un humano.
- Ejecuta el formateador antes de cada commit y usa su modo de solo verificación para comprobar sin modificar archivos; el código que no cumple con el estilo del proyecto obliga a idas y vueltas de revisión evitables.
- Ejecuta la suite de pruebas desde la raíz del repositorio y añade las pruebas de regresión como archivos dentro del directorio de pruebas del proyecto. Cuando no haya GPU ni hardware especial disponible, usa los modos de prueba por CPU o por intérprete en lugar de omitir cobertura.
- En entornos multi-shell, por ejemplo un shell de Unix que invoca herramientas del host, añade la extensión de ejecutable correcta para que se ejecute el binario del host previsto y no uno de una plataforma equivocada.
## Corrige la causa de origen en vez de añadir guardas
- Apunta a una representación que sea correcta y robusta por construcción, incluso cuando eso implique una reestructuración mayor que el diff más pequeño posible.
- Corrige las causas raíz, no los síntomas. Cuando un error aparece en una fase tardía, como la generación de salida, la causa suele estar más arriba en el flujo: una transformación anterior, una legalización, una especialización, un lowering, o la propia representación de datos. Rastréalo hasta ahí y corrígelo ahí.
- Cuestiona cada cambio antes de conservarlo: por qué es necesario, qué prueba falla sin él, y si esta es la corrección correcta o si el problema está señalando que la representación es defectuosa. Si no puedes nombrar una prueba que falle sin un cambio, probablemente ese cambio no debería existir.
- No enmascares. Una guarda, una comprobación de nulo o un caso especial que disimula una estructura malformada oculta un error de representación, y una guarda que nunca se activa con una entrada correcta es código muerto. Haz que la representación sea correcta para que los consumidores posteriores permanezcan simples.
- Cuestiona la forma de la entrada. Cada vez que manejes una forma particular de entrada, pregúntate si esa forma es en sí correcta, o si en cambio debería corregirse su productor más arriba en el flujo; trátala localmente solo cuando la forma sea una entrada genuinamente válida.
- Prefiere la representación correcta por encima de la distancia de edición. Si dos formas superficiales deberían ser equivalentes, modélalas de manera idéntica, y accede a los datos clave-valor conceptualmente desordenados por clave o rol, en lugar de por posición o índice.
- Mantén un registro de trabajo durante toda la tarea: el problema con un ejemplo que lo motive, los bloqueos, cómo una corrección se encadena con la siguiente, la corrección elegida y por qué se basa en principios, y las alternativas rechazadas. Destílalo en la descripción del cambio y no lo incluyas en el código del commit.
## Revisa tu diff en busca de helpers que solo tapan un mal diseño
- Antes de finalizar un cambio no trivial, revisa el diff en busca de señales de que está compensando una representación defectuosa, y trata estos patrones como señales de alerta hasta que puedas demostrar que pertenecen a esta capa.
- Los helpers personalizados de equivalencia semántica, como nuevas funciones recursivas del tipo are-equivalent o does-match, a menudo significan que se permitió que sobrevivieran dos representaciones alternativas; primero pregúntate por qué una ruta existente de canonicalización, sustitución o igualdad no las hace ya idénticas.
- Crecimiento de helpers sin auditar: trata cada nuevo helper, fallback o función de tipo try como un objetivo de revisión, especialmente uno que exista solo para hacer pasar una única prueba que falla, o que reimplemente parte de un mecanismo ya existente.
- La reconstrucción de sintaxis a partir de semántica, es decir, reconstruir la sintaxis superficial a partir de datos semánticos ya verificados, normalmente significa que el productor conservó la representación equivocada; el valor semántico ya verificado debería ser la fuente de verdad.
- El redescubrimiento de contexto recorriendo el grafo —recorrer cadenas de operandos, referencias o de propiedad (ownership) para recuperar argumentos, claves o padres— suele ser una reparación posterior en el flujo; en su lugar, construye la forma canónica en el productor.
- Parches del lado del consumidor: las fases tardías no deberían parchear formas malformadas provenientes de fases anteriores, y los detalles de representación codificados de forma fija (casos especiales para subclases particulares, nombres mágicos, índices de argumentos u orden de entradas) requieren un invariante sólido y normalmente pertenecen a un límite de construcción canónica.
- Manejo silencioso de formas imposibles: una guarda que devuelve un valor por defecto para una forma fuera de contrato oculta errores; usa un assert para la forma imposible en lugar de aceptarla silenciosamente.
- Comienza la revisión con un inventario de helpers sospechosos que liste cada helper nuevo, con qué mecanismo existente se superpone, y la prueba que falla sin él. Para cada cambio señalado, nombra la forma exacta de la entrada y su productor, decide si esa forma es canónica o una alternativa accidental, intenta primero la corrección del lado del productor, nombra la fuente de verdad existente, y realiza el ejercicio de reversión: elimina el cambio, ejecuta la prueba más pequeña que falla, y rastrea la ruptura real. No conserves un cambio señalado solo porque hace que las pruebas pasen.
## Escribe comentarios con ejemplos y guarda cada dato en un solo lugar
- Escribe los comentarios de las funciones como oraciones completas: indica primero qué hace la función, luego por qué existe si eso no es obvio, e incluye un ejemplo concreto para comportamientos no triviales en lugar de un fragmento escueto.
- Usa ejemplos conversacionales en los comentarios y explicaciones. Prefiere algo como «Considera este ejemplo:» con código real, y luego describe paso a paso qué ocurre, qué pass crea la forma, qué invariante preserva el código local, y qué consumidor depende de ella, en lugar de usar etiquetas abstractas como sustituto de la explicación.
- Reutiliza antes de escribir, y luego extrae la lógica no trivial a un helper nombrado y documentado. Busca primero en las utilidades compartidas; cuando la lógica sea genuinamente nueva, dale un nombre que revele su intención y un comentario de documentación, en lugar de enterrar un cálculo de varios pasos en un bloque en línea.
- Mantén una única fuente de verdad y elimina el código muerto después de una refactorización: mapea o clasifica una cosa determinada en exactamente un solo lugar, y cuando un cambio deje inalcanzable una rama o un helper, elimínalo en lugar de dejarlo atrás.
- Mantén una única representación canónica por valor y verifica el invariante con un assert. Una segunda representación del mismo valor lógico rompe la igualdad, la identidad y la deduplicación; usa un assert en el punto de construcción cuando un invariante garantice que cierta forma nunca se produce para determinadas entradas.
- Falla de forma ruidosa ante una entrada fuera de contrato. Cuando un helper solo es válido para un conjunto restringido de entradas, usa un assert para cualquier cosa fuera de ese conjunto en lugar de devolver silenciosamente un valor por defecto.
## Escribe el pull request en cinco partes e incluye pruebas de regresión
- Formatea el código y luego etiqueta el cambio como no disruptivo (non-breaking) por defecto, o como disruptivo (breaking) cuando altere una interfaz binaria o el comportamiento del lenguaje.
- Incluye pruebas de regresión que habrían fallado antes de la corrección.
- Escribe la descripción con una estructura fija de cinco partes: motivación (el problema con un ejemplo concreto que lo motive), solución propuesta (el enfoque y por qué es el más apegado a los principios), resumen del cambio (una lista de los archivos o áreas tocadas y qué hace cada una), conceptos y vocabulario (un breve glosario solo de los términos específicos de la base de código o sutiles en los que se apoya el informe, no de conceptos básicos ya conocidos), y un informe del proceso.
- En el informe del proceso, explica cada cambio con una razón lógica. Para un problema en cascada, da la prueba que lo motiva y un rastreo del código que nombre las funciones exactas involucradas, no solo una descripción. Para cualquier cambio que proteja o trate como caso especial una forma de entrada, indica si esa forma es una entrada válida o si su productor debería haberse corregido, de modo que quien revise pueda confirmar que la corrección está en la capa correcta.
- Escribe pensando en quien revisa y no tiene tu contexto: parte de un ejemplo concreto, incluye el fragmento relevante en lugar de solo nombrar un tipo o una función, y explica los pasos en orden.
## Añade al final de enums e interfaces para no romper la API pública ni el ABI
- Trata cada cambio a un header de una API pública como si necesitara preservar la compatibilidad binaria y de código fuente para los llamadores ya compilados contra una versión anterior.
- Enums: nunca insertes un nuevo enumerador en medio de un enum existente, porque la inserción desplaza todos los valores posteriores y rompe silenciosamente a los llamadores que lo almacenaron o compararon. Añade los nuevos enumeradores justo antes del miembro de conteo terminal o centinela, con un valor secuencial explícito, y para retirar uno, renómbralo a una forma de marcador de eliminado manteniendo su entero original, sin reutilizar nunca un valor retirado.
- Las tablas de métodos virtuales en interfaces de estilo COM tienen un diseño fijado por el orden de declaración, así que nunca reordenes métodos virtuales, nunca cambies la firma ni la convención de llamada de un método, nunca insertes un nuevo método en medio (añádelo al final, antes de la llave de cierre), y nunca elimines un método: reemplaza su cuerpo con un stub de "no implementado" y conserva la declaración.
- Evita extender una interfaz pública existente in situ cuando los clientes puedan implementarla o consultarla por identificador; prefiere añadir una nueva interfaz derivada o versionada con su propio identificador, manteniendo la original compatible para los llamadores existentes.
