La promesa y el riesgo son la misma cosa

La gracia de un agente de código es que puede encadenar pasos sin que estés mirando cada uno: leer un archivo, decidir qué tocar, editarlo, correr los tests, corregir si fallan, repetir. Eso es exactamente lo que lo hace útil y lo que lo hace peligroso. La autonomía que ahorra tiempo es la misma autonomía que, si se equivoca de rumbo, se equivoca durante veinte pasos seguidos antes de que nadie se dé cuenta.

He dejado agentes sueltos en tareas que ahorraron una tarde entera. También he tenido que revertir un cambio que "funcionaba" porque el agente había reescrito una función crítica de una forma que pasaba los tests existentes pero rompía un caso que nadie había testeado. Con el tiempo me he hecho un criterio bastante concreto de cuándo sí y cuándo no.

El criterio no es la dificultad de la tarea

El primer error que cometí fue pensar en términos de "esto es fácil, se lo doy suelto" y "esto es difícil, lo hago yo". Es el criterio equivocado. Una tarea puede ser trivial en dificultad técnica y aun así catastrófica si sale mal: borrar una tabla, tocar un archivo de configuración de producción, cambiar una migración de base de datos ya aplicada.

El criterio que uso de verdad es reversibilidad, no dificultad. Me pregunto: si el agente hace esto mal, ¿cuánto cuesta darse cuenta y cuánto cuesta deshacerlo?

Si la respuesta es "lo veo en el diff antes de aceptar nada y deshacerlo es un git checkout", lo dejo suelto sin mucha vigilancia. Si la respuesta es "esto toca algo que no tiene vuelta atrás fácil, o que afecta a datos reales", no, aunque la tarea en sí sea sencilla.

Tareas que dejo sueltas

Refactors mecánicos dentro de un módulo acotado: renombrar, extraer funciones, mover código sin cambiar comportamiento. Escribir tests para código ya existente cuya lógica no va a cambiar. Actualizar dependencias menores y arreglar lo que rompan, siempre con la suite de tests como red. Generar código repetitivo con un patrón claro, como adaptadores o mapeos entre estructuras similares.

En todos estos casos, el espacio de lo que puede salir mal es acotado y visible en el diff. Si el agente se equivoca, se ve en la revisión, y revertir cuesta lo mismo que revertir cualquier commit.

Tareas que no dejo sueltas, aunque parezcan simples

Cualquier cosa que toque autenticación, permisos o manejo de datos de usuario, sin importar lo pequeño que parezca el cambio. Migraciones de esquema, incluso en local, porque un error ahí puede ser silencioso hasta que ya es tarde. Decisiones donde el agente tiene que elegir entre dos formas de resolver algo y esa elección tiene implicaciones de diseño a largo plazo: ahí no quiero que decida solo, quiero que me pregunte o que yo revise antes de que siga.

Y una categoría que subestimé al principio: cualquier tarea donde el "éxito" según los tests no garantiza corrección real. Un agente que hace pasar los tests existentes no ha demostrado que el comportamiento sea correcto, ha demostrado que no rompió lo que ya estaba cubierto. Si la cobertura de tests de esa parte del proyecto es floja, la autonomía del agente hereda esa debilidad sin saberlo.

El punto intermedio que uso más

Entre "suelto del todo" y "reviso cada paso" hay un punto intermedio que uso mucho: dejo que el agente trabaje de forma autónoma pero limito el alcance de lo que puede tocar. Le doy acceso a una carpeta o módulo concreto, no a todo el repo. Así, aunque se equivoque, el radio del daño está acotado por diseño, no por su buen juicio.

Esto cambia la pregunta de "¿confío en que decida bien?" a "¿qué es lo peor que puede pasar si decide mal aquí?", que es una pregunta mucho más fácil de responder con certeza.

La recomendación

No decidas la autonomía de un agente por lo difícil que parece la tarea. Decídela por lo reversible que es el error. Si un fallo se ve en el diff y se deshace con un revert, suéltalo. Si un fallo puede quedar silencioso, tocar datos reales, o tomar una decisión de diseño que luego arrastras durante meses, no lo sueltes, por muy simple que suene la petición.

Relacionado: Automatizar tareas sin perder el control del resultado