Uma atualização preencheu um buraco de 434 GB no disco

Você clica em Atualizar no notebook, vai fazer um café e volta para um navegador que não abre, um editor reclamando de espaço em disco e o próprio Hermes se recusando a iniciar. O culpado acaba sendo o ~/.hermes/hermes-agent/.git, que cresceu de cerca de um gigabyte para 434 GB e comeu um SSD de 1 TB.
Isso não foi “o download da atualização era gigante”. Uma checagem de versão em background disparou o fetch sob demanda num certo layout de instalação, e a coisa virou bola de neve. Aqui está o mecanismo, os números reais e como limpar.
Uma chamada git que só pergunta sobre um caminho
O app desktop do Hermes roda uma checagem de consistência na inicialização (internamente: bundle-skew). Ela pergunta se o código embutido no app bate com o código em disco, e sugere reiniciar se os dois divergirem. Faz isso com dois comandos git:
git merge-base --is-ancestor <stamp> HEAD
git rev-list --count <stamp>..HEAD -- apps/desktop/...
Os dois são baratos num clone normal. O problema é o layout da instalação: algumas instalações são clones parciais sem trees (--filter=tree:0 — as trees são buscadas sob demanda em vez de ficarem guardadas para cada commit histórico).
O segundo comando é filtrado por caminho (-- apps/desktop/...). Para contar quantos commits naquele intervalo tocaram apps/desktop, o git precisa ler a tree de cada commit ao longo do intervalo. Nenhuma delas é local, então o git cai na maquinaria de promisor e as busca pela rede — e cada fetch deixa o seu próprio promisor pack em disco. Histórico mais longo significa mais trees para buscar e mais packs.
No relatório do incidente, esse intervalo cobria 1.784 commits.
De 1 GB a 434 GB: os números
Tudo abaixo vem do relatório na issue #129514 (macOS, SSD de 1 TB):
| Item | Valor |
|---|---|
Crescimento do .git |
~1,1 GB para um clone completo → 434 GB |
| Promisor packs | 2.659 (~398 GiB) |
Arquivos tmp_pack_* abandonados |
1.309 (~29 GiB) |
| Maior pack único | 641 MB |
| Manutenção em background concorrente | 179 processos no pico (git maintenance run --auto → git commit-graph write --split --reachable), cada um com ~2 GB de RSS |
| Load do sistema | load average 220 |
| Cadência da checagem | um novo rev-list a cada ~2 minutos depois do reboot, cada um rodando 9–15 minutos |
| Desfecho | a atualização falhou com [Errno 28] No space left on device, o Mac reiniciou e o CLI/TUI não iniciou depois |
Rajadas anteriores estavam no mesmo relatório: 138 promisor packs em 25 de setembro, 11 em 27 de setembro, 339 em 28 de setembro, com um reboot inesperado no meio. Não foi uma execução azarada — isso se acumula.
Por que “já foi corrigido” não te salva no meio de uma atualização
A correção chegou em 28 de setembro. Hoje a checagem na main se parece com isto:
const options = {
cwd: repoRoot,
env: { ...process.env, GIT_NO_LAZY_FETCH: '1', GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'Never' },
timeoutMs: 5000
}
GIT_NO_LAZY_FETCH=1 significa não busque objetos sob demanda — falhe em vez disso. É exatamente a semântica que essa checagem quer, já que ela nunca deveria depender da rede. Some o timeout de 5 segundos e o reap da árvore de processos, e ela não consegue mais derrubar a máquina.
Mas existe um problema de timing, e é a parte mais valiosa do relatório:
A build que roda a checagem é a que já está instalada, e ela só é substituída no finalzinho da atualização.
O app desktop de quem reportou foi buildado em 27 de setembro a partir de 96cce68 — um dia antes da correção. Assim que a atualização puxou 1.292 commits, a checagem do app antigo percorreu 96cce68..HEAD usando o código antigo, sem GIT_NO_LAZY_FETCH. O disco encheu, a atualização falhou antes do passo que troca a nova build do desktop, então o app sem correção continuou instalado — e retomou o loop depois do reboot. Ele não se cura sozinho.
Cheque se você foi afetado
Quatro comandos somente leitura, alguns segundos:
du -sh ~/.hermes/hermes-agent/.git # tamanho do object store; ~1GB é normal
ls ~/.hermes/hermes-agent/.git/objects/pack/*.pack | wc -l # contagem de packs; um ou dois dígitos é normal
ls ~/.hermes/hermes-agent/.git/objects/pack/tmp_pack_* | wc -l # downloads não ativados; normalmente 0
df -h ~ # espaço livre
Depois procure uma fila de processos git e um load anormal:
uptime
ps aux | grep -E '[r]ev-list|[g]it maintenance' | wc -l
Contagens de packs na casa das centenas, uma pilha de tmp_pack_*, load na casa das dezenas — é o mesmo padrão.
Como limpar
Esta é uma sequência geral de limpeza do git, não uma lista oficial de comandos. Entenda cada passo antes de rodá-lo:
- Estanque o sangramento. Feche o app desktop (senão ele dispara de novo a cada par de minutos) e pare os processos
git rev-list/git maintenanceem execução. Não comece trabalho pesado naquela máquina enquanto isso. - Apague as sobras não ativadas. Os arquivos
tmp_pack_*em.git/objects/pack/são downloads incompletos que o git nunca vai referenciar; é seguro removê-los. No caso reportado, só isso recuperou cerca de 29 GiB. - Consolide o object store. Rode
git gc(precedido degit repack -adse preciso) para juntar os packs espalhados. Esteja ciente de que gc num repositório com 2.659 packs é lento e faminto por CPU — faça com a máquina ociosa e acompanhedf -h. - Atualize o código pela linha de comando. O detalhe-chave: atualize pelo terminal, não pelo app desktop anterior à correção que causou o problema. Veja a referência do hermes update para as flags. Se a sua instalação é um clone filtrado/shallow preso nesse estado, uma instalação completa nova do canal oficial costuma ser mais rápida do que desembaraçar isso.
- Confirme que a correção está no lugar. Depois de atualizar,
~/.hermes/hermes-agent/apps/desktop/electron/bundle-skew.tsdeve conterGIT_NO_LAZY_FETCH. Se não contiver, você ainda está no código antigo. - Defina um teto mental para o
.git. Cerca de um gigabyte é saudável; dezenas de gigabytes significam que algo está puxando histórico em silêncio. Não espere o disco encher primeiro.
Onde isso está
Em 2026-10-01 a issue #129514 continua aberta e marcada como P1. É um desdobramento da #125932, que descrevia o mesmo mecanismo num estágio mais leve (dezenas de processos git travados). A correção em si está na main — o bundle-skew.ts agora define GIT_NO_LAZY_FETCH=1 com um timeout de 5 segundos, e o hermes_cli/_subprocess_compat.py traz um NO_LAZY_FETCH_ENV correspondente — mas o caminho de upgrade a partir de uma build anterior à correção não tem proteção, que é o problema central levantado pelo relatório. Se builds antigas vão receber uma guarda adicional ainda não está decidido.
Se você instala a partir de um clone parcial, ou clica em Atualizar dentro do app desktop, rode os passos 1 e 2 acima hoje.
A conclusão
Nem uma linha de código aqui estava “errada”. Duas decisões razoáveis colidiram: usar um clone sem trees para economizar disco e rodar uma checagem de consistência de versão por segurança — sendo que essa checagem contém um passeio de histórico filtrado por caminho que, num repositório sem trees, significa “baixe este histórico inteiro”. Uma vez que a bola de neve começou, a manutenção em background do próprio git se somou e derrubou CPU, memória e disco de uma vez.
Três sinais valem ser lembrados: o tamanho do .git, o número de arquivos pack e a contagem de sobras tmp_pack_*. Os três levam segundos para ler, e os três teriam te avisado que algo estava errado muito antes de o disco encher. Atualizações têm outros modos de falha também — uma atualização que trava mas reporta sucesso e como fazer um upgrade com elegância valem a leitura.