Home / Claude Code / PermissionDenied Hook: Seu Auto Mode Bloqueia Ações e Você Nem Sabe?

PermissionDenied Hook: Seu Auto Mode Bloqueia Ações e Você Nem Sabe?

PermissionDenied Hook: Seu Auto Mode Bloqueia Ações e Você Nem Sabe?

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:

1. Stage 1 — Fast filter: decisão binária (bloquear ou permitir) em um único token. Rápido.

2. 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:

1. O Claude recebe uma mensagem: “ação negada, encontre um caminho alternativo”

2. O Claude tenta outra abordagem — que muitas vezes é bloqueada também (mesmo falso positivo, padrão similar)

3. 3 bloqueios consecutivos OU 20 bloqueios totais na sessão → auto mode pausa completamente

4. O Claude Code volta a pedir permissão manual pra cada ação

5. 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 destrutivas
  • **`auto mode`** é seguro mas pode travar — false positives pausam a execução sem aviso
  • **`default`** é 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](https://thuliobittencourt.com/claude-code-http-hooks-controle-centralizado-software-house/) Todas as tool calls via webhook v2.1.63
    [StopFailure](https://thuliobittencourt.com/claude-code-stopfailure-hook-alarme-automacao-ia/) Erros de API (rate limit, auth) v2.1.78
    [Conditional `if`](https://thuliobittencourt.com/claude-code-conditional-hooks-if-performance-software-house/) Filtragem cirúrgica por args v2.1.85
    [CwdChanged/FileChanged](https://thuliobittencourt.com/claude-code-cwdchanged-filechanged-hooks-environment-reativo-software-house/) 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:

    1. Infraestrutura confiável: repos, buckets, serviços internos que o classifier deve considerar seguros

    2. Categorias de bloqueio: ações que sempre devem ser barradas

    3. 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.

    Marcado:

    Deixe um Comentário

    O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *