← Voltar ao curso
Nível 3 — Automação e escala · Módulo 3.2 — Hooks: da recomendação à garantia

3.2.3 · PreToolUse: allow, deny, ask — e reescrever

5 min de vídeo TEC BASIS

Objetivo: ao final, o consultor implementa um hook que decide (allow, deny, ask, defer) e um que reescreve a chamada antes de executar, redigindo dado sensível em vez de bloquear o trabalho.

O que você precisa levar desta aula

  1. A decisão de um PreToolUse vai em JSON no stdout, com exit 0. O campo permissionDecision aceita allow, deny, ask e defer — e defer significa "segue o fluxo normal de permissão".
  2. updatedInput reescreve a chamada antes de executar: dá para redigir dado sensível em vez de bloquear, e o trabalho legítimo continua. Ele troca apenas os campos declarados — campos omitidos mantêm o valor original.
  3. PostToolUse tem updatedToolOutput, que reescreve o resultado da ferramenta antes de o Claude ver — redação também no caminho de volta.

A forma da resposta

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Regra Wayon: nenhuma ação automatizada em QAS ou PRD do MRD."
  }
}

O hook escreve isso no stdout e sai com exit 0. Exit 0 significa "executei corretamente, interprete meu JSON" — não significa "aprovado". Quem aprova ou nega é o campo permissionDecision. Essa confusão é o assunto da aula 3.2.4.

As quatro decisões

Valor O que faz Quando usar
allow Libera a chamada sem perguntar ao consultor Ação segura e frequente que não deveria gerar pergunta
deny Barra a chamada e devolve a razão para o Claude Proibido por política — não há o que perguntar (QAS, PRD)
ask Escala para o consultor decidir Depende de contexto que o hook não tem
defer Segue o fluxo normal de permissão, como se o hook não opinasse O hook examinou e concluiu que não é caso dele

Sempre preencha permissionDecisionReason em deny e ask: é o texto que volta para o Claude (em deny) ou aparece para o consultor (em ask). Um deny sem razão faz o Claude tentar de novo, às cegas.

Reescrever em vez de barrar

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "grep '[CPF-ANONIMIZADO]' log-mrd.txt"
    }
  }
}

Como ler isso: o hook detectou um CPF no comando, trocou por placeholder, e liberou. O Claude executa a versão limpa.

A correção que importa: updatedInput substitui apenas os campos declarados. No exemplo, só command foi trocado — qualquer outro argumento da chamada original permanece intacto. Não é preciso reecoar o que não está mudando.

Material mais antigo (inclusive uma versão anterior do blueprint deste curso) afirmava que updatedInput substitui o objeto de input inteiro, e que era preciso reecoar os campos não alterados. Isso está errado e produz hooks mais frágeis do que o necessário. Está registrado em DIVERGENCIAS-BLUEPRINT-VS-DOC.md.

Redação nos dois sentidos

Direção Evento Campo Caso de uso
Entrando na ferramenta PreToolUse updatedInput O comando que o Claude montou contém dado pessoal
Saindo da ferramenta PostToolUse updatedToolOutput A saída do comando trouxe dado que não deveria entrar no contexto

O segundo caso é comum em Basis: você roda uma consulta legítima num log de produção, e o retorno vem cheio de nome de usuário real. updatedToolOutput limpa antes de aquilo virar contexto de sessão — o que resolve, de forma automática, o que a aula 2.8.B1 pedia manualmente.

Regra Wayon Dois hooks são obrigatórios em pasta de projeto de cliente: um PreToolUse que nega ação em QAS e PRD do sistema do cliente, e um par de redação (updatedInput no PreToolUse e updatedToolOutput no PostToolUse) para CPF, CNPJ e nome de usuário real. O segundo par é o que transforma a política de anonimização da Decisão 1 de uma instrução que o consultor precisa lembrar numa garantia que roda sozinha.

📖 Saída JSON de hooks · Modos de permissão — módulo 2.5

Quiz — 4 questões

1.Seu hook PreToolUse detectou um comando que toca o PRD do MRD. Como ele deve responder?
  • a)Sair com exit 1, para sinalizar erro e interromper

    exit 1 não bloqueia — é a pegadinha da aula 3.2.4. E a decisão de negar se expressa em JSON, não em exit code.

  • b)Escrever um JSON com permissionDecision: "deny" e a razão, saindo com exit 0

    Correto. Exit 0 significa "leia meu JSON"; quem nega é o campo permissionDecision.

  • c)Escrever um JSON com permissionDecision: "ask", para o consultor decidir

    ask é para o que depende de contexto que o hook não tem. Ação em PRD é proibida por política — não há o que perguntar.

Ver resposta e por quê
a) exit 1 não bloqueia — é a pegadinha da aula 3.2.4. E a decisão de negar se expressa em JSON, não em exit code.
b) Correto. Exit 0 significa "leia meu JSON"; quem nega é o campo permissionDecision.
c) ask é para o que depende de contexto que o hook não tem. Ação em PRD é proibida por política — não há o que perguntar.
2.Um comando legítimo do Claude inclui um CPF real que ele leu de um log. Você quer que o trabalho continue, sem o CPF passar.
  • a)permissionDecision: "deny", forçando o Claude a montar o comando de novo sem o CPF

    Interrompe trabalho legítimo e depende de o Claude acertar na segunda tentativa — existe caminho melhor.

  • b)permissionDecision: "ask", para o consultor remover o CPF manualmente

    Transfere trabalho manual para o consultor a cada ocorrência, quando o hook já detectou o padrão e pode corrigir sozinho.

  • c)permissionDecision: "allow" com updatedInput trocando o CPF por um placeholder

    Correto. É o padrão "redigir em vez de bloquear": o trabalho acontece, o dado pessoal não passa.

Ver resposta e por quê
a) Interrompe trabalho legítimo e depende de o Claude acertar na segunda tentativa — existe caminho melhor.
b) Transfere trabalho manual para o consultor a cada ocorrência, quando o hook já detectou o padrão e pode corrigir sozinho.
c) Correto. É o padrão "redigir em vez de bloquear": o trabalho acontece, o dado pessoal não passa.
3.Ao usar updatedInput para trocar o campo command de uma chamada de Bash, o que acontece com os outros argumentos da chamada original?
  • a)Permanecem com o valor original — updatedInput substitui apenas os campos declarados

    Correto. Não é preciso reecoar o que não está mudando.

  • b)São apagados, porque updatedInput substitui o objeto de input inteiro

    Era o que material antigo afirmava, e está errado: campos omitidos mantêm o valor original.

  • c)São mantidos, mas apenas se você declarar preserveFields: true

    Não existe esse campo — a preservação dos campos omitidos é o comportamento padrão.

Ver resposta e por quê
a) Correto. Não é preciso reecoar o que não está mudando.
b) Era o que material antigo afirmava, e está errado: campos omitidos mantêm o valor original.
c) Não existe esse campo — a preservação dos campos omitidos é o comportamento padrão.
4.Uma consulta legítima a um log de produção retorna centenas de nomes de usuário reais, que entrariam no contexto da sessão. O que resolve isso automaticamente? ---
  • a)Um PreToolUse com updatedInput, limpando a consulta antes de rodar

    Limpa o que entra na ferramenta, mas o problema aqui está no que sai dela — a consulta em si era legítima.

  • b)Um PreToolUse com permissionDecision: "deny", barrando consultas a log de produção

    Impediria trabalho legítimo de Basis; a triagem de log é justamente uma das tarefas da faixa (aula 2.8.B1).

  • c)Um PostToolUse com updatedToolOutput, limpando a saída antes de o Claude vê-la

    Correto. É a redação no caminho de volta — automatiza o que a aula 2.8.B1 pedia manualmente.

Ver resposta e por quê
a) Limpa o que entra na ferramenta, mas o problema aqui está no que sai dela — a consulta em si era legítima.
b) Impediria trabalho legítimo de Basis; a triagem de log é justamente uma das tarefas da faixa (aula 2.8.B1).
c) Correto. É a redação no caminho de volta — automatiza o que a aula 2.8.B1 pedia manualmente.