Por qué el trabajo entre repos se rompe
Imagina una funcionalidad que toca una API, su librería cliente y la web app. Entrégale cada repo a su propio agente con la misma descripción vaga y cada uno hará algo razonable. El agente de la API renombra un campo; el agente del cliente mantiene el nombre antiguo; el agente web inventa un tercero. Cada agente tiene razón en su propio repo y se equivocan juntos.
Dos cosas fallan: el contrato entre los repos se desalinea, y el orden de merge importa. Si mergeas al consumidor antes que al productor, main queda roto hasta que aterrice el otro PR.
Planifica una vez, a nivel de arquitectura
Escribe una story padre que describa todo el cambio —la misión, el nuevo contrato, el orden— y luego un sub-issue por repositorio, cada uno con sus propios criterios de aceptación. Linear soporta exactamente esto con issues padre y sub-issues. [4] El issue padre contiene la arquitectura; cada sub-issue es lo bastante pequeño para una ejecución de agente. Cómo escribir cada uno se cubre en nuestra guía de stories para agentes de codificación.
Expand, migrate, contract
La forma más segura de cambiar una interfaz de la que dependen varios repos es una antigua. Martin Fowler lo llama parallel change:
"Parallel change, también conocido como expand and contract, es un patrón para implementar cambios incompatibles hacia atrás en una interfaz de forma segura, dividiendo el cambio en tres fases distintas: expand, migrate y contract." [1]
Expand
El productor acepta y devuelve tanto la forma antigua como la nueva.
Migrate
Cada consumidor migra a la forma nueva, un repo a la vez.
Contract
Una vez que todos los consumidores han migrado, se elimina la forma antigua.
Cada paso es desplegable por sí solo. El libro de Google sobre cambios a gran escala hace el mismo punto a mayor escala: "el cambio atómico más grande posible disminuye, contraintuitivamente" a medida que crece una base de código, así que los cambios grandes se dividen en piezas independientes. [2]
Codifica el orden como dependencias
No guardes el orden en tu cabeza. En Linear, marca el sub-issue del consumidor como bloqueado por el sub-issue del productor —los issues bloqueados muestran una bandera naranja bajo "Blocked by" en la barra lateral del issue. [5] La porción del consumidor no empieza hasta que la del productor se haya mergeado.
Parent: Show customers' chosen display name everywhere
├─ API-1 Expand: accept + return display_name alongside customer_name
├─ SDK-1 Use display_name in the typed client (blocked by API-1)
├─ WEB-1 Render display_name in account pages (blocked by SDK-1)
└─ API-2 Contract: remove customer_name (blocked by WEB-1)Blocked-by significa mergeado —así que asegúrate de que mergear el productor también lo despliega o publica (para un SDK, una versión publicada) antes de que la porción del consumidor dependa de ella. Si tus despliegues no ocurren al mergear, añade un paso de release a la story del productor.
Leído de arriba abajo, eso es a la vez el plan y el orden de merge —y cada línea deja main desplegable. Es la misma idea que mergear trabajo paralelo una rama a la vez, algo que cubrimos en nuestra guía de agentes de codificación en paralelo.
Fija el contrato
Un contrato escrito solo en una story puede seguir desalineándose. Fíjalo en el código, para que un desajuste falle en CI: un cliente tipado compartido generado desde una única fuente, una especificación OpenAPI que ambas partes validan, o tests de contrato dirigidos por el consumidor. Pact describe la idea: un contrato está entre un consumidor —"un cliente que quiere recibir ciertos datos"— y un proveedor —"una API en un servidor que provee los datos que el cliente necesita." [3] Los tests del consumidor registran lo que espera; el CI del proveedor comprueba que sigue entregándolo.
¿Un agente en todos los repos, o uno por repo?
Los Cloud Agents de Cursor pueden ejecutarse en entornos multi-repo, donde un agente cambia varios repositorios en una sola ejecución y abre un pull request en cada uno. [6] ¿Cuándo es la herramienta adecuada?
Un agente en todos los repos
Un cambio pequeño y muy acoplado que es seguro entregar junto —las mismas personas son dueñas de cada repo y puedes desplegarlos a la vez.
Un agente por repo
Un cambio mayor, dueños distintos, o algo que necesita despliegues escalonados. Porciones separadas con orden blocked-by mantienen cada paso revisable y desplegable.
Dónde encaja Aerunit
Esta guía pide dos cosas: un plan para todo el cambio, y un orden explícito que mantenga main desplegable. Aerunit hace ambas. Su generador de stories lee cada repositorio que toca el cambio y propone un issue padre más un sub-issue por repo afectado, cada uno con alcance, criterios de aceptación y no-objetivos.
Aerunit sigue las relaciones blocks / blocked-by de Linear, así que cuando el PR de la porción del productor se mergea, los sub-issues que bloqueaba se encolan automáticamente para Cursor Cloud Agents —la siguiente porción empieza sobre código ya mergeado, sin que tengas que despacharla a mano.
Un plan
Un issue padre más un sub-issue por repo, escrito a partir de tu código real.
Orden explícito
Las relaciones blocked-by hacen visible el orden de merge en Linear.
Una revisión por repo
Cada porción es su propio PR, revisado contra su propio sub-issue.
Preguntas frecuentes
¿Deberíamos pasar a un monorepo para los agentes?
No solo por los agentes. Un monorepo permite que un cambio aterrice de forma atómica, pero incluso Google, con uno de los monorepos más grandes, evita cambios atómicos masivos a esa escala y los divide en piezas más pequeñas e independientes. Los hábitos de esta guía —planificar una vez, expand-migrate-contract, orden explícito— funcionan en cualquiera de los dos esquemas. [2]
¿Cómo evito que los agentes de dos repos inventen nombres de campo distintos?
Decide el contrato antes de que arranque cualquier agente, escríbelo en la story padre y fíjalo en el código: un cliente tipado compartido, una especificación OpenAPI o tests de contrato dirigidos por el consumidor. Así, un desajuste falla en CI y no en producción. [3]
¿Qué pasa si una porción downstream falla después de que la upstream se haya mergeado?
Para eso sirve expand-migrate-contract: el cambio upstream acepta tanto la forma antigua como la nueva, así que nada se rompe mientras la porción downstream se arregla o se reintenta. Solo elimina la forma antigua cuando todos los consumidores hayan migrado. [1]