Provedores e failover

O que o AutoDev faz quando um provedor informa um limite de uso ou não pode ser alcançado. Ele passa o turno adiante em vez de dormir, e só dorme quando não sobra para onde ir.

Como um limite é detectado #

O AutoDev roda três CLIs de provedor por enquanto: claude, codex e antigravity, cujo binário é agy. Ele não prevê um limite. Ele fica sabendo de um quando uma chamada o informa. Três coisas contam:

Um aviso é curto, e texto escrito por um modelo, quando passa de 300 caracteres, nunca é examinado, então um relatório que apenas cita um aviso de limite não é confundido com um. O horário de reinício é lido do aviso. Quando nenhum pode ser lido, o claude espera uma hora, e o codex e o agy sondam de novo a cada 30 minutos.

Um limite pertence a um engine #

Um limite pertence ao engine que o informou, não ao participante que o atingiu. Todo participante que roda no mesmo engine o compartilha, então um turno limitado nunca é entregue a um colega na mesma conta esgotada, que atingiria o mesmo limite segundos depois. Um engine é um provedor e, quando uma entrada de failover nomeia um, um modelo dele: claude e claude:<model> são dois engines.

Primeiro, a cadeia do participante #

Um participante pode levar uma cadeia failover no arquivo dele: entradas provider[:model], em ordem.

.autodev/participants/<id>.md
---
provider: claude
failover: [codex, claude]
---

Quando o turno de um participante informa um limite e nada foi postado, o AutoDev percorre primeiro a cadeia desse participante. Ele passa para a próxima entrada que não se sabe limitada e roda o turno ali, no mesmo passo. Se essa entrada também estiver limitada, ele avança de novo. Ele escreve <id>: <from> limited → <to>. Uma entrada que nomeia uma CLI que esta máquina não tem é ignorada. Num salto, a fixação do participante não se aplica, já que nomeia um modelo do provedor principal, a menos que a própria entrada nomeie um modelo.

A cadeia vem primeiro porque um colega muitas vezes não tem nada seu a fazer: quando a thread espera uma revisão ou uma resposta deste participante, os outros só poderiam dizer que estão esperando, e cada um desses é um turno pago.

Não existe cadeia padrão: um participante sem failover só tem os colegas. O campo mora no arquivo do participante. Edit prompt o abre no painel, e autodev team edit <id> imprime o caminho dele. Participantes e lanes trata do arquivo.

Depois, um colega #

Quando a cadeia se esgota ou está vazia, o turno vai para outro participante cujo engine esteja livre. O AutoDev escreve <id> is rate limited — @<other> takes the turn no log. O colega é escolhido pelas mesmas regras de qualquer turno.

Dois casos não passam para um colega. Quem tem um gate pendente nunca entrega o turno, e uma equipe de um só não tem a quem entregar. Os dois usam só a própria cadeia. Se o limite chega depois de o participante já ter postado, o post fica: o limite é registrado, e o turno seguinte o contorna.

Quando ninguém pode assumir o turno #

Quando todo participante e toda cadeia estão limitados, a execução não para. O status vira rate_limited, e o daemon dorme até o reinício mais próximo entre todos eles, não o de quem falhou por último. O AutoDev escreve every participant limited — waiting for <provider>, back first at <time>.

O sono acontece em fatias. Cada fatia atualiza o heartbeat, e um stop ou um pause o interrompe. Uma mensagem sua não, já que nada pode rodar antes do reinício. Uma máquina que ficou suspensa durante a espera acorda no reinício, não depois do sono que lhe restava. O tempo dormindo não é cobrado de maxRuntimeMin.

Uma espera por limite não tem teto próprio. maxWaitMin e a reverificação em intervalos que dobram pertencem a uma jogada wait, que A thread compartilhada descreve.

A volta #

No início de cada turno, o AutoDev levanta os limites cujo reinício já passou. Um participante que tinha avançado na cadeia volta para a entrada livre mais antiga, que nem sempre é a principal. O AutoDev escreve <id>: limit lifted — back on its primary provider.

A escolha do provedor é feita quando um turno começa e nunca durante um. O registro de quem está limitado fica na memória durante a vida do daemon. Um reinício o esquece e descobre de novo, sondando.

Quando uma chamada falha em vez disso #

Um limite de uso é algo que um provedor informa, com um horário para esperar. Uma chamada também pode falhar sem informar um, e o AutoDev distingue dois casos.

A rede caiu. Quando uma chamada falha com um erro de rede, como um endereço que não é encontrado ou uma conexão recusada, reiniciada ou expirada, e nada foi postado, o provedor fica fora para todo participante que roda nele, como um limite tira um engine. O AutoDev tenta de novo depois de 1, 2, 4 e 8 minutos, e então a cada 15 enquanto durar. Ele escreve <provider> could not be reached; trying it again at <time>, e <provider> is reachable again quando volta. Os participantes em outro provedor seguem. Com todos os provedores fora, ele escreve No provider can be reached; trying again at <time>. Isso não tem prazo: uma noite em que a internet fica fora por três horas continua quando ela volta.

A chamada falha de outro jeito. A CLI quebra, expira, ou termina o turno sem um post. O AutoDev espera mais depois de cada falha seguida: um segundo depois da primeira, depois 1, 2, 4, 8, 15 e 30 minutos, e então uma hora a cada vez. Depois de 12 falhas seguidas, cerca de cinco horas de espera, a execução pausa e escreve Gave up after 12 failed attempts in a row, over five hours (<what failed>); run paused — resume to try again. Uma mensagem sua encurta uma espera, e uma chamada que funciona zera a contagem. Se um engine limitado volta antes, a próxima tentativa é feita então. Ao contrário de uma espera por limite, cada tentativa conta contra maxIterations.

As cinco horas existem para um limite cujo aviso o AutoDev ainda não consegue ler. Ele se levanta sozinho, e uma agenda que passa de uma janela de uso leva a execução até lá. Um participante que termina três turnos seguidos sem um post é outro caso: tentar de novo gastaria cota para não aprender nada, então a execução pausa de uma vez e o nomeia.

O que isto não faz #

Participantes e lanes trata do arquivo onde a cadeia mora, e Todas as chaves de configuração lista maxIterations, maxRuntimeMin e maxWaitMin.