Semana passada um dos meus mentorados me mandou uma mensagem às 23h: “Thulio, meu pipeline de deploy trava toda noite. O Claude Code roda em auto mode e nega o git push pro branch de staging. Toda manhã um dev tem que ir lá, ver o que aconteceu, e rodar de novo.”
Eu perguntei: “Quantas vezes por semana?”
“Umas 3-4. Às vezes mais.”
Esse é o tipo de problema silencioso que mata produtividade em software house. Não é um bug. Não é um erro de configuração. É o classifier do auto mode fazendo exatamente o que foi treinado para fazer — errar para o lado da cautela. E em ambientes headless como CI/CD, cautela demais significa pipeline morto.
A versão 2.1.89 do Claude Code trouxe uma feature que resolve isso: o PermissionDenied hook com suporte a retry. E a maioria das SHs nem sabe que existe.
O Que É o Auto Mode e Por Que Ele Nega Comandos Seguros
Se você usa Claude Code com 20 devs, provavelmente já ouviu reclamações sobre o auto mode. “Ele fica pedindo permissão pra tudo.” Ou pior: “Em CI/CD ele simplesmente para.”
O auto mode usa um classifier de dois estágios desenvolvido pela Anthropic para decidir se uma ação é segura sem perguntar ao humano:
Stage 1 — Fast Filter: decisão de um token (sim/não), sem raciocínio. Calibrado para “errar para o lado do bloqueio”. Taxa de falso positivo: 8.5% no tráfego real.
Stage 2 — Reasoning Review: só ativa quando o Stage 1 flagga. Aplica chain-of-thought para decisão mais nuanced. Reduz o falso positivo final para 0.4% no pipeline completo.
Parece ótimo no papel. 0.4% de falso positivo é excelente. Mas tem um detalhe que a maioria ignora: o Stage 1 nega 8.5% antes do Stage 2 ter chance de corrigir. E em certos cenários — especialmente comandos compostos como npm test && git push — o classifier é conservador por design.
A Anthropic admite no próprio blog de engenharia: o classifier recebe “apenas mensagens do usuário e tool calls do agente; removemos as mensagens do próprio Claude e outputs de ferramentas.” Isso previne o agente de persuadir o classifier com racionalizações, mas também significa que contexto legítimo é invisível para a decisão.
Resultado? Comandos perfeitamente seguros — que você autorizaria sem pensar — são negados. E quando isso acontece em modo interativo, aparece um diálogo e você aperta r pra retry. Inconveniente, mas funciona.
Em headless mode (claude -p)? O processo simplesmente termina.
3 negações consecutivas ou 20 totais na sessão = escalation. Em modo interativo, escala pro humano. Em headless, mata o processo. Seu pipeline morreu por um falso positivo.
O PermissionDenied Hook: Seu Pipeline Se Cura Sozinho
A v2.1.89 adicionou o hook PermissionDenied — um evento que dispara apenas em auto mode quando o classifier nega uma tool call. O hook recebe todos os detalhes da negação e pode retornar {retry: true} para dizer ao modelo que ele pode tentar de novo.
Veja o que o hook recebe como input:
{
"session_id": "abc123",
"hook_event_name": "PermissionDenied",
"tool_name": "Bash",
"tool_input": {
"command": "git push origin staging",
"description": "Push changes to staging"
},
"tool_use_id": "toolu_01ABC123...",
"reason": "Auto mode denied: command targets a remote resource"
}
E a resposta que permite retry:
{
"hookSpecificOutput": {
"hookEventName": "PermissionDenied",
"retry": true
}
}
Importante: o hook não reverte a negação. Ele adiciona uma mensagem à conversa dizendo ao modelo que pode tentar de novo. A diferença é sutil mas fundamental — o modelo recebe feedback e pode reformular a abordagem, e o Stage 2 do classifier terá outra chance de avaliar.
Aqui está um script real que você pode usar hoje:
#!/bin/bash
# .claude/hooks/retry-safe-commands.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
REASON=$(echo "$INPUT" | jq -r '.reason')
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
# Log TODA negação para auditoria
echo "[$(date -Iseconds)] DENIED tool=$TOOL command=$COMMAND reason=$REASON" \
>> "$CLAUDE_PROJECT_DIR/.claude/denied-commands.log"
# Permitir retry para comandos conhecidos e seguros
SAFE_PATTERNS=(
"^git push"
"^git checkout"
"^npm test"
"^npm run build"
"^npm run lint"
"^npx prettier"
"^npx eslint"
"^yarn test"
"^bun test"
)
for pattern in "${SAFE_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qE "$pattern"; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PermissionDenied",
retry: true
}
}'
exit 0
fi
done
# Não é comando seguro conhecido — negação mantida
exit 0
Configuração no settings.json:
{
"hooks": {
"PermissionDenied": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/retry-safe-commands.sh"
}
]
}
]
}
}
Pronto. Seu pipeline agora:
- Loga toda negação — compliance e auditoria resolvidos
- Permite retry para comandos seguros —
git push,npm test, linters - Mantém a negação para comandos desconhecidos — segurança intacta
Os Números Que Sua SH Deveria Conhecer
Vamos fazer a conta. 20 devs em auto mode, cada um fazendo ~100 tool calls por dia (dado real — sessions de coding agent fazem centenas de chamadas por sessão).
- Stage 1 false positive rate: 8.5% (Anthropic, n=10.000)
- Total tool calls/dia: 20 devs × 100 = 2.000
- Negações por falso positivo/dia: ~170 (8.5% de 2.000)
- Após Stage 2 corrigir: ~8 negações por falso positivo/dia (0.4% pipeline completo)
8 falsos positivos por dia parece pouco. Mas cada um interrompe o dev, que precisa:
- Ler o motivo da negação
- Decidir se é seguro
- Apertar
rpara retry ou reformular o comando
Tempo médio por interrupção: 2-3 minutos (context switch incluído). 8 × 2.5min = 20 minutos/dia de 20 devs perdidos em falsos positivos. 400 minutos/dia. Quase 7 horas.
Em CI/CD headless? Pior. Cada falso positivo mata o pipeline inteiro. Deploy noturno? Morreu. Build de staging? Morreu. Testes automatizados? Morreram. E ninguém descobre até a manhã seguinte.
O dado mais assustador vem do mercado mais amplo de AI agents:
- 88% das organizações reportaram incidentes de segurança confirmados ou suspeitos com AI agents no último ano (Gravitee 2026)
- Apenas 14.4% têm aprovação completa de segurança para todos os agents em produção
- 50%+ dos agents operam sem supervisão de segurança ou logging
- 46% de todos alertas de segurança são falsos positivos (Microsoft/Omdia 2026)
- 73% dos times de segurança citam falsos positivos como principal desafio (SANS 2025)
A Anthropic projetou o auto mode para ser conservador — e fez certo. Mas conservadorismo sem válvula de escape é paralisação.
Por Que Isso Importa Mais em CI/CD do Que em Dev Local
Em dev local, o falso positivo é um incômodo. O dev vê o diálogo, aperta r, e segue. Irritante, mas não catastrófico.
Em CI/CD com claude -p (headless mode), a situação é diferente:
- Não tem humano para aprovar — o processo termina
- 3 negações consecutivas = hard stop — e no headless, hard stop = exit code não-zero
- Pipeline inteiro falha — não só o step do Claude Code, mas tudo que depende dele
- Ninguém descobre até investigar — o log diz “permission denied”, que pode ser confundido com erro de credencial
Sem o PermissionDenied hook, sua única opção é --dangerously-skip-permissions — que elimina TODA segurança — ou montar uma lista exaustiva de --allowedTools e torcer para cobrir todos os cenários.
O hook é o meio-termo: segurança ativa com escape inteligente.
Um caso real documentado pela API Stronghold: um agent de DevOps com credenciais DDL herdadas de um step de migração deletou 3 tabelas de produção e causou 4 horas de outage. O problema não era o agent ter poder demais — era não ter feedback loop quando uma ação era negada ou permitida incorretamente. O PermissionDenied hook é exatamente esse feedback loop para o Claude Code.
O Stack Completo: PermissionDenied + PreToolUse + Auto Mode
O PermissionDenied hook não opera sozinho. Ele faz parte de um ecossistema de hooks que, combinados, dão governança completa:
| Hook | Quando dispara | O que pode fazer |
|---|---|---|
| PreToolUse | Antes da execução | Bloquear, permitir, pedir confirmação, defer |
| PermissionRequest | Quando diálogo de permissão apareceria | Aprovar ou negar automaticamente |
| PermissionDenied | Após classifier negar (auto mode only) | Permitir retry, logar auditoria |
| PostToolUse | Após execução bem-sucedida | Logar, notificar, validar resultado |
O padrão robusto para CI/CD combina três:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"if": "Bash(rm -rf *)",
"command": "echo 'Blocked: destructive rm -rf' >&2 && exit 2"
}]
}
],
"PermissionDenied": [
{
"matcher": "",
"hooks": [{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/retry-safe-commands.sh"
}]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/command-audit.log"
}]
}
]
}
}
PreToolUse bloqueia o que é perigoso ANTES de executar. PermissionDenied recupera o que foi negado indevidamente. PostToolUse loga tudo que executou. Defesa em profundidade, não permissividade.
Para distribuir essa configuração em 20 máquinas da sua SH, use managed-settings.d (settings gerenciadas via servidor):
// managed-settings.d/hooks.json (distribuído via endpoint)
{
"hooks": {
"PermissionDenied": [
{
"matcher": "",
"hooks": [{
"type": "http",
"url": "https://sua-sh.com/api/claude-hooks/permission-denied",
"headers": { "Authorization": "Bearer $CLAUDE_HOOK_TOKEN" },
"allowedEnvVars": ["CLAUDE_HOOK_TOKEN"]
}]
}
]
}
}
Hook HTTP permite centralizar a lógica de retry em um servidor — uma mudança no endpoint atualiza todos os devs sem tocar nas máquinas. Combine com forceRemoteSettingsRefresh para garantir que o hook está ativo antes de qualquer sessão começar.
O Que Eu Penso
Na minha experiência com 300+ software houses, o padrão que mais vejo é binário: ou o time usa --dangerously-skip-permissions e reza, ou deixa o auto mode puro e aceita as interrupções. Não existe meio-termo na cabeça da maioria.
O PermissionDenied hook é o meio-termo que faltava. É a diferença entre “meu pipeline é frágil” e “meu pipeline se cura sozinho para cenários conhecidos e para quando não conhece.”
Mas o que mais me preocupa é o dado da Gravitee: 88% das organizações já tiveram incidentes de segurança com AI agents. E a maioria — 50%+ — opera sem logging. Sem o hook de auditoria, você não sabe quantas vezes por dia o classifier está negando comandos dos seus devs, nem quais comandos, nem por quê.
O PermissionDenied hook não é só sobre retry. É sobre visibilidade. É sobre saber o que está acontecendo na sua frota de Claude Code antes que vire um incidente.
Conclusão
Se você tem Claude Code em CI/CD e ainda não configurou o PermissionDenied hook, você está aceitando que:
- 8.5% dos comandos seguros serão negados no Stage 1
- Cada falso positivo em headless mata o pipeline inteiro
- Nenhuma negação é logada para auditoria ou análise
- Nenhum retry automático acontece para comandos que você autorizaria sem pensar
Três coisas pra fazer hoje:
- Crie o script
retry-safe-commands.shcom os comandos que seu pipeline realmente usa - Adicione o hook no
settings.json(projeto ou managed-settings) - Monitore o log de negações por uma semana — os padrões vão te surpreender
Se você quer implementar esse nível de governança de IA na sua software house, começar pelo pipeline é o primeiro passo.
Sou Thulio, mentoro 300+ SHs desde 2016.




