Proveedores y failover
Qué hace AutoDev cuando un proveedor informa de un límite de uso o no se puede alcanzar. Pasa el turno a otro en lugar de dormir, y solo duerme cuando no queda adónde ir.
Cómo se detecta un límite #
Por ahora AutoDev ejecuta tres CLI de proveedor: claude, codex y antigravity, cuyo binario es agy. No predice un límite. Se entera de uno cuando una llamada lo informa. Cuentan tres cosas:
- para
claude, unrate_limit_eventestructurado cuyo estado esrejectedoblocked, o un aviso corto en la salida que coincide con una frase conocida, comoUsage limit reached. Try again in 2 hours.oYou've hit your limit · resets 2:30pm (<time zone>); - para
codex, sus propios avisos, que dan un tiempo relativo, una hora o una fecha completa; - para
agy, su propio error en stderr, un estado429oRESOURCE_EXHAUSTEDo una línea que dice que se alcanzó la cuota, con el reinicio escrito como duración, comoResets in 2h31m33s.
Un aviso es corto, y el texto que escribió un modelo, cuando pasa de 300 caracteres, nunca se examina, así que un informe que solo cita un aviso de límite no se confunde con uno. La hora de reinicio se lee del aviso. Cuando no se puede leer ninguna, claude espera una hora, y codex y agy vuelven a sondear cada 30 minutos.
Un límite pertenece a un motor #
Un límite pertenece al motor que lo informó, no al participante que lo alcanzó. Todo participante que corre en el mismo motor lo comparte, así que un turno limitado nunca se cede a un compañero en la misma cuenta agotada, que alcanzaría el mismo límite segundos después. Un motor es un proveedor y, cuando una entrada de failover nombra uno, un modelo suyo: claude y claude:<model> son dos motores.
Primero, la cadena del participante #
Un participante puede llevar una cadena failover en su archivo: entradas provider[:model], en orden.
--- provider: claude failover: [codex, claude] ---
Cuando el turno de un participante informa de un límite y no se publicó nada, AutoDev recorre primero la cadena de ese participante. Pasa a la siguiente entrada que no se sepa limitada y ejecuta el turno allí, en el mismo paso. Si esa entrada también está limitada, avanza de nuevo. Escribe <id>: <from> limited → <to>. Una entrada que nombra una CLI que esta máquina no tiene se omite. En un salto, la fijación del participante no se aplica, porque nombra un modelo del proveedor principal, a menos que la propia entrada nombre un modelo.
La cadena va primero porque un compañero a menudo no tiene nada propio que hacer: cuando el hilo espera una revisión o una respuesta de este participante, los demás solo podrían decir que esperan, y cada uno de esos es un turno de pago.
No existe una cadena predeterminada: un participante sin failover solo tiene a sus compañeros. El campo vive en el archivo del participante. Edit prompt lo abre en el panel, y autodev team edit <id> imprime su ruta. Participantes y lanes cubre el archivo.
Después, un compañero #
Cuando la cadena se agota o está vacía, el turno pasa a otro participante cuyo motor esté libre. AutoDev escribe <id> is rate limited — @<other> takes the turn en el log. El compañero se elige con las mismas reglas que cualquier turno.
Dos casos no pasan a un compañero. Quien tiene un gate pendiente nunca cede el turno, y un equipo de uno no tiene a quién cedérselo. Ambos usan solo su propia cadena. Si el límite llega después de que el participante ya publicó, la publicación se queda: el límite se registra, y el siguiente turno lo rodea.
Cuando nadie puede tomar el turno #
Cuando todos los participantes y todas las cadenas están limitados, la ejecución no se detiene. El estado pasa a rate_limited, y el daemon duerme hasta el reinicio más cercano entre todos ellos, no el de quien falló al final. AutoDev escribe every participant limited — waiting for <provider>, back first at <time>.
El sueño transcurre en tramos. Cada tramo actualiza el heartbeat, y un stop o un pause lo interrumpe. Un mensaje tuyo no, porque nada puede correr antes del reinicio. Una máquina que estuvo suspendida durante la espera despierta en el reinicio, no después del sueño que le quedaba. El tiempo dormido no se carga a maxRuntimeMin.
Una espera por límite no tiene un tope propio. maxWaitMin y la nueva comprobación a intervalos que se duplican pertenecen a una jugada wait, que El hilo compartido describe.
La vuelta #
Al inicio de cada turno, AutoDev levanta los límites cuyo reinicio ya pasó. Un participante que había avanzado por su cadena vuelve a la entrada libre más antigua, que no siempre es la principal. AutoDev escribe <id>: limit lifted — back on its primary provider.
La elección del proveedor se hace cuando un turno empieza y nunca durante uno. El registro de quién está limitado se guarda en memoria durante la vida del daemon. Un reinicio lo olvida y lo averigua de nuevo sondeando.
Cuando una llamada falla en cambio #
Un límite de uso es algo que un proveedor informa, con una hora a la que esperar. Una llamada también puede fallar sin informar de uno, y AutoDev distingue dos casos.
La red cayó. Cuando una llamada falla con un error de red, como una dirección que no se encuentra o una conexión rechazada, reiniciada o agotada, y no se publicó nada, el proveedor queda fuera para todo participante que corre en él, como un límite saca a un motor. AutoDev lo intenta de nuevo tras 1, 2, 4 y 8 minutos, y luego cada 15 mientras dure. Escribe <provider> could not be reached; trying it again at <time>, y <provider> is reachable again cuando vuelve. Los participantes de otro proveedor siguen. Con todos los proveedores fuera, escribe No provider can be reached; trying again at <time>. Esto no tiene plazo: una noche en que internet está caído tres horas continúa cuando vuelve.
La llamada falla de otra forma. La CLI se cae, agota el tiempo o termina su turno sin una publicación. AutoDev espera más tras cada fallo seguido: un segundo tras el primero, luego 1, 2, 4, 8, 15 y 30 minutos, y después una hora cada vez. Tras 12 fallos seguidos, unas cinco horas de espera, la ejecución se pausa y escribe Gave up after 12 failed attempts in a row, over five hours (<what failed>); run paused — resume to try again. Un mensaje tuyo acorta una espera, y una llamada que funciona reinicia la cuenta. Si un motor limitado vuelve antes, el siguiente intento se hace entonces. A diferencia de una espera por límite, cada intento cuenta contra maxIterations.
Las cinco horas están para un límite cuyo aviso AutoDev aún no puede leer. Se levanta solo, y una agenda que supera una ventana de uso lleva la ejecución hasta allí. Un participante que termina tres turnos seguidos sin una publicación es otro caso: reintentar gastaría cuota para no aprender nada, así que la ejecución se pausa de inmediato y lo nombra.
Lo que esto no hace #
- Un límite solo se conoce cuando una llamada lo informa. Los heartbeats de cuota que AutoDev registra no son lo que lee la decisión.
- Un límite escrito de una forma que AutoDev nunca ha visto se lee como una llamada que falló, y rige la agenda de arriba.
codexyagyno tienen comprobación de inicio de sesión. Una CLI sin sesión muestra su propio error en su lane, y CLI sin autenticar explica por qué.- Un turno iniciado por la aprobación de un gate está exento. Espera a su propio proveedor y nunca corre en otro.
- Con un solo motor en el equipo y sin cadena, un límite es una espera.
Participantes y lanes cubre el archivo donde vive la cadena, y Todas las claves de configuración enumera maxIterations, maxRuntimeMin y maxWaitMin.