Hooks no Claude Code: guardrails, contexto e automação no ciclo de execução
English summary: Claude Code hooks make operational policy executable at lifecycle boundaries. This production guide explains where pre- and post-tool hooks help, where they do not, and how to build small, observable, idempotent guardrails without turning automation into a hidden control plane.
TL;DR
- Hooks tornam políticas operacionais executáveis.
- Bloqueie antes do side effect e registre depois com parcimônia.
- Teste idempotência, timeout, saída e comportamento de falha.
Exemplo concreto: um hook pode negar `deploy` quando não há aprovação, antes que qualquer side effect aconteça.
Um agente pode receber uma instrução impecável e ainda rodar o comando errado no lugar errado.
Esse é o ponto em que muita estratégia de IA para engenharia quebra: o time tenta resolver um problema operacional com um lembrete em linguagem natural. “Não mexa em produção”, “rode testes”, “não exponha segredos” e “use rg, não grep” acabam presos em um prompt que compete com urgência, contexto parcial e dezenas de decisões no meio do loop.
A tese deste artigo é simples: hooks do Claude Code são úteis quando transformam uma regra pequena, objetiva e verificável em uma decisão executável no ponto exato do ciclo de vida; eles são perigosos quando viram uma segunda aplicação invisível, com permissões demais e sem dono.
Vamos focar no que funciona em produção: lifecycle hooks, PreToolUse e PostToolUse, validação, bloqueio, logging seguro, idempotência, limites e verificação. O objetivo não é “automatizar tudo”. É reduzir classes concretas de erro sem criar um plano de controle impossível de auditar.
O que um hook é — e o que ele não é
No Claude Code, hooks são comandos shell, endpoints HTTP, chamadas MCP ou prompts que rodam automaticamente em eventos do ciclo de vida. Eles recebem contexto em JSON e podem, conforme o evento, adicionar contexto, registrar um fato, pedir aprovação ou bloquear uma ação. A referência oficial lista eventos de sessão, de turno e do loop agentic; entre os últimos estão PreToolUse, PostToolUse, PostToolUseFailure e PostToolBatch.1
Isso torna o hook uma peça de controle de fronteira. Ele não é o cérebro do agente e não deveria reimplementar seu plano.
| Mecanismo | Pergunta que responde | Melhor uso | Limite principal |
|---|---|---|---|
CLAUDE.md e skills | “Como este projeto funciona?” | contexto, convenções e playbooks | instrução não é imposição |
| Permissões | “Esta categoria de ação é permitida?” | política estável e escopo de tools | pode ser ampla demais para condição contextual |
| Hook | “Esta ação, agora, passa nesta regra?” | validação de fronteira, bloqueio e telemetria | adiciona latência e superfície operacional |
| CI/CD | “A mudança entregue atende ao contrato?” | testes, análise estática, deploy gates | normalmente só vê depois do trabalho local |
| Workflow comum | “Qual passo fixo vem agora?” | tarefas determinísticas | não precisa de loop agentic |
Um hook também não é um firewall, um SIEM, um mecanismo de autorização corporativa nem uma garantia de que produção está segura. Se um comando tem credenciais de produção e pode ser executado fora do Claude Code, a proteção real precisa existir no provedor, na identidade e na pipeline. O hook reduz risco no caminho que ele observa; não elimina as outras rotas.
Há casos em que um hook é a escolha errada. Formatar todos os arquivos ao salvar pode caber melhor no editor ou no linter. Validar um pull request inteiro deve continuar na CI. Uma decisão que depende de semântica de negócio ambígua deve parar para uma pessoa, em vez de ser escondida em um script de 400 linhas. Use hook para regra de borda; use workflow para sequência previsível; use humano para exceção relevante.
As quatro falhas que aparecem antes do primeiro incidente
1. Policy as prose
O repositório declara “nunca faça deploy” em um documento, mas nada impede Bash de invocar um deploy. A regra até pode estar no contexto do modelo; não está no caminho de execução.
O antídoto é escolher poucas invariantes que podem ser decididas sem interpretação. “Nunca terraform apply neste projeto” é uma boa candidata. “Não faça mudanças arriscadas” não é: alguém precisa definir risco, escopo e exceções.
2. O hook que bloqueia tarde demais
PostToolUse acontece depois que a tool concluiu. Ele é excelente para registrar um evento, iniciar uma verificação ou acrescentar feedback ao agente, mas não desfaz um arquivo gravado, um comando executado ou uma requisição enviada. A documentação também deixa claro que substituir a saída pós-tool muda o que o modelo vê, não o efeito que já ocorreu.1
Para prevenir um efeito, a fronteira é PreToolUse. Para aprender com um efeito já ocorrido, a fronteira é PostToolUse ou PostToolUseFailure. Confundir os dois produz um “guardrail” que só explica o acidente.
3. Logging que vaza o que deveria proteger
Receber JSON completo no stdin é útil para decisão, mas é uma péssima justificativa para gravar tudo. Comandos podem conter tokens, URLs assinadas, nomes de clientes, paths internos e argumentos sensíveis. Saídas podem carregar PII ou dados de banco. Um arquivo de auditoria com esses valores vira um novo ativo sensível, frequentemente sem retenção, acesso ou alerta definidos.
Logue o mínimo que permite responder “qual regra decidiu o quê e quando?”: evento, tool, resultado, versão da política e identificador técnico pseudônimo. Não registre prompt, comando, conteúdo de arquivo, resposta de tool ou segredo. E não imprima diagnósticos no stdout de um hook que precisa devolver JSON: a documentação exige que esse stdout contenha somente o objeto JSON de controle.1
4. Automação não idempotente e sem dono
PostToolUse pode disparar em chamadas paralelas, e retries existem. Um hook que abre ticket, manda mensagem ou altera estado remoto em toda invocação cria duplicatas e ruído. Pior: ninguém sabe se o dono é a plataforma, segurança ou o time que criou o script.
Toda ação com efeito deve ter uma chave de deduplicação, prazo de retenção e responsável. Quando não houver uma chave confiável, prefira logging local de baixo risco e deixe uma automação posterior consolidar os eventos. “Rodou sem erro” não prova correção; prova apenas que o processo chegou ao fim.
O harness Claude Code: onde o hook se encaixa
Um agente confiável não é apenas um modelo com terminal. Ele combina contexto, tools, permissões, ambiente e verificação. O hook entra como uma camada estreita entre a intenção do modelo e a ação observável.
flowchart LR
A[Contexto: CLAUDE.md e skills] --> B[Modelo decide usar tool]
B --> C[Permissões e PreToolUse]
C -->|nega ou pede aprovação| H[Humano ou alternativa segura]
C -->|permite| D[Tool executa no ambiente]
D --> E[PostToolUse ou Failure]
E --> F[Verificação, log mínimo e feedback]
F --> B
Essa ordem importa. As permissões são a política ampla: ferramentas ou padrões que em geral devem ser negados, perguntados ou permitidos. O PreToolUse é a avaliação contextual antes da chamada. O PostToolUse observa o resultado já produzido. Testes, git diff, linters e CI fecham a verificação fora do hook.
As permissões continuam prevalecendo como defesa em camadas. Segundo a documentação, uma regra deny ou ask aplicável não é anulada por um allow devolvido pelo hook; hooks de bloqueio também têm precedência sobre regras permissivas.2 Isso é uma boa propriedade: o script não deve ter poder para ampliar uma política mais restritiva por acidente.
Uma escada incremental, em vez de um painel de automações
Evolua em degraus. Não pule para hooks HTTP e integrações remotas porque o JSON parece poderoso.
- Trabalho — descreva o comportamento a proteger em uma frase: “comandos que alteram infraestrutura precisam de aprovação humana”.
- Contrato — registre a regra, o dono, a exceção e a evidência exigida em
CLAUDE.mdou runbook. - Skill — transforme o playbook repetido, como “preparar deploy”, em instruções reutilizáveis.
- Agent — especialize um subagent quando o trabalho precisa de contexto isolado, não quando só precisa de um
if. - Hooks/MCP — adicione um
PreToolUselocal para regra determinística; conecte dados externos apenas quando aumentarem a qualidade da decisão. - Autonomia limitada — permita ações reversíveis e mantenha
askpara transições sensíveis. - Evals — teste entradas permitidas, bloqueadas, compostas, malformadas e repetidas; acompanhe falsos positivos e escapes.
O ganho dessa escada é reversibilidade. Um shell hook no projeto pode ser revisado junto do código. Um endpoint que decide permissões para toda a empresa exige autenticação, versionamento, disponibilidade, observabilidade e processo de mudança. Comece no menor mecanismo que prova valor.
Artefato copiável: bloquear, pedir aprovação e auditar sem vazar conteúdo
O exemplo abaixo é deliberadamente pequeno. Ele usa jq, disponível em muitos ambientes de desenvolvimento, e dois hooks de projeto. O primeiro barra padrões destrutivos e pede aprovação para git push; o segundo escreve uma auditoria mínima. Ele não substitui controles do seu cloud, não entende todos os shells e não deve ser copiado sem ajustar os padrões e o owner.
Crie .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-bash.sh",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash|Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-tool-use.sh",
"timeout": 5
}
]
}
]
}
}
Crie .claude/hooks/guard-bash.sh e marque-o como executável (chmod +x .claude/hooks/guard-bash.sh):
#!/usr/bin/env bash
set -euo pipefail
input="$(cat)"
command="$(jq -r '.tool_input.command // empty' <<<"$input")"
deny() {
jq -n --arg reason "$1" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: $reason
}
}'
}
ask() {
jq -n --arg reason "$1" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: $reason
}
}'
}
case "$command" in
*"terraform apply"*|*"kubectl "*"prod"*|*"git push --force"*|*"rm -rf /"*)
deny "Blocked by project policy: destructive or production-like command."
;;
*"git push "*)
ask "A remote write needs explicit human approval."
;;
*)
exit 0
;;
esac
E crie .claude/hooks/audit-tool-use.sh, também executável:
#!/usr/bin/env bash
set -euo pipefail
# Best-effort, local audit: no prompts, commands, file contents or tool output.
input="$(cat)"
tool_use_id="$(jq -r '.tool_use_id // empty' <<<"$input")"
tool_name="$(jq -r '.tool_name // "unknown"' <<<"$input")"
# A missing ID means no safe deduplication key. Do not invent one from sensitive input.
[ -n "$tool_use_id" ] || exit 0
runtime_dir="${CLAUDE_PROJECT_DIR}/.claude/runtime"
seen_dir="${runtime_dir}/hook-seen"
mkdir -p "$seen_dir"
# mkdir is atomic on a local filesystem: retries for the same tool use do not log twice.
mkdir "${seen_dir}/${tool_use_id}" 2>/dev/null || exit 0
jq -cn \
--arg time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg id "$tool_use_id" \
--arg tool "$tool_name" \
--arg policy "hooks-v1" \
'{time: $time, event: "PostToolUse", tool_use_id: $id, tool: $tool, policy: $policy}' \
>> "${runtime_dir}/tool-audit.jsonl"
O primeiro script escolhe uma semântica de resposta: sai com código zero e escreve JSON estruturado. Isso permite deny e ask. Também seria possível sair com código 2 para bloquear um PreToolUse, mas não se deve misturar essa saída com JSON: Claude Code ignora JSON quando o hook sai com código 2.3
O segundo script não anuncia uma garantia de entrega. Ele usa tool_use_id como chave de deduplicação e mkdir como operação atômica local; se o ID não vier, não inventa um hash de prompt ou comando. O diretório .claude/runtime/ deve ser ignorado pelo Git e ter retenção definida. Em produção, substitua esse arquivo por um coletor autorizado, com acesso restrito, rotação e deduplicação também no destino.
Teste o contrato antes de ativar em um time:
printf '%s' '{"tool_name":"Bash","tool_input":{"command":"git push --force origin main"}}' \
| .claude/hooks/guard-bash.sh | jq .
printf '%s' '{"tool_name":"Bash","tool_input":{"command":"pnpm test"}}' \
| .claude/hooks/guard-bash.sh
O primeiro caso deve devolver permissionDecision: "deny"; o segundo não deve escrever nada e deve sair com sucesso. Adicione casos para comandos compostos, aliases, paths esperados e um stdin inválido. Depois abra /hooks para confirmar que o Claude Code carregou a configuração; os detalhes de execução também podem ser gravados com claude --debug-file <caminho>.3
Decisões e trade-offs que precisam ser explícitos
| Decisão | Escolha conservadora | Quando aceitar mais autonomia | Evidência para revisar |
|---|---|---|---|
| Ação remota | ask ou deny | ação reversível, escopo pequeno e owner definido | aprovação, ID da mudança, rollback |
| Logging | metadados mínimos locais | coletor central com acesso e retenção aprovados | amostra sem segredo, política de retenção |
| Falha do hook | falhar fechado para bloqueios críticos | falhar aberto somente para telemetria não crítica | teste de timeout e indisponibilidade |
| Regex/padrões | lista curta de comandos proibidos | parser ou política central após medir escapes | corpus de comandos reais e falsos positivos |
| Pós-tool | verificar e registrar | side effect remoto idempotente | chave de dedupe e teste de retry |
O “fail closed” merece cuidado. Em PreToolUse, falhar fechado protege uma ação crítica, mas um jq ausente pode paralisar trabalho legítimo. A solução não é esconder o erro: é declarar a dependência, testá-la no setup e escolher uma política por classe de risco. Telemetria pode falhar aberta; um bloqueio de produção provavelmente não deveria.
Também evite usar o hook como filtro de prompt injection. Dados vindos de uma tool ou de um MCP podem tentar influenciar o agente, mas um regex sobre texto não é uma defesa geral contra instruções adversariais. Reduza tools e permissões, trate conteúdo externo como não confiável, separe credenciais e mantenha ações de alto impacto sob aprovação. A própria documentação de MCP recomenda cautela com servidores e conteúdo de terceiros.4
Segurança, ownership e verificação
Antes de chamar um hook de “produção”, responda estas perguntas sem abstração:
- Quem é o dono? Um time ou papel nomeado mantém a política, os padrões e o prazo de revisão.
- Qual é a autoridade? O hook apenas recomenda, pede aprovação ou bloqueia? Isso está documentado por evento.
- Quais dados entram e saem? O contrato de log exclui prompt, comando, conteúdo e saída sensível por padrão.
- Como reverter? Desativar ou remover o bloco de configuração restaura o comportamento sem apagar registros necessários.
- Como verificar? Há fixtures para permitir, perguntar, negar, timeout, JSON inválido e retry.
- Quando revisar? Métricas incluem bloqueios, aprovações, erros, latência e falsos positivos; uma revisão periódica decide se a regra continua útil.
Permissões, hooks e CI precisam concordar. Um hook que permite uma ação que a CI bloqueia cria frustração; um hook que bloqueia tudo e faz a equipe desativá-lo cria uma falsa sensação de segurança. Use a telemetria mínima para calibrar padrões. Se as pessoas pedem exceção todos os dias, a regra está mal recortada ou o workflow precisa de uma rota legítima.
Verifique o ciclo completo, não apenas o script
Um teste unitário que alimenta JSON no shell é necessário, mas não suficiente. A configuração pode estar no escopo errado, o matcher pode não corresponder ao nome real da tool, uma regra de permissão pode mudar o resultado ou um hook concorrente pode decidir diferente. Exercite o fluxo em uma cópia segura do repositório: inicie uma sessão, invoque uma ação permitida, uma que pede aprovação e uma que deve ser bloqueada. Confirme na interface que a decisão aparece como esperado e consulte o arquivo de debug apenas em ambiente sem dados sensíveis.
Para regras que dependem de mais de uma tool chamada em paralelo, PostToolBatch pode ser um ponto melhor do que vários PostToolUse: ele roda uma vez após a resolução do lote, antes da próxima chamada ao modelo. Isso reduz mensagens repetidas, mas continua não sendo uma transação distribuída. Se a correção depende de consistência remota, a fonte de verdade precisa deduplicar e validar de novo no destino.1
Checklist operacional
- O evento escolhido consegue realmente prevenir ou apenas observar o efeito?
- A regra cabe em uma frase objetiva e tem uma exceção documentada?
- O
matcherlimita o hook à tool e ao escopo necessários? - O script valida JSON ausente ou malformado sem expor entrada em logs?
-
stdoutcontém somente JSON de protocolo quando há decisão estruturada? - A política distingue
deny,aske observação pós-execução? - Cada side effect tem chave de idempotência e retenção?
- Logs não incluem prompt, comando, conteúdos, tool output ou segredos?
- Timeout, dependências e comportamento fail-open/fail-closed foram testados?
- Há owner, canal de incidente, revisão de regras e evidência de que o hook está carregado?
Conclusão
Hooks não tornam um agente confiável por si só. Eles tornam algumas promessas operacionais executáveis: bloquear uma classe pequena de comando, exigir uma aprovação, injetar contexto de verificação ou registrar um evento sem reter conteúdo perigoso.
O custo é coordenação. Cada hook adiciona um contrato, uma dependência, uma latência e alguém responsável por corrigir um falso positivo às 18h de sexta-feira. Por isso, o sucesso não é o número de automações instaladas. É conseguir demonstrar que uma regra importante passou a ser aplicada no ponto certo, com efeito reversível, logs seguros e menos incidentes ou retrabalho.
Comece por um PreToolUse que resolve uma fronteira real. Meça. Teste as negações e exceções. Só depois avance para context injection, integrações remotas e autonomia. Em sistemas agentic, o guardrail mais valioso raramente é o mais inteligente; é o que ainda funciona quando o contexto está confuso e a ação precisa acontecer agora.
Referências primárias
Notas de rodapé
-
Claude Code Docs, Hooks reference — documentação consultada em 12 de agosto de 2026. ↩ ↩2 ↩3 ↩4
-
Claude Code Docs, Configure permissions — documentação consultada em 12 de agosto de 2026. ↩
-
Claude Code Docs, Automate workflows with hooks — documentação consultada em 12 de agosto de 2026. ↩ ↩2
-
Claude Code Docs, Connect Claude Code to tools via MCP — documentação consultada em 12 de agosto de 2026. ↩