Santiago Mansilla.

Lo que se rompe cuando el loop trabaja solo (parte 2)

Cuatro fallos medidos operando una agent box 24/7: credenciales que se pisan, errores que el móvil no enseña, el OOM-killer y la doctrina ganado-no-mascotas.

Santiago Mansilla 7 min de lectura

Un loop de agentes que trabaja solo durante horas no falla como el software que tiene un humano delante: falla en silencio, y el silencio se parece mucho al éxito. En la primera parte de esta serie monté la arquitectura de una agent box —demonio saliente, motor de worktrees propio, cero puertos abiertos— y prometí contar la otra mitad. Esta es: lo que se rompió con la caja encendida semanas enteras y agentes trabajando dentro a todas horas.

Que un loop sin supervisión entregue trabajo grande ya no es la duda: la reescritura de Bun de Zig a Rust salió en 11 días de trabajo agéntico contra los 1-2 años estimados a mano. La duda es la otra: cuando nadie mira, ¿qué se degrada primero? En mi caja, la respuesta no fue el modelo ni el código que escribe. Fueron cuatro cosas más aburridas: una credencial, un canal de errores, dos valores por defecto y mi propia manera de arreglar máquinas. Las cuatro, reproducidas y diagnosticadas; ninguna avisó al romperse.

Dos procesos legítimos bastan para corromper una credencial

El peor incidente hasta la fecha se anunció como un reinicio cada 15 segundos en un proceso que lleva encendido las 24 horas: el bridge —el demonio del server que mantiene la conexión saliente con Anthropic y sostiene las sesiones— entraba en bucle de arranque y muerte con un único mensaje, You must be logged in. Ningún atacante, ningún token caducado. La causa: el bridge se autentica con la credencial del seat —la plaza de la suscripción de su dueño— y un cron nocturno usaba esa misma credencial para su proceso batch.

El mecanismo es una carrera sobre un fichero. Ambos clientes refrescan el token OAuth cuando expira: el batch lo rota primero, el bridge —que guarda su copia en memoria— escribe encima la versión vieja, y la credencial del disco queda corrupta para los dos. Es el tipo de fallo que solo aparece con el tiempo: funciona todo, durante días, hasta que los relojes de refresco se cruzan. Y quien lo sufre no es el cron que lo causó, sino la sesión que intentas abrir desde el móvil al día siguiente.

La acción concreta: una credencial por proceso, sin excepciones. El bridge con el login de su dueño; cada cron con su propio token de claude setup-token en su .env con permisos 600. Audita el tuyo con este criterio: si no puedes revocar un proceso sin tumbar otro, hay dos cosas compartiendo una identidad que no deben compartir.

El móvil no te enseña los errores del sistema que lo sostiene

Desde que Claude Code estrenó el modo remoto —las sesiones se ejecutan en tu propio server y las diriges desde la app del móvil— en febrero de 2026, dirigir agentes a distancia tiene una consecuencia estructural: el canal humano muestra la sesión —mensajes, diffs, permisos—, pero no el proceso que la sostiene. 4 flags de configuración separan un bridge que funciona de uno que se cuelga mudo, y ninguno de los cuatro produce un error visible desde la app. Si el bridge muere, no ves un error; ves que tu server ha dejado de aparecer en la lista de entornos, que es exactamente el mismo síntoma que “todavía no ha arrancado”.

Dos ejemplos medidos. Uno: sin arrancar el demonio con -v --debug-file, sus errores no van a ninguna parte — el proceso vive bajo systemd, sin terminal, y quien dirige desde el móvil jamás los verá. Dos: claude auth login regenera el fichero de configuración y borra los flags que permiten funcionar sin interfaz (remoteEnabled, hasCompletedOnboarding y compañía); el síntoma aparece después, como un Workspace not trusted o un cuelgue silencioso tras adquirir el lock del workspace. El CTO de Modal lo formuló para el código en su tesis de agent experience: cuando quien escribe es el agente, la observabilidad importa más que leer el código. Operando la caja aprendes el corolario: aplica también a la infraestructura que sostiene al agente.

La acción concreta: configura el log a fichero desde el primer arranque, y deja dos comandos de salud a mano para cuando administres la caja — un grep del registro de conexión para saber que el bridge está vivo, un grep de errores para ver lo que el móvil nunca enseña. Tras cada re-login, re-siembra los flags con un merge sobre la configuración, no con una sobreescritura. Y revisa la salud en modo pull —la consultas tú al entrar, en un vistazo—, no con alertas que nadie lee.

Los valores por defecto operan cuando nadie mira

4 GB de swap —memoria de disco que respalda la RAM— y una casilla en una consola web separan una caja sana de una inaccesible. El primer valor por defecto que me mordió fue la memoria: un build de frontend moderno más un Chromium sin interfaz gráfica disparan el OOM-killer —el proceso del kernel que mata procesos cuando se agota la RAM— incluso en una caja de 16 GB, y la víctima no es el build: son las demás sesiones que trabajaban al lado. Con 4 GB de swap el mismo build pasa sin drama. El swap no es opcional en una caja de agentes; es la diferencia entre un build lento y todas tus sesiones muertas.

El segundo default es más traicionero porque tarda medio año en activarse: la clave de dispositivo de la VPN de malla caduca sola —en Tailscale, a los ~180 días salvo que lo desactives por nodo; otras VPN tienen su propia versión del mismo temporizador—. En un portátil es higiene razonable; en un server cuyo único camino de entrada es esa red privada —la caja no expone ni SSH público—, la caducidad significa quedarte fuera de tu propia máquina. El patrón general: los valores por defecto están pensados para el caso mayoritario con un humano delante, y una agent box es el caso contrario. Cerrar el firewall también tiene su orden estricto —levantar la VPN, verificar desde otra terminal que entras por ella, permitir su interfaz en el firewall y solo entonces activarlo—, porque hacerlo en otro orden te deja fuera con la misma elegancia.

La acción concreta: añade swap aunque la RAM parezca sobrada, desactiva la caducidad de claves en cada nodo que sea un server, y ensaya el lockout antes de sufrirlo — comprueba que la consola del proveedor te da un acceso de emergencia que funciona. Un default que no has decidido tú sigue siendo una decisión: solo que la ha tomado otro y se ejecuta cuando no estás.

Ganado, no mascotas: la recuperación como operación por defecto

Estrenar una caja nueva desde un snapshot —con la fecha en el nombre: dev-system-2026-07-10— cuesta unos 20 minutos de administración; diagnosticar una caja enferma no tiene cota superior. De esa asimetría sale la doctrina que hace la caja sostenible: las cajas son ganado, no mascotas. El sistema se monta en dos capas con ciclos de vida distintos — la capa de sistema vive en un script idempotente en git y cambia poco; la capa de proyecto vive en la semilla de cada repo (clonado, dependencias, base de datos con datos de demo, test de humo) y cambia a diario. El snapshot es solo una caché de tiempo: cualquier caja vieja se pone al día re-ejecutando el script, y el script en git es la fuente de verdad.

La regla operativa: si un arreglo exige cirugía sobre estado con datos —subir de versión una base de datos con el directorio de datos vivo, dependencias compiladas contra otro runtime—, no se opera: se recrea la caja y el proyecto se vuelve a montar con su semilla. La doctrina tiene además un dividendo que no se ve hasta que hace falta: retirar una caja es borrarla y revocar sus claves — nada más que recordar. Sin backups que custodiar ni estado que migrar: el trabajo vive en git, los datos de demo se regeneran con la semilla y el snapshot recrea la máquina en minutos.

La acción concreta: escribe la semilla de tu proyecto —un script en el propio repo que lo deja funcionando de cero hasta el test de humo— aunque no tengas ninguna caja: es útil el primer día que alguien estrena portátil. Y adopta el criterio de recreación: en cuanto el diagnóstico de una máquina supera el coste de recrearla, deja de diagnosticar.

La arquitectura de la primera parte era la mitad fácil: se diseña una vez y se queda quieta. Esta mitad no — las credenciales se refrescan, los valores por defecto caducan, la memoria se agota — y ningún fallo de la lista lanza una excepción que el loop pueda atrapar: todos degradan el sistema por debajo del agente, que parece dormido en vez de roto. La mitad operativa no consiste en diseñar mejor, sino en decidir qué medir cuando nadie mira.

¿Cómo te enteras tú de que tu agente lleva seis horas parado: por un log que ya mirabas, o porque el trabajo que esperabas no aparece?

Suscríbete al boletín

Ingeniería de IA en producción — un email por semana, sin ruido.

Suscribirme →