← Voltar ao Blog

Eu estava lançando meu plugin às 3 da manhã quando ele me avisou: 67% de contexto. Vinte minutos depois, ele mesmo quebrou

Eram 3 da manhã e eu estava fechando o release do compact-ops, um plugin de Claude Code que protege a sessão contra a perda de memória do /compact. O uso de contexto bateu 67% e uma notificação apareceu dentro da minha própria sessão: limite cruzado, rode /compact num ponto limpo de parada, aqui está seu plano atual e sua última decisão. Fui eu que escrevi esse aviso. Também fui eu a pessoa que ele salvou.

Vinte minutos depois, a mesma noite me entregou a cena oposta. Rodei o /compact e os três hooks do plugin falharam de uma vez. Durante o release eu tinha mudado a estrutura de diretórios, e a sessão em execução ainda segurava o caminho antigo. A compactação terminou normalmente mesmo assim, porque todo hook é fail-open: se quebra, sai da frente em vez de travar o comportamento padrão. Numa única sessão eu vi o recurso funcionar e vi ele falhar com segurança. Dogfooding melhor que isso não se compra.

O que o /compact joga fora

Quando a janela de contexto enche, o Claude Code comprime a conversa inteira num único resumo interno e descarta as mensagens originais. Vale para o /compact manual e para o automático.

O resumo até se sai bem com código. O que ele derruba é o estado operacional: “o push já foi aprovado”, “essa abordagem a gente já tentou e falhou”, “esse número veio daquele arquivo”. Quando o agente pós-compactação esquece isso, ele volta pedindo permissão que você já deu e tenta de novo a correção que você já enterrou. Quem usa agente todo dia conhece essa tarde perdida.

O compact-ops não substitui o resumo. Ele coloca um seguro por fora, usando só hooks oficiais:

  • Antes da compactação, um hook PreCompact faz backup do transcript e chama um LLM separado para escrever um arquivo de estado com 10 seções fixas (plano ativo, decisões da sessão, bloqueios, arquivos em edição, tentativas que falharam…)
  • Logo depois da compactação, um hook SessionStart injeta esse estado no contexto novo, junto com uma nota dizendo para reler os arquivos originais antes de confiar em qualquer resumo
  • O mesmo estado é injetado quando você roda claude --resume no dia seguinte, porque ele fica salvo em ~/.claude/compact-ops/ por 30 dias, e sobrevive a reboot
  • Um hook UserPromptSubmit calcula o uso de contexto a cada prompt e avisa uma única vez quando passa de 60%

O algoritmo de compactação continua intocado. Se qualquer hook morrer, o /compact padrão passa direto.

O crédito: isso é um derivado do compact-plus

A ideia central não é minha. O compact-plus do u-ichi (MIT) chegou lá primeiro: hook PreCompact chamando um segundo LLM para salvar estado estruturado antes do resumo engolir tudo. Li o README e quis instalar na hora.

Só que o meu fluxo tinha três desencontros com as premissas dele, então fiz um derivado e mudei exatamente três coisas: o aviso de uso funciona só com o plugin (o original depende de um script de statusline que mora no dotfiles do autor, então numa instalação limpa o aviso nunca dispara), o estado sai do $TMPDIR e vai para um diretório que sobrevive a reboot, e a recuperação também dispara no --resume, não só ao cruzar uma compactação. O backend de LLM ficou Claude puro (Sonnet com fallback para Haiku); o original usa Codex como fallback, o que pressupõe uma assinatura de ChatGPT Pro do lado.

Custo honesto: cada compact passa a gastar uma chamada extra de LLM. Dá para rebaixar para Haiku ou desligar com uma variável de ambiente.

A terceira mancada da noite, já que estamos sendo honestos

O aviso e a falha dos hooks foram as duas cenas boas. A terceira fui eu mesmo. Para testar uma correção do marketplace, instalei uma cópia do plugin com outro nome de marketplace e depois rodei o uninstall da cópia. O uninstall casou só pelo nome do plugin e levou junto a instalação de produção. Fiquei uns minutos olhando para um claude plugin list vazio sem entender o que eu tinha feito.

A regra que ficou no meu log: nunca teste um plugin com o mesmo nome num marketplace diferente; mude o nome do plugin também. A falha foi justamente essa, confiar que o identificador composto era composto de verdade.

Os 6 pontos que endureci antes de abrir o repositório

A v0.1.0 era “funciona na minha máquina”. Antes de tornar o repositório público, passei minha própria revisão mais uma segunda revisão com um CLI independente, e a v0.2.0 saiu com seis correções:

  1. Permissões. Estado e backup carregam sua conversa crua, incluindo qualquer segredo que uma saída de ferramenta ecoou. Tudo agora nasce com umask 077, diretórios 700, arquivos 600.
  2. Validação do session_id. O JSON de entrada do hook fluía direto para caminhos de arquivo. Agora passa por uma allowlist antes de tocar o filesystem.
  3. Validação da saída do LLM. O gerador de estado era confiado depois de checar a primeira linha. Agora as 10 seções precisam existir, senão o estado anterior é preservado.
  4. jq em passada única. O processamento do transcript disparava jq 3 ou 4 vezes por linha; numa sessão longa isso comia o timeout inteiro do hook. Virou um processo só, em stream.
  5. Backup com gzip. O JSONL comprime para mais ou menos um décimo do tamanho.
  6. Log de depuração. A lição de verdade. Design fail-open não atrapalha em produção, e também morre em silêncio absoluto. As três falhas de hook lá do começo só ficaram visíveis porque o harness imprimiu uma linha de erro; o plugin não tinha como me contar o porquê. Agora COMPACT_OPS_DEBUG=1 registra cada falha engolida. Se você vai escrever hook fail-open, escreva o log primeiro. Eu fiz na ordem errada e dei sorte.

Instalar

git clone https://github.com/kenimo49/compact-ops.git
claude plugin marketplace add /path/to/compact-ops --scope user
claude plugin install compact-ops@compact-ops-local

Precisa de Claude Code v2.x, jq e o CLI claude como backend. Linux e macOS. Depois de instalar, é só rodar /compact como sempre. O estado salvo é markdown puro: quando uma retomada de sessão parecer estranha, dá para abrir o arquivo e ler o que a sessão anterior achou que estava passando adiante.

Entrei naquela noite achando que estava lançando um plugin. Saí como o primeiro usuário que ele salvou — e o primeiro que ele viu quebrar. Qual foi a última coisa que o seu agente esqueceu depois de um /compact? Se uma sessão pós-compactação já desfez uma tarde sua com toda a confiança do mundo, conta aí nos comentários.

ken imoto · WebRTC & Voice AI engineer · kenimoto.dev · TabNews