Diagnóstico de Errores (Diagnosing Bugs)
Una disciplina para errores difíciles. Salta fases solo cuando esté explícitamente justificado.
Al explorar el código base, lee CONTEXT.md (si existe) para obtener un modelo mental claro de los módulos relevantes y revisa los ADR en el área que estás tocando.
Redactar
Esta habilidad te pide que muestres comandos, salidas y artefactos capturados. Redacta primero cada secreto: escribe <REDACTED> en su lugar. Construye bucles contra variables de entorno, para que la credencial permanezca en el entorno en lugar de en lo que muestras. Los artefactos capturados contienen cabeceras de autenticación: cita solo las líneas que llevan la señal.
Si la salida redactada no es suficiente para diagnosticar el error, dícelo y pregunta al usuario.
Fase 1: Construir un bucle de retroalimentación
Esta es la habilidad. Todo lo demás es mecánico. Si tienes una señal de pasa/falla ajustada para el error (que se vuelve roja con este error), encontrarás la causa; la bisección, la prueba de hipótesis y la instrumentación solo la consumen. Si no tienes una, ninguna cantidad de mirar el código te salvará.
Dedica un esfuerzo desproporcionado aquí. Sé agresivo. Sé creativo. Rehúsate a rendirte.
Formas de construir una, aproximadamente en este orden
- Prueba fallida en la costura que alcance el error: unitaria, integración, e2e.
- Script curl/HTTP contra un servidor de desarrollo en ejecución.
- Invocación CLI con una entrada fija, comparando la salida estándar con una instantánea correcta conocida.
- Script de navegador sin interfaz (Playwright/Puppeteer) que manipule la IU y verifique el DOM/consola/red.
- Reproducir una traza capturada. Guarda una solicitud de red real/carga útil/registro de eventos en el disco; reprodúcela a través de la ruta de código de forma aislada.
- Arnés desechable. Inicia un subconjunto mínimo del sistema (un servicio, dependencias simuladas) que ejercite la ruta de código del error con una única llamada a función.
- Bucle de propiedades/fuzzing. Si el error es "salida incorrecta a veces", ejecuta 1000 entradas aleatorias y busca el modo de fallo.
- Arnés de bisección. Si el error apareció entre dos estados conocidos (commit, conjunto de datos, versión), automatiza "iniciar en estado X, verificar, repetir" para que puedas ejecutar
git bisect run. - Bucle diferencial. Ejecuta la misma entrada a través de la versión antigua frente a la nueva (o dos configuraciones) y compara las salidas.
- Script bash HITL. Último recurso. Si un humano debe hacer clic, dirígelos con
scripts/hitl-loop.template.shpara que el bucle siga estructurado. La salida capturada se retroalimenta a ti.
Construye el bucle de retroalimentación correcto y el error estará 90% solucionado.
Ajustar el bucle
Trata el bucle como un producto. Una vez que tienes un bucle, ajústalo:
- ¿Puedo hacerlo más rápido? (Almacenar en caché la configuración, saltar inicialización no relacionada, reducir el alcance de la prueba).
- ¿Puedo hacer la señal más nítida? (Verificar el síntoma específico, no "no se estrelló").
- ¿Puedo hacerlo más determinista? (Fijar tiempo, inicializar generador de números aleatorios, aislar sistema de archivos, congelar red).
Un bucle inestable de 30 segundos apenas es mejor que ningún bucle; uno determinista de 2 segundos es ajustado, un superpoder de depuración.
Errores no deterministas
El objetivo no es una reproducción limpia sino una mayor tasa de reproducción. Repite el desencadenante 100 veces, paraleliza, añade estrés, reduce ventanas de tiempo, inyecta pausas. Un error con 50% de inestabilidad es depurable; 1% no lo es, así que sigue aumentando la tasa hasta que sea depurable.
Cuando realmente no puedes construir un bucle
Detente y dilo explícitamente. Enumera lo que intentaste. Pide al usuario: (a) acceso al entorno que lo reproduce, (b) un artefacto capturado redactado (archivo HAR, volcado de registros, volcado de núcleo, grabación de pantalla con marcas de tiempo), o (c) permiso para añadir instrumentación temporal en producción. No procedas a hacer hipótesis sin un bucle.
Criterio de finalización: un bucle ajustado que se vuelve rojo
La Fase 1 termina cuando el bucle es ajustado y capaz de volverse rojo: puedes nombrar un comando (una ruta de script, una invocación de prueba, un curl) que ya has ejecutado al menos una vez (muestra la invocación y su salida, redactada), y que es:
- Capaz de volverse rojo: ejecuta la ruta de código del error real y verifica el síntoma exacto del usuario, por lo que puede volverse rojo con este error y verde una vez corregido. No "se ejecuta sin errores"; debe poder detectar este error específico.
- Determinista: mismo veredicto en cada ejecución (errores inestables: una tasa de reproducción alta y fija, según lo anterior).
- Rápido: segundos, no minutos.
- Ejecutable por el agente: puedes ejecutarlo sin supervisión; un humano interviene solo mediante
scripts/hitl-loop.template.sh.
Si te sorprendes leyendo código para construir una teoría antes de que exista este comando, detente: saltar directamente a una hipótesis es el fallo exacto que esta habilidad previene. Sin comando capaz de volverse rojo, no hay Fase 2.
Fase 2: Reproducir + minimizar
Ejecuta el bucle. Obsérvalo volverse rojo mientras aparece el error.
Confirma:
- El bucle produce el modo de fallo que el usuario describió, no un fallo diferente que resulta estar cerca. Error equivocado = solución equivocada.
- El fallo es reproducible en múltiples ejecuciones (o, para errores no deterministas, reproducible a una tasa suficientemente alta para depurar contra ella).
- Has capturado el síntoma exacto (mensaje de error, salida incorrecta, tiempo lento) para que las fases posteriores puedan verificar que la solución realmente lo aborda.
Minimizar
Una vez que se vuelve rojo, reduce la reproducción al escenario más pequeño que aún se vuelve rojo. Recorta entradas, invocadores, configuración, datos y pasos uno a la vez, volviendo a ejecutar el bucle después de cada recorte, y conserva solo lo que es esencial para el fallo.
¿Por qué molestarse? Una reproducción mínima reduce el espacio de hipótesis en la Fase 3 (quedan menos partes móviles por sospechar) y se convierte en la prueba de regresión limpia en la Fase 5.
Terminado cuando cada elemento restante es esencial: eliminar cualquiera de ellos hace que el bucle se vuelva verde.
No procedas hasta que hayas reproducido y minimizado.
Fase 3: Hacer hipótesis
Genera 3–5 hipótesis clasificadas antes de probar cualquiera de ellas. La generación de una sola hipótesis se ancla en la primera idea plausible.
Cada hipótesis debe ser falsable: indica la predicción que hace.
Formato: "Si es la causa, entonces hará que el error desaparezca / lo empeorará."
Si no puedes indicar la predicción, la hipótesis es una sensación: descártala o afílala.
Muestra la lista clasificada al usuario antes de probar. A menudo tienen conocimiento del dominio que la reordena instantáneamente ("acabamos de implementar un cambio en el #3"), o saben hipótesis que ya han descartado. Punto de control barato, gran ahorro de tiempo. No te detengas por ello; procede con tu clasificación si el usuario no está disponible.
Fase 4: Instrumentar
Cada prueba debe mapear a una predicción específica de la Fase 3. Cambia una variable a la vez.
Preferencia de herramientas:
- Depurador/inspección REPL si el entorno lo soporta. Un punto de interrupción supera a diez registros.
- Registros dirigidos en los límites que distinguen las hipótesis.
- Nunca "registrar todo y filtrar".
Etiqueta cada registro de depuración con un prefijo único, p. ej. [DEBUG-a4f2]. La limpieza al final se convierte en un solo filtrado. Los registros sin etiqueta sobreviven; los etiquetados mueren.
Rama de rendimiento. Para regresiones de rendimiento, los registros suelen ser incorrectos. En su lugar: establece una medición de referencia (arnés de tiempo, performance.now(), perfilador, plan de consulta), luego biseca. Mide primero, corrige después.
Fase 5: Corregir + prueba de regresión
Escribe la prueba de regresión antes de la corrección, pero solo si hay una costura correcta para ello.
Una costura correcta es aquella donde la prueba ejercita el patrón de error real tal como ocurre en el punto de llamada. Si la única costura disponible es demasiado superficial (prueba de un solo invocador cuando el error necesita múltiples invocadores, prueba unitaria que no puede replicar la cadena que desencadenó el error), una prueba de regresión allí da falsa confianza.
Si no existe una costura correcta, ese es el hallazgo. Anótalo. La arquitectura del código base está impidiendo que el error sea bloqueado. Señala esto para la siguiente fase.
Si existe una costura correcta:
- Convierte la reproducción minimizada en una prueba fallida en esa costura.
- Obsérvala fallar.
- Aplica la corrección.
- Obsérvala pasar.
- Vuelve a ejecutar el bucle de retroalimentación de la Fase 1 contra el escenario original (no minimizado).
Fase 6: Limpieza
Requerido antes de declarar terminado:
- La reproducción original ya no se reproduce (vuelve a ejecutar el bucle de la Fase 1)
- La prueba de regresión pasa (o la ausencia de costura está documentada)
- Toda la instrumentación
[DEBUG-...]se ha eliminado (filtra el prefijo) - Los prototipos desechables se han eliminado (o movido a una ubicación de depuración claramente marcada)
- La hipótesis que resultó correcta se indica en el mensaje del commit/PR, para que el próximo depurador aprenda