O cemitério de branches órfãs era a maior fonte de trabalho perdido
Um agent que dá crash não perde o que fez push, perde o que só ele conseguia ver.
Apollo Space Research
Apollo Space
Vá procurar uma única mudança faltante numa fleet multi-agent movimentada e você pode encontrar uma vala comum no lugar. Dezenas de working directories abandonados. Edições em stash que ninguém consegue datar. Documentos de design inteiros que não existem em lugar nenhum exceto numa branch que nenhum humano jamais deu checkout. Nada deletado. Nada commitado tampouco. Só sentado no gap entre “um agent fez o trabalho” e “alguém conseguia encontrá-lo”, o gap mais caro de uma fleet, e o que quase ninguém projeta para evitar.
Cada um daqueles agents tinha feito push do seu código. Isso nunca foi o problema.
Um agent que dá crash não perde o que fez push, perde o que só ele conseguia ver.
Este post é sobre essa frase: por que o artefato visível sobrevive a um crash, por que o contexto invisível não sobrevive, e o que foi preciso para parar de perder a metade que importa.
A versão ingênua: um agent, um checkout, confie no push
A forma óbvia de rodar agents numa codebase é a forma como você rodaria a si mesmo. Você dá a um agent um repo, ele faz uma branch, trabalha, comita, faz push. Quando termina, a branch está no remote e você dorme bem, porque uma branch que recebeu push é imortal, nenhum crash de laptop, nenhum processo morto, nenhum reaper de out-of-memory pode levar um commit que já está no servidor.
Esse modelo está correto, e também é uma armadilha, porque silenciosamente ensina você a equiparar “pushado” com “seguro”.
Rode um agent e a equação se mantém. Rode vinte, durante a noite, cada um mastigando uma fatia diferente do mesmo monorepo, e você descobre o que “pushado” de fato cobre. Cobre o diff. Não cobre o raciocínio que produziu o diff, o plano meio-acabado em que o agent estava três passos adentro, o documento de design que ele tinha escrito no seu scratch directory mas ainda não commitado, o beco sem saída que ele tinha descartado para o próximo agent não desperdiçar uma noite re-descartando. Um push é um snapshot do output. A working memory do agent, o que ele sabia, o que estava prestes a fazer, o que já tinha aprendido a não tentar, nunca esteve no snapshot.
Então quando um daqueles vinte agents morreu no meio da tarefa, e com vinte agents, um sempre morre, o commit que tinha feito push sobreviveu perfeitamente. Tudo o que ele ainda não tinha commitado morreu com ele. E a parte cruel é que o material não-commitado costumava ser o material valioso: não o boilerplate que ele já tinha shipado, mas o entendimento em progresso que ele ainda segurava na cabeça.
Um agent que dá crash não perde o que fez push, perde o que só ele conseguia ver.
Por que o cemitério enche mais rápido do que você pensaria
O checkout único piora a situação, e este é o modo de falha que transforma uma fleet organizada num cemitério.
Dois agents num working directory não podem ambos vencer. O Git tem exatamente um HEAD por checkout, um index, um conjunto de arquivos rastreados. Quando o agent A está no meio da edição de doze arquivos e o agent B, compartilhando o mesmo diretório, roda um git switch ou um git stash ou um reset --hard para “sair do caminho”, os doze arquivos de trabalho não-commitado de A evaporam. Não para uma branch. Para o nada. B não era malicioso; B estava sendo organizado. Organização num checkout compartilhado é como você deleta a tarde do seu colega.
Num checkout compartilhado, “limpar” é indistinguível de destruir trabalho que não é seu.
E ninguém percebe no momento, porque a destruição é silenciosa. Sem erro. Sem marcador de conflito. Os arquivos só voltam ao seu estado commitado, as edições de A se foram, A nada mais sábio, A é um processo, ele não sente a perda, ele só continua de um estado que está silenciosamente faltando uma hora de raciocínio. Quando um humano vai procurar aquela mudança semanas depois, tudo o que resta é um stash com uma auto-mensagem críptica, ou uma branch órfã com um nome que ninguém reconhece, ou nada.
Multiplique isso por uma fleet rodando toda noite e o cemitério não é um acidente. É o output esperado do design ingênuo. Você vai perder trabalho proporcional a quantos agents você roda, porque cada um deles é um processo que pode morrer, e cada diretório compartilhado é um lugar onde um agent pode sobrescrever outro. O número surpreendente não é quanto se perdeu. É quanto se perdeu silenciosamente, as perdas que nunca lançaram um erro e por isso nunca pediram a ninguém para olhar.
A correção em uma linha: nunca compartilhe um writer, nunca confie na working memory
A disciplina que mata o cemitério são duas regras, e nenhuma é esperta. São só as regras às quais você chega depois de ter perdido o terceiro documento de design.
A primeira regra: uma working tree por writer. Todo agent que pode escrever ganha seu próprio checkout isolado, seu próprio HEAD, seu próprio index, seus próprios arquivos, e nunca dois agents com capacidade de escrita compartilham um. O Git tem uma ferramenta nativa para exatamente isso; as working trees são baratas de criar e baratas de descartar. O ponto não é a ferramenta. O ponto é que um agent não pode mais alcançar o diretório de outro agent e resetá-lo, porque não há outro agent no seu diretório. Você torna a colisão impossível em vez de pedir a todos que sejam cuidadosos, e “impossível” vence “cuidadoso” toda noite.
A segunda regra: comite cedo, faça push com frequência, e trate a working memory como já perdida. A correção para perder contexto não-commitado não é um backup melhor. É recusar deixar o contexto ficar não-commitado. Um agent que escreve um plano comita o plano antes de agir sobre ele. Um agent que descarta um beco sem saída anota isso e faz push, para que o próximo agent herde a lição em vez de re-aprendê-la. O modelo é simples a ponto de ser sombrio: assuma que este processo vai morrer nos próximos sessenta segundos, e garanta que quando morrer, tudo o que ele sabia já esteja no remote onde um crash não pode alcançar.
Um agent que dá crash não perde o que fez push, perde o que só ele conseguia ver. Então o trabalho inteiro é continuar encolhendo o conjunto de coisas que só ele consegue ver, até não restar nada nesse conjunto que valha a pena perder.
O registro: saber o que está vivo antes de tocar
Há mais uma peça, e é a que as pessoas pulam porque soa como burocracia.
Checkouts isolados resolvem colisões. Eles criam um novo problema: agora você tem muitos checkouts, e nenhum lugar único diz quais estão vivos, qual agent é dono de cada um, e quais foram abandonados por um processo que morreu na terça passada. Sem esse mapa, você está de volta à arqueologia, encarando um diretório de working trees, incapaz de distinguir o ativo do morto, com medo de limpar qualquer um deles porque um pode segurar a única cópia de algo.
Então toda working tree se registra: seu path, sua branch, a sessão que é dona dela, o momento em que foi reivindicada. Um ledger vivo de quem-segura-o-quê. Antes de qualquer agent, ou qualquer humano, fazer algo destrutivo, ele lê o ledger primeiro. A tree pertence a ele? Está suja com trabalho que outra pessoa escreveu? Se está suja com trabalho que ele não escreveu, ele não pode ser organizado; ele resgata esse trabalho primeiro, comita, faz push para uma branch claramente nomeada, ou a deixa em paz e pergunta.
Esse ledger é o que transforma a limpeza de uma aposta numa procedure. Um processo reaper pode varrer a fleet, encontrar as trees cujas sessões donas morreram, e, crucialmente, resgatar primeiro, podar depois. Qualquer coisa não-commitada é commitada numa branch rescue/ e recebe push antes de a tree ser removida. Nada é deletado até seu conteúdo ser imortal em algum lugar. O cemitério para de encher porque cada enterro agora começa checando se há pulso.
Imagine a diferença no fim de um longo run noturno. A fleet ingênua deixa para trás um campo de diretórios sem marcação e um humano que tem que escavá-los à mão, nunca certo de que pegou tudo. A fleet disciplinada deixa para trás um registro que diz exatamente quais trees estão prontas, quais ainda estão rodando, e quais foram resgatadas, e um remote onde toda branch tem um nome que diz quem a fez e por quê.
A virada: o trabalho que você perde é o trabalho que você não consegue ver que perdeu
Aqui está a coisa silenciosa por baixo de tudo.
As perdas que machucam um time nunca são as barulhentas. Um build que falha grita. Um conflito de merge te bloqueia até você resolvê-lo. Essas estão bem, elas se anunciam, e um problema que se anuncia é corrigido. As perdas que de fato compõem são as silenciosas: o documento de design que morreu numa branch órfã, o beco sem saída que um agent já explorou e que os próximos três agents vão explorar de novo, a hora de raciocínio que sumiu num organizado reset --hard e não lançou erro algum para marcar sua passagem. Você não debuga essas, porque debugar começa com perceber, e uma perda silenciosa é precisamente a perda que ninguém percebeu.
O que de fato construímos não foi uma convenção de git. Foi uma forma de tornar as perdas silenciosas barulhentas, de converter “o contexto de um agent desapareceu e vamos descobrir em três semanas” em “o contexto de um agent está commitado, pushado, registrado e resgatável, então não pode desaparecer sem alguém ver”. A disciplina não é sobre git. É sobre recusar deixar uma fleet perder a metade do seu trabalho que não aparece num diff.
Um agent que dá crash não perde o que fez push, perde o que só ele conseguia ver. O trabalho inteiro é garantir que nada importante esteja jamais nessa segunda categoria. Comite, faça push, registre, e um crash vira um não-evento: um processo morre, outro pega exatamente de onde ele parou, e o PR da manhã parece que ninguém tropeçou.
Essa é a propriedade que uma fleet tem que ter antes de você confiar a ela uma noite de trabalho sem supervisão. Não que nenhum agent jamais morra, eles morrem constantemente, isso é um processo para você. Mas que quando um morre, ele não leva nada insubstituível consigo.
É isso que estamos construindo na Apollo Space: um sistema onde um agent dando crash às 3h da manhã é um dar de ombros, não uma investigação, porque o trabalho que ele conseguia ver nunca foi o único lugar onde esse trabalho vivia. Se você já passou uma segunda-feira escavando a branch perdida de um colega para recuperar uma mudança que todos juravam estar pronta, você já sabe por que tratamos o cemitério como uma falha de design e não um fato da vida.
A Apollo cuida da operação repetitiva da sua empresa pro seu time não precisar.
Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.
Entrar na lista de esperaO imposto oculto dos agents em paralelo é um diamante de migrations
Seis agents escrevendo para um schema conflitam no banco de dados, não no código, e a CI morre em "multiple heads".
EngenhariaUm orchestrator que não sobrevive ao próprio crash não é um
Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir.
EngenhariaColoque um portão determinístico na frente do seu revisor mais esperto
A pega-defeito mais barata é um script burro que checa se duas branches mergeadas ainda sobem antes de qualquer julgamento.