Imagina o seguinte cenário: você configurou o Claude Code em auto mode pra rodar uma refatoração longa enquanto faz uma call. Quando volta, o agente está parado. Nada aconteceu nos últimos 40 minutos. O classifier do auto mode bloqueou uma ação que parecia arriscada — mas era perfeitamente segura — e o Claude ficou ali, tentando, sendo bloqueado de novo, até atingir o limite e pausar.
Na minha experiência com 300+ software houses, essa é a reclamação número um de quem adota auto mode: “funciona lindo até parar sem avisar.”
A Anthropic acabou de lançar na versão 2.1.88 um hook que resolve exatamente isso: o PermissionDenied. E se você usa auto mode em qualquer capacidade — seja interativo, pipeline CI/CD ou agent teams — precisa entender o que ele faz.
O que é o PermissionDenied Hook
O PermissionDenied é um novo evento de hook que dispara imediatamente após o classifier do auto mode negar uma tool call. Ele não impede o bloqueio — o classifier já decidiu. Mas ele te dá a chance de reagir: logar o evento, notificar alguém, e até permitir que o modelo tente novamente retornando {retry: true}.
Pra entender o valor, precisa entender o problema que ele resolve.
Como o auto mode decide
O auto mode usa o Claude Sonnet 4.6 como classifier separado. Não é o mesmo modelo que está trabalhando no seu código — é uma instância dedicada que avalia cada tool call antes de executar. Funciona em dois estágios:
- Stage 1 — Fast filter: decisão binária (bloquear ou permitir) em um único token. Rápido.
- Stage 2 — Chain-of-thought: se o Stage 1 sinaliza risco, o classifier faz raciocínio profundo antes de decidir.
Detalhe crucial: o classifier não vê a resposta do Claude nem os resultados de ferramentas anteriores. Ele vê apenas as mensagens do usuário e as tool calls. Isso é uma proteção contra prompt injection — conteúdo malicioso dentro de arquivos ou páginas web não consegue manipular a decisão do classifier.
As estatísticas reais
A Anthropic publicou os números do classifier com transparência rara:
| Métrica | Tráfego real (n=10k) | Ações overeager reais (n=52) | Exfiltração sintética (n=1k) |
|---|---|---|---|
| Stage 1 FPR | 8,5% | 6,6% FNR | 1,8% FNR |
| Pipeline completo | 0,4% FPR | 17% FNR | 5,7% FNR |
Traduzindo: a cada 250 tool calls legítimas, 1 é bloqueada por engano (0,4% de falsos positivos). Parece pouco? Faça a conta numa sessão longa com agent teams rodando 500+ tool calls. São 2 bloqueios falsos. E cada um desses bloqueios pode cascatear.
O efeito cascata sem o hook
Sem o PermissionDenied hook, quando o classifier bloqueia uma ação:
- O Claude recebe uma mensagem: “ação negada, encontre um caminho alternativo”
- O Claude tenta outra abordagem — que muitas vezes é bloqueada também (mesmo falso positivo, padrão similar)
- 3 bloqueios consecutivos OU 20 bloqueios totais na sessão → auto mode pausa completamente
- O Claude Code volta a pedir permissão manual pra cada ação
- Em modo não-interativo (
-p): a sessão é abortada
Esse é o cenário silencioso que destrói pipelines de CI/CD. O agente trava, a pipeline fica hanging, e só descobre quando o timeout mata o processo.
Como funciona na prática
Configuração básica
O PermissionDenied hook se configura como qualquer outro hook no settings.json:
{
"hooks": {
"PermissionDenied": [
{
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PermissionDenied\",\"retry\":true}}'"
}
]
}
]
}
}
Esse é o caso mais simples: sempre permite retry. Quando o classifier bloqueia qualquer ação, o hook retorna {retry: true} e o modelo tenta novamente. Útil pra ambientes onde você confia no escopo do trabalho e quer que falsos positivos não interrompam a execução.
Retry seletivo com logging
Na prática, você não quer retry cego. Você quer logar e decidir:
#!/bin/bash
# .claude/hooks/handle-denial.sh
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
REASON=$(echo "$INPUT" | jq -r '.denial_reason // "unknown"')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Loga pra auditoria
echo "{\"timestamp\":\"$TIMESTAMP\",\"tool\":\"$TOOL_NAME\",\"reason\":\"$REASON\"}" >> ~/.claude/denial-audit.log
# Retry seletivo: permite retry pra git e npm, mas não pra Bash genérico
case "$TOOL_NAME" in
"Bash")
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
if echo "$COMMAND" | grep -qE "^(git|npm|yarn|pnpm) "; then
echo '{"hookSpecificOutput":{"hookEventName":"PermissionDenied","retry":true}}'
else
echo '{"hookSpecificOutput":{"hookEventName":"PermissionDenied","retry":false}}'
fi
;;
*)
echo '{"hookSpecificOutput":{"hookEventName":"PermissionDenied","retry":true}}'
;;
esac
{
"hooks": {
"PermissionDenied": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/handle-denial.sh"
}
]
}
]
}
}
Notificação via Slack/webhook
Pra quem roda pipelines autônomas, a combinação com HTTP hooks é natural:
{
"hooks": {
"PermissionDenied": [
{
"hooks": [
{
"type": "http",
"url": "https://hooks.slack.com/services/T00/B00/xxx",
"headers": { "Content-Type": "application/json" }
}
]
}
]
}
}
O endpoint Slack recebe o JSON do evento com tool_name, denial_reason e pode postar no canal da equipe: “⚠️ Auto mode bloqueou git push na sessão abc123 — verificar.”
Por que isso importa para sua Software House
O problema real: auto mode em produção
Toda software house que escala automação com IA enfrenta o mesmo dilema:
bypassPermissionsé rápido mas perigoso — zero proteção contra prompt injection ou ações destrutivasauto modeé seguro mas pode travar — false positives pausam a execução sem avisodefaulté seguro mas lento — pede permissão pra tudo
O PermissionDenied hook resolve o “mas” do auto mode. Ele transforma falsos positivos de interrupção silenciosa em eventos observáveis e acionáveis.
Custo operacional de denials não-tratados
Faz a conta:
- Uma sessão com agent teams faz 500 tool calls
- 0,4% de FPR = 2 bloqueios falsos por sessão
- Se 1 desses gera cascata → 3 denials consecutivos → auto mode pausa
- Pipeline de CI/CD com
-p: sessão abortada - Tempo do dev pra diagnosticar: 30+ minutos (“por que a pipeline falhou?”)
- Com o hook: zero downtime — retry automático, log pra auditoria
Se sua SH roda 10 pipelines por dia com auto mode, são potencialmente 20 falsos positivos por dia. Sem observabilidade, você só descobre quando algo quebra.
Completando o ecossistema de hooks
Se você acompanha esta série, o PermissionDenied fecha um gap importante:
| Hook | O que monitora | Artigo |
|---|---|---|
| HTTP Hooks | Todas as tool calls via webhook | v2.1.63 |
| StopFailure | Erros de API (rate limit, auth) | v2.1.78 |
Conditional if |
Filtragem cirúrgica por args | v2.1.85 |
| CwdChanged/FileChanged | Mudança de diretório e arquivos | v2.1.83 |
| PermissionDenied | Bloqueios do auto mode classifier | v2.1.88 |
Juntos, esses hooks formam uma camada de observabilidade completa sobre o Claude Code. Você sabe quando uma tool call é executada (PostToolUse), quando falha (PostToolUseFailure/StopFailure), quando o environment muda (CwdChanged/FileChanged), e agora quando o auto mode bloqueia algo (PermissionDenied).
Detalhe técnico: o classifier por dentro
Pra quem quer entender a fundo — porque na minha experiência, donos de SH que entendem a mecânica tomam decisões melhores sobre quando confiar e quando não confiar.
O classifier do auto mode tem uma arquitetura fixa com três slots customizáveis:
- Infraestrutura confiável: repos, buckets, serviços internos que o classifier deve considerar seguros
- Categorias de bloqueio: ações que sempre devem ser barradas
- Exceções: overrides pra casos específicos
As 4 categorias padrão de bloqueio:
- Destruir ou exfiltrar — deletar dados, enviar info pra endpoints externos
- Degradar postura de segurança — modificar permissões, desabilitar proteções
- Cruzar fronteiras de confiança — acessar sistemas que o classifier não reconhece
- Bypass de review — pular verificações, force push, deploy direto
E aqui está o ponto-chave: o classifier não conhece a infra da sua empresa por padrão. Seus repos internos, buckets S3, serviços — tudo é “externo não-confiável” até você configurar. É por isso que muitos false positives acontecem em SHs: o classifier bloqueia push pro repo da empresa porque não sabe que é confiável.
A solução: configurar autoMode.environment nos managed settings. Mas mesmo com isso, false positives vão acontecer. E é aí que o PermissionDenied hook salva.
O que eu penso
Eu venho acompanhando a evolução dos hooks do Claude Code desde a v2.1.63. Cada release adiciona uma peça no quebra-cabeça da observabilidade. O PermissionDenied era a peça que faltava pra fechar o ciclo do auto mode.
Minha provocação: se você está usando auto mode sem nenhum hook configurado, você está voando cego. É como rodar um servidor em produção sem monitoring. Funciona 99,6% do tempo — mas os 0,4% que não funcionam são exatamente os que precisam de atenção.
O PermissionDenied hook não é sexy. Não é uma feature que vai pra demo no pitch de vendas. Mas é a diferença entre uma automação que funciona e uma automação que funciona e você sabe quando não funciona.
E na minha experiência mentoringando 300+ software houses: saber quando algo não funciona é mais valioso do que fazer funcionar.
Conclusão
O PermissionDenied hook transforma denials silenciosos do auto mode em eventos que você pode interceptar, logar e reagir. Com {retry: true}, falsos positivos viram inconveniências de 1 retry em vez de sessões abortadas. Com logging, você tem auditoria completa de tudo que o classifier bloqueia. Com webhooks, sua equipe é notificada em tempo real.
Se você usa auto mode — ou planeja usar — configure esse hook antes de colocar em pipeline de produção. É 5 minutos de setup que podem economizar horas de diagnóstico.
Se você quer implementar esse nível de automação com IA na sua software house, vem pro nosso próximo evento presencial. É onde a gente destrincha isso na prática, com código rodando ao vivo.
Sou Thulio, mentoro 300+ SHs desde 2016.



