Muchos agentes, una arquitectura: qué pasa cuando dos de ellos cambian el mismo sistema
Tres agentes, tres pull requests, tres opiniones razonables sobre dónde va la lógica de retry.
Uno mete el retry en el cliente HTTP. Otro envuelve el handler. Otro añade una queue y la vacía. Lee cualquiera de las tres por separado y la aprobarías. Léelas la misma tarde y te das cuenta de que el sistema ahora reintenta en tres sitios, con tres políticas de backoff distintas, y eso no lo decidió nadie.
Esta es la forma del problema en cuanto más de un agente trabaja sobre una base de código a la vez. Cada agente es correcto en local. La incoherencia es global, y solo se vuelve visible para quien revisa el último.
Por qué el archivo de reglas no arbitra esto
La respuesta estándar es un archivo de reglas: CLAUDE.md, AGENTS.md, .cursor/rules. Ya hemos escrito sobre por qué esos archivos se quedan obsoletos, y quedarse obsoleto es aquí el problema pequeño. El grande es que un archivo de reglas no puede arbitrar.
Es prosa. Dos agentes a los que les das el mismo párrafo van a producir dos lecturas distintas de él, ambas defendibles, y no hay ningún punto en el que esas lecturas se encuentren. Vive por repositorio, así que una regla sobre una frontera de servicio está en un repo mientras que el servicio del otro lado de la frontera está en otro. Y no tiene estado: no puede saber que otro agente propuso algo hace cuarenta minutos, porque es un archivo, y los archivos no saben cosas.
Lo que hace falta para arbitrar no es mejor prosa. Es una cosa compartida que ambos agentes leen y escriben, que puede sostener una decisión, y que puede notar un desacuerdo.
Lo que te da un modelo y no te da un documento
Un modelo de arquitectura son elementos y relaciones que puedes consultar. Sistemas, containers, components, las aristas entre ellos, y colgando de esas aristas las cosas que hacen que un diseño sea un diseño: la decisión que hizo deliberada una frontera, el owner al que avisar, el contrato del que depende un consumidor.
De ahí salen tres cosas, y cada una es un mecanismo, no una intención.
Todos los agentes pueden leer los mismos bytes. La action generate-context escribe un archyl.txt a partir del modelo, en markdown por defecto, y opcionalmente lo commitea automáticamente al repositorio. Nueve agentes leyendo un archivo generado es una situación distinta de nueve agentes parafraseando cada uno un documento en prosa. No es ingenioso. Simplemente es compartido.
El desacuerdo se puede pillar en la entrada. La action conformance-check ejecuta reglas de arquitectura contra los archivos que cambió una pull request, anota las violaciones inline, y hace fallar el check con la severidad que elijas. fail-on acepta error, warning o none. Si "los retries van en el cliente" es una regla y no una frase, los dos agentes que los pusieron en otro sitio se enteran en CI en vez de en la revisión.
Una decisión tiene dónde vivir. Los ADR se enganchan a los elementos C4 que restringen. La razón por la que existe la queue está en la queue, no en un hilo de Slack de marzo que ningún agente ha visto jamás.
La parte que tuvimos mal
Aquí es donde esto dejó de ser un post de blog sobre una idea bonita.
Los agentes no cambian el modelo directamente. Abren un Change Request: una propuesta, revisada y mergeada por una persona. Cuando se crea un Change Request, archyl registra la versión del modelo contra la que se construyó. Cuando se mergea, la versión se incrementa. Esa es exactamente la maquinaria que querrías para este problema.
Nunca habíamos conectado las dos cosas.
La versión base se escribía al crear y no se leía en ningún sitio. Lo que significaba que esta secuencia funcionaba, en silencio y del todo:
- El agente A y el agente B leen el modelo. Los dos ven la versión 7.
- A abre un Change Request. B abre un Change Request. Los dos están basados en la versión 7.
- El Change Request de A se mergea. El modelo está ahora en la versión 8.
- El Change Request de B se mergea. Estaba escrito contra un modelo que ya no existe.
Ningún aviso, ningún conflicto, ninguna nota en el historial. El segundo conjunto de cambios aterriza encima del primero, y si se contradicen, la contradicción es ahora la arquitectura documentada. Esto es un merge al que le han quitado la detección de conflictos, y es exactamente el fallo que se supone que evita toda la historia de "muchos agentes".
Así que lo arreglamos. Mergear un Change Request cuya versión base ya no coincide con el proyecto ahora falla con un 409 Conflict y un mensaje que nombra las dos versiones:
architecture request is based on version 7 but the model is now at version 9;
rebase the request and merge again
Aunque lo que lo hace seguro no es la comparación. Dos merges que llegaran en el mismo instante pasarían los dos una comparación y los dos seguirían adelante. Lo que cierra ese hueco es hacer condicional el incremento de versión en sí: el merge avanza el modelo solo si el modelo sigue en la versión contra la que se construyó el Change Request. Si ya se ha movido, el merge no encuentra nada que avanzar, todo hace rollback, y no se aplica ni un solo cambio. La comparación previa existe solo para que el mensaje de error pueda decirte cuánto te has quedado atrás.
409 en vez de 400 importa más de lo que parece. Un agente que reintenta con 400 se queda en bucle para siempre, porque una petición malformada sigue estando malformada. 409 dice lo contrario: lo que enviaste estaba bien y dejó de ser aplicable. Trae el modelo actual e inténtalo otra vez.
Lo que esto sigue sin hacer
Cuatro límites, todos comprobables.
Ningún agente mergea nada. No hay ningún tool MCP que mergee un Change Request. Los agentes proponen; una persona revisa y mergea. Es una frontera deliberada y no tenemos pensado quitarla, pero significa que el bucle no es totalmente automático y no deberías diseñar como si lo fuera.
La detección de conflictos es tosca. La versión es por proyecto, no por elemento. Dos agentes tocando rincones genuinamente sin relación del mismo proyecto van a colisionar igualmente en la versión. Esa es la dirección segura en la que equivocarse, y está mal.
La recuperación de contexto es léxica. find_relevant_context puntúa elementos por solapamiento de palabras en nombres, descripciones, tags y rutas. No hay embeddings ni expansión por sinónimos, así que una tarea sobre "checkout" no va a sacar a flote un component llamado OrderProcessor. La ventaja es real (determinista, sin coste en tokens, sin enviar código a ninguna parte), pero es emparejar, no entender.
Las reglas no se escriben solas. Todo lo anterior asume que alguien expresó "los retries van en el cliente" como una regla de conformidad. Un conjunto de reglas vacío no pilla nada, por muchos agentes que estén corriendo.
Qué hacer con esto esta semana
No necesitas comprar nada para averiguar dónde estás.
Coge la última semana en la que tu equipo mergeó más de una pull request escrita por un agente. Léelas juntas en lugar de en secuencia. Pregúntate si dos de ellas tomaron la misma decisión de forma distinta, y luego pregúntate qué, en tu setup actual, te lo habría dicho.
Si la respuesta es "se dio cuenta quien revisaba", eso funciona hasta el día en que quien revisa está leyendo nueve.
Los Change Requests, las reglas de conformidad y el modelo C4 son parte de archyl. Las GitHub Actions y las agent skills son open source. Lectura relacionada: por qué tus agentes tienen un archivo de reglas y no un modelo, cómo funcionan los Change Requests, y cómo se mantiene honesto el modelo.