Audité a mis guardianes: seis llevaban meses sin hacer su trabajo
En resumen
- El 7 de septiembre de 2026 audité a los guardianes automáticos de mi servidor: el podador de memoria, el reparador de crons, el auditor semanal, el verificador de la web.
- Desde agosto tengo una regla: si algo funciona pero no produce efecto, está roto.
- El script encargado de reparar mis crons cuando se rompen tiene un error que solo aparece cuando hay algo que reparar. Usa logging.info sin haber importado logging.
El script encargado de reparar mis crons cuando se rompen tiene un error que solo aparece cuando hay algo que reparar. Usa logging.info sin haber importado logging. En Python eso revienta con un NameError, y la línea que revienta está dentro de la rama que solo se ejecuta si el sistema detecta problemas graves.
O sea: mientras todo va bien, el socorrista flota en la piscina con muy buena pinta. El día que alguien se ahoga, se ahoga él.
Llevaba tiempo así. Corriendo cada día, saliendo con exit 0, sin que nadie lo notara. Y era solo uno de los seis.
Desde agosto tengo una regla: si algo funciona pero no produce efecto, está roto. Me costó 17 días con un cron muerto y un dashboard en verde aprenderla, y otras tantas veces que mi agente me jurara que algo estaba arreglado cuando no lo estaba.
Hoy la apliqué a los guardianes. Los que vigilan que todo lo demás funcione: el script que poda mi memoria, el que repara crons, el auditor semanal, el verificador de la web. Cada uno con su misión, cada uno con su panel, todos reportando ok.
La primera pasada la hice como manda el manual: grep para confirmar que las cosas existían, códigos de salida, logs. Todo en verde. Y ese fue el primer hallazgo de verdad: cuando auditas con las mismas herramientas que mintieron, la auditoría hereda la mentira. Tuve que cambiar de método para que empezaran a aparecer los muertos.
El podador que no podaba
memory-pruner.py se encarga de podar la memoria de mi asistente para que no se desborde. Declara la ruta del archivo que debe vigilar y el límite de caracteres. La documentación dice que gestiona USER.md, y todo el mundo da por hecho que la gestiona.
La realidad cabe en un comando: grep -c "USER_FILE" devuelve 1. Una sola aparición, la de la propia definición. En ningún otro sitio del script se lee esa constante. La ruta existe. El límite existe. La intención existe. La lógica, no.
El archivo llevaba tiempo al 99%, 1360 de 1375 caracteres, sin que nadie lo contuviera. Y el verificador semanal añadía el toque: "el pruner lo bajará". Mentira con buena letra: el pruner no bajaba nada. Un aviso que promete un arreglo inexistente es peor que no avisar: se lee, se asume gestionado y se olvida para siempre.
El mismo script escondía el segundo bug. Calculaba una puntuación para cada entrada de memoria y las clasificaba por importancia. Y a la hora de recortar, las eliminaba en orden de aparición. grep -c "sort" devuelve 0: en todo el archivo no hay ni un ordenamiento. Las entradas recién escritas, las más valiosas porque acaban de nacer de una conversación, eran las primeras en caer. Prioridad decorativa: se calcula, se admira, no se usa.
El socorrista que no sabía nadar
El tercero es el que abre este artículo. cron-self-repair existe para arreglar crons cuando algo va mal, y su rama de emergencia, la que decide qué hacer si hay problemas graves, nunca se había ejecutado. Nunca. Todas las ejecuciones pasaban por el camino feliz, donde no hay nada que reparar.
Cuando fui a mirar esa rama, descubrí que la primera línea era logging.info sin import. La primera vez que el sistema tuviera un problema de verdad, el reparador habría muerto antes de reparar nada.
Esto tiene nombre: probar solo el camino feliz. El código que te salva solo corre cuando todo se rompe. Si no le inyectas un fallo a propósito, jamás sabrás si sabe nadar.
El auditor que certificaba salud sin mirar
El cuarto era casi cruel. drift-auditor corre cada semana y reporta "memoria reparada". Ejecuta el pruner sobre USER.md. Y el pruner, como acabamos de ver, no toca USER.md. Éxito falso por diseño: cada domingo, durante meses, un auditor certificando que un muerto respira.
El quinto vivía en la web. El verificador del sitemap comparaba el número de URLs contra un 435 escrito a mano. Cada vez que publicábamos un artículo, la cifra cambiaba y el verificador daba FAIL. Un guard que se rompe con la actividad normal no es un guard: es ruido. Y cuando un guard se vuelve ruido, dejas de leerlo. Justo cuando falla de verdad.
El sexto no se ve con un grep
Los cinco primeros tenían la misma firma: código que declaraba hacer algo y no lo hacía. El sexto es distinto, y fue el más difícil de ver: cada pieza funciona, y el circuito sigue abierto.
Tengo un estratega de contenidos que cada semana decide qué artículos hay que refrescar y escribe sus decisiones en un archivo compartido. El cron editorial debería leerlas antes de publicar. No las lee: en su prompt no aparece ni una vez la palabra que el estratega usa para nombrar lo que escribe. Tres ciclos proponiendo, cero ejecutadas. Objetivos sin tocarse durante 67 y 99 días, según el artículo.
Lo más retorcido es que nadie mentía. El productor reportaba éxito: escribió el archivo. El consumidor reportaba éxito: publicó su artículo. El fallo vivía en el hueco entre los dos, y la documentación del sistema afirmaba que ese bucle estaba cerrado.
Ahora lo verifico por el efecto: si el estratega manda refrescar un artículo, la pregunta no es si corrió, sino si cambió la fecha de modificación del artículo que mandó cambiar.
Cómo auditar sin heredar la mentira
Ninguno de los seis sonaba. Todos existían, corrían, salían con exit 0 y reportaban ok. La telemetría medía que estaban vivos; ninguna medía si cumplían. El método que me funcionó cabe en cinco frases:
- Existir no es funcionar. Un grep de presencia es una hipótesis, no un hallazgo: cuenta los usos, no las definiciones. Una constante con una sola aparición está muerta.
- exit 0 no es una reparación. Mide el estado antes y después. Que el proceso no haya reventado no significa que haya arreglado nada.
- Prueba la rama de fallo. Si el código que salva solo corre cuando todo se rompe, inyéctale un fallo y míralo. El camino feliz no prueba nada.
- Todo aviso debe nombrar a quien repara. Si no hay nadie, que lo diga. Un guard que promete un arreglo inexistente es papel mojado con buena intención.
- Productor y consumidor se verifican por el destino. ¿Cambió el archivo que debía cambiar? El éxito de cada extremo no cierra el circuito.
Ninguna de estas frases es nueva. Las había leído todas. La diferencia es que hoy las he ejecutado, y han aparecido seis muertos que el panel de control daba por vivos.
Conclusión
Lo que más me ha removido no es que los bugs existieran. Bugs hay siempre, y se arreglan. Es que los seis tenían el visto bueno del sistema: existen, corren, salen con exit 0, reportan ok. Ninguno mentía a propósito. Todos compartían el mismo agujero: medir que algo vive no es medir si cumple.
Si tienes un backup nocturno, un pipeline o un informe que nadie abre, la pregunta no es "¿corrió?". Es "¿cambió algo?". Y si no puedes responder mirando el estado de la cosa vigilada, no tienes un backup: tienes un directorio con archivos. Ya escribí sobre agentes que se reparan solos; hoy he aprendido que los reparadores también necesitan que alguien los mire a los ojos de vez en cuando.
He tardado en aprenderlo, y ha hecho falta una auditoría entera para que me entrara. Ahora, cuando un panel me dice que todo va bien, voy a mirar el efecto, no el informe. La máquina puede gestionarse sola, es el trato que hice conmigo mismo. Pero los guardianes necesitan un guardián. Resulta que ese soy yo.