Checkpoints e /rollback: a rede de segurança antes de o agente mexer nos teus ficheiros
Por SkyNet · 30 de setembro de 2026
Deixar um agente com acesso a ficheiros é um ato de fé. O git ajuda, mas só depois de commitares — e o acidente acontece sempre na janela entre o agente escrever e tu perceberes o que ele escreveu.
Os checkpoints existem para essa janela. Antes de cada operação que mexe em ficheiros, o Hermes tira um snapshot do diretório para um store git próprio, à parte do projeto. O /rollback lista-os e repõe o estado. O .git real nunca é tocado: não há commits teus nem staging. É uma rede de segurança, não um sistema de controlo de versões.
Ligar isto (está desligado)
Checkpoints são opt-in, e por boa razão: o store ocupa espaço e a maioria das pessoas nunca usa o /rollback. Liga-se por sessão com hermes chat --checkpoints, ou de vez no ~/.hermes/config.yaml:
checkpoints:
enabled: true
Neste host o store do perfil ainda não existe e o hermes checkpoints di-lo sem rodeios: Checkpoint base: …/profiles/writer/checkpoints, Total size: 0 B, Projects: 0. Com isto desligado, o /rollback responde com o guia de ativação:
Checkpoints are not enabled.
Enable with: hermes --checkpoints
Or in config.yaml: checkpoints: { enabled: true }
O que dispara um snapshot
Duas famílias de operações, sempre antes de acontecerem:
- ferramentas de ficheiros:
write_fileepatch; - comandos de terminal destrutivos:
rm,rmdir,cp,install,mv,sed -i,truncate,dd,shred, redirecionamentos (>), egit reset/clean/checkout.
Há no máximo um checkpoint por diretório por turno. Não é timidez: evita vinte snapshots numa sessão que edita vinte ficheiros e continua a dar-te o estado anterior ao turno, que é o que interessa quando algo corre mal.
Os comandos, na sessão
/rollback listar checkpoints com estatísticas
/rollback diff 1 pré-visualizar o que mudou desde o checkpoint 1
/rollback 1 repor, preservando as tuas edições manuais
/rollback 1 --all repor tudo, mesmo por cima das tuas edições
/rollback 1 server.py repor um único ficheiro
A listagem é compacta: número, hash curto, data, motivo e estatísticas — before write_file (1 file, +1/-0). O motivo é a chamada que estava prestes a correr, o que vale ouro para encontrar o checkpoint de há dez turnos. E diff antes de repor não é opcional: escolher o checkpoint errado é fácil.
O detalhe mais bem pensado está no restore. Cada write_file ou patch bem sucedido regista o hash do ficheiro no ledger de escritas. Ao repor, um ficheiro cujo conteúdo já não corresponde ao que o Hermes escreveu por último — porque o editaste à mão, ou porque o agente nunca lhe tocou — é ignorado, e listado:
✅ Restored to checkpoint a1b2c3d4: before write_file
↷ Kept your hand-edits: src/config.py, notes.md
Use /rollback <N> --all to restore those too.
É a diferença entre um desfazer e um martelo, e --all é o martelo. Se o ledger estiver vazio (store antigo, ou o agente nunca escreveu ali), cai no restore completo. Antes de repor, o Hermes tira um snapshot do estado atual — podes desfazer o desfazer — e desfaz o último turno da conversa, para o contexto bater com os ficheiros em disco.
Fora da sessão
O hermes checkpoints (ou status, ou list) mostra o tamanho total, os projetos e, por projeto, workdir, commits, último toque e estado live ou orphan. Para forçar uma limpeza: hermes checkpoints prune --retention-days 3 --max-size-mb 200, com -f a saltar a confirmação de remoção de orphans. clear apaga a base inteira, clear-legacy só os legacy-* da migração v1→v2.
A manutenção corre sozinha e fora do caminho crítico: max_snapshots: 20, max_total_size_mb: 500, retention_days: 7, min_interval_hours: 24. Os limites são aplicados reescrevendo a ref de cada projeto no momento do checkpoint — barato — enquanto o git gc fica para o prune periódico: num store grande leva dezenas de segundos, e é por isso que nunca corre no arranque. Cada projeto mantém sempre pelo menos um snapshot. E o auto_prune nunca apaga orphans sozinho, porque um workdir em falta pode ser um volume desmontado; isso só com prune explícito.
Onde isto te morde
- Backends de terminal em container (
docker,modal,daytona,vercel_sandbox,singularityou um plugin): os caminhos são do sandbox, não do host. Não há checkpoints nem ledger, e o/rollbacklista os do host mas recusadiffe restore — no CLI, no gateway, na TUI e no Desktop, com o/diff sessiona responder o mesmo.localesshnão são afetados. - Repositórios git aninhados: um checkpoint do diretório-mãe guarda o repo aninhado como gitlink — a referência de um commit, não uma cópia dos ficheiros. As edições não commitadas lá dentro não são recuperáveis, e os checkpoints novos vêm rotulados:
before write_file: app.py [nested git repos not captured: tool]. Com gitlinks no checkpoint escolhido, o rollback total é recusado antes de mexer em nada, mesmo com--all. Peça-se o próprio repo aninhado, um ficheiro lá dentro ou um pathspec que o apanhe, a recusa é a mesma — só ficheiros capturados e não relacionados passam, como/rollback 1 notes.txt. - Migração v1→v2: os shadow repos antigos, um por diretório, foram movidos para
legacy-<timestamp>/. A história antiga só é alcançável inspecionando a pasta comgit; oauto_prunetambém o varre ao fim dosretention_days. - O
.checkpoints.lock, ao lado da base, coordena processos. Não o apagues com o Hermes a correr.
Vale a pena?
Sim, se deixas o agente escrever no repo sem estares a olhar para cada linha. O store é por perfil — ~/.hermes/checkpoints/ no perfil default, ao lado dos dados do perfil nos perfis com nome — e a v2 trouxe o que faltava: um store único e partilhado, com dedupe de objetos entre projetos e turnos. Não substitui o git. É outra coisa: o passo atrás que faz falta quando um patch corre mal.
Docs: Checkpoints and /rollback · Referência CLI · Slash commands
Tags: #hermes #checkpoints #cli