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:

  1. 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 maintenance em execução. Não comece trabalho pesado naquela máquina enquanto isso.
  2. 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.
  3. Consolide o object store. Rode git gc (precedido de git repack -ad se 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 acompanhe df -h.
  4. 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.
  5. Confirme que a correção está no lugar. Depois de atualizar, ~/.hermes/hermes-agent/apps/desktop/electron/bundle-skew.ts deve conter GIT_NO_LAZY_FETCH. Se não contiver, você ainda está no código antigo.
  6. 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.