/click-path-audit — Auditoria de Fluxo Comportamental
Encontre bugs que a leitura estática de código ignora: efeitos colaterais de interação de estado, condições de corrida entre chamadas sequenciais e manipuladores que se desfazem silenciosamente.
O Problema Que Isso Resolve
A depuração tradicional verifica:
- A função existe? (fiação ausente)
- Ela falha? (erros de execução)
- Ela retorna o tipo certo? (fluxo de dados)
Mas ela NÃO verifica:
- O estado final da IU corresponde ao que o rótulo do botão promete?
- A função B desfaz silenciosamente o que a função A acabou de fazer?
- O estado compartilhado (Zustand/Redux/contexto) tem efeitos colaterais que cancelam a ação pretendida?
Exemplo real: Um botão "Novo Email" chamava setComposeMode(true) e depois selectThread(null). Ambas funcionavam individualmente. Mas selectThread tinha um efeito colateral que resetava composeMode: false. O botão não fazia nada. 54 bugs foram encontrados por depuração sistemática — este foi perdido.
Como Funciona
Para CADA ponto de interação na área alvo:
1. IDENTIFICAR o manipulador (onClick, onSubmit, onChange, etc.)
2. RASTREAR cada chamada de função no manipulador, EM ORDEM
3. Para CADA chamada de função:
a. Qual estado ela LÊ?
b. Qual estado ela ESCREVE?
c. Ela tem EFEITOS COLATERAIS no estado compartilhado?
d. Ela reseta/limpa algum estado como efeito colateral?
4. VERIFICAR: Alguma chamada posterior DESFAZ uma mudança de estado de uma chamada anterior?
5. VERIFICAR: O estado FINAL é o que o usuário espera do rótulo do botão?
6. VERIFICAR: Existem condições de corrida (chamadas assíncronas que resolvem na ordem errada)?
Passos de Execução
Passo 1: Mapear Stores de Estado
Antes de auditar qualquer ponto de interação, construa um mapa de efeitos colaterais de cada ação do store de estado:
Para cada store Zustand / contexto React no escopo:
Para cada ação/setter:
- Quais campos ele define?
- Ele RESETA outros campos como efeito colateral?
- Documentar: nomeDaAção → {define: [...], reseta: [...]}
Esta é a referência crítica. O bug "Novo Email" era invisível sem saber que selectThread reseta composeMode.
Formato de saída:
STORE: emailStore
setComposeMode(bool) → define: {composeMode}
selectThread(thread|null) → define: {selectedThread, selectedThreadId, messages, drafts, selectedDraft, summary} RESETA: {composeMode: false, composeData: null, redraftOpen: false}
setDraftGenerating(bool) → define: {draftGenerating}
...
RESETES PERIGOSOS (ações que limpam estado que não possuem):
selectThread → reseta composeMode (pertence a setComposeMode)
reset → reseta tudo
Passo 2: Auditar Cada Ponto de Interação
Para cada botão/toggle/envio de formulário na área alvo:
PONTO DE INTERAÇÃO: [rótulo do botão] em [Componente:linha]
MANIPULADOR: onClick → {
chamada 1: funçãoA() → define {X: true}
chamada 2: funçãoB() → define {Y: null} RESETA {X: false} ← CONFLITO
}
ESPERADO: Usuário vê [descrição do que o rótulo do botão promete]
REAL: X é false porque funçãoB resetou
VEREDITO: BUG — [descrição]
Verifique cada um desses padrões de bug:
Padrão 1: Desfazer Sequencial
manipulador() {
definirEstado_A(true) // define X = true
definirEstado_B(null) // efeito colateral: reseta X = false
}
// Resultado: X é false. A primeira chamada foi inútil.
Padrão 2: Corrida Assíncrona
manipulador() {
buscarA().then(() => definirEstado({ carregando: false }))
buscarB().then(() => definirEstado({ carregando: true }))
}
// Resultado: estado final de carregamento depende de qual resolve primeiro
Padrão 3: Closure Obsoleto
const [contador, definirContador] = useState(0)
const manipulador = useCallback(() => {
definirContador(contador + 1) // captura contador obsoleto
definirContador(contador + 1) // mesmo contador obsoleto — incrementa 1, não 2
}, [contador])
Padrão 4: Transição de Estado Ausente
// Botão diz "Salvar" mas o manipulador só valida, nunca salva de fato
// Botão diz "Excluir" mas o manipulador define uma flag sem chamar a API
// Botão diz "Enviar" mas o endpoint da API foi removido/quebrado
Padrão 5: Caminho Morto Condicional
manipulador() {
if (algumEstado) { // algumEstado é SEMPRE false neste ponto
fazerAcoesReais() // nunca alcançado
}
}
Padrão 6: Interferência de useEffect
// Botão define estadoX = true
// Um useEffect observa estadoX e o reseta para false
// Usuário não vê nada acontecer
Passo 3: Relatar
Para cada bug encontrado:
CAMINHO-CLIQUE-NNN: [severidade: CRÍTICO/ALTO/MÉDIO/BAIXO]
Ponto de Interação: [rótulo do botão] em [arquivo:linha]
Padrão: [Desfazer Sequencial / Corrida Assíncrona / Closure Obsoleto / Transição Ausente / Caminho Morto / Interferência de useEffect]
Manipulador: [nome da função ou inline]
Rastreamento:
1. [chamada] → define {campo: valor}
2. [chamada] → RESETA {campo: valor} ← CONFLITO
Esperado: [o que o usuário espera]
Real: [o que realmente acontece]
Correção: [correção específica]
Controle de Escopo
Esta auditoria é custosa. Escopo-a adequadamente:
- Auditoria completa do app: Use ao lançar ou após grande refatoração. Lance agentes paralelos por página.
- Auditoria de página única: Use após construir uma nova página ou quando um usuário relata um botão quebrado.
- Auditoria focada em store: Use após modificar um store Zustand — audite todos os consumidores das ações alteradas.
Divisão recomendada de agentes para app completo:
Agente 1: Mapear TODOS os stores de estado (Passo 1) — este é contexto compartilhado para todos os outros agentes
Agente 2: Dashboard (Tarefas, Notas, Diário, Ideias)
Agente 3: Chat (DanteChatColumn, JustChatPage)
Agente 4: Emails (ThreadList, DraftArea, EmailsPage)
Agente 5: Projetos (ProjectsPage, ProjectOverviewTab, NewProjectWizard)
Agente 6: CRM (todas as sub-abas)
Agente 7: Perfil, Configurações, Cofre, Notificações
Agente 8: Suite de Gerenciamento (todas as páginas)
O Agente 1 DEVE ser concluído primeiro. Sua saída é entrada para todos os outros agentes.
Quando Usar
- Após depuração sistemática encontrar "nenhum bug", mas usuários relatarem IU quebrada
- Após modificar qualquer ação de store Zustand (verifique todos os chamadores)
- Após qualquer refatoração que toque estado compartilhado
- Antes do lançamento, em fluxos críticos do usuário
- Quando um botão "não faz nada" — esta é a ferramenta certa para isso
Quando NÃO Usar
- Para bugs de nível de API (formato de resposta incorreto, endpoint ausente) — use depuração sistemática
- Para problemas de estilo/layout — inspeção visual
- Para problemas de desempenho — ferramentas de perfilamento
Integração com Outras Habilidades
- Execute APÓS
/superpowers:depuração-sistemática(que encontra os outros 54 tipos de bug) - Execute ANTES de
/superpowers:verificação-antes-da-conclusão(que verifica se as correções funcionam) - Alimenta
/superpowers:desenvolvimento-orientado-a-testes— cada bug encontrado aqui deve gerar um teste
Exemplo: O Bug Que Inspirou Esta Habilidade
Botão "Novo Email" em ThreadList.tsx:
onClick={() => {
useEmailStore.getState().setComposeMode(true) // ✓ define composeMode = true
useEmailStore.getState().selectThread(null) // ✗ RESETA composeMode = false
}}
Definição do store:
selectThread: (thread) => set({
selectedThread: thread,
selectedThreadId: thread?.id ?? null,
messages: [],
drafts: [],
selectedDraft: null,
summary: null,
composeMode: false, // ← este reset silencioso matou o botão
composeData: null,
redraftOpen: false,
})
A depuração sistemática perdeu porque:
- O botão tem um manipulador onClick (não está morto)
- Ambas as funções existem (nenhuma fiação ausente)
- Nenhuma função falha (nenhum erro de execução)
- Os tipos de dados estão corretos (nenhuma incompatibilidade de tipo)
A auditoria de caminhos de clique pega porque:
- Passo 1 mapeia que
selectThreadresetacomposeMode - Passo 2 rastreia o manipulador: chamada 1 define true, chamada 2 reseta false
- Veridito: Desfazer Sequencial — estado final contradiz intenção do botão