/click-path-audit — Auditoria de Fluxo Comportamental
Encontre bugs que a leitura estática de código perde: efeitos colaterais de interação de estado, condições de corrida entre chamadas sequenciais e handlers que silenciosamente se desfazem.
O Problema Que Isso Resolve
A depuração tradicional verifica:
- A função existe? (fiação faltante)
- Ela trava? (erros de runtime)
- Ela retorna o tipo certo? (fluxo de dados)
Mas NÃO verifica:
- O estado final da UI corresponde ao que o rótulo do botão promete?
- A função B silenciosamente desfaz o que a função A acabou de fazer?
- O estado compartilhado (Zustand/Redux/context) tem efeitos colaterais que cancelam a ação pretendida?
Exemplo real: Um botão "Novo E-mail" chamava setComposeMode(true) e depois selectThread(null). Ambas funcionavam individualmente. Mas selectThread tinha um efeito colateral redefinindo 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. IDENTIFIQUE o handler (onClick, onSubmit, onChange, etc.)
2. RASTREIE cada chamada de função no handler, 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 redefine/limpa algum estado como efeito colateral?
4. VERIFIQUE: Alguma chamada posterior DESFAZ uma mudança de estado de uma chamada anterior?
5. VERIFIQUE: O estado FINAL é o que o usuário espera do rótulo do botão?
6. VERIFIQUE: Existem condições de corrida (chamadas assíncronas que resolvem na ordem errada)?
Etapas de Execução
Etapa 1: Mapear Stores de Estado
Antes de auditar qualquer ponto de interação, construa um mapa de efeitos colaterais de cada ação da store de estado:
Para cada store Zustand / contexto React no escopo:
Para cada ação/setter:
- Quais campos ela define?
- Ela REDEFINE outros campos como efeito colateral?
- Documente: actionName → {sets: [...], resets: [...]}
Esta é a referência crítica. O bug "Novo E-mail" era invisível sem saber que selectThread redefine composeMode.
Formato de saída:
STORE: emailStore
setComposeMode(bool) → sets: {composeMode}
selectThread(thread|null) → sets: {selectedThread, selectedThreadId, messages, drafts, selectedDraft, summary} REDEFINE: {composeMode: false, composeData: null, redraftOpen: false}
setDraftGenerating(bool) → sets: {draftGenerating}
...
REDEFINIÇÕES PERIGOSAS (ações que limpam estado que não possuem):
selectThread → redefine composeMode (possuído por setComposeMode)
reset → redefine tudo
Etapa 2: Auditar Cada Ponto de Interação
Para cada botão/alternância/envio de formulário na área alvo:
PONTO: [Rótulo do botão] em [Componente:linha]
HANDLER: onClick → {
chamada 1: functionA() → define {X: true}
chamada 2: functionB() → define {Y: null} REDEFINE {X: false} ← CONFLITO
}
ESPERADO: Usuário vê [descrição do que o rótulo do botão promete]
REAL: X é false porque functionB redefiniu
VEREDITO: BUG — [descrição]
Verifique cada um desses padrões de bug:
Padrão 1: Desfazer Sequencial
handler() {
setState_A(true) // define X = true
setState_B(null) // efeito colateral: redefine X = false
}
// Resultado: X é false. Primeira chamada foi inútil.
Padrão 2: Race Assíncrona
handler() {
fetchA().then(() => setState({ loading: false }))
fetchB().then(() => setState({ loading: true }))
}
// Resultado: estado final de loading depende de qual resolve primeiro
Padrão 3: Fechamento Obsoleto
const [count, setCount] = useState(0)
const handler = useCallback(() => {
setCount(count + 1) // captura count obsoleto
setCount(count + 1) // mesmo count obsoleto — incrementa 1, não 2
}, [count])
Padrão 4: Transição de Estado Ausente
// Botão diz "Salvar" mas handler apenas valida, nunca salva de fato
// Botão diz "Excluir" mas handler 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
handler() {
if (someState) { // someState é SEMPRE false neste ponto
doTheActualThing() // nunca alcançado
}
}
Padrão 6: Interferência de useEffect
// Botão define stateX = true
// Um useEffect observa stateX e o redefine para false
// Usuário não vê nada acontecer
Etapa 3: Relatar
Para cada bug encontrado:
CLICK-PATH-NNN: [gravidade: CRÍTICO/ALTO/MÉDIO/BAIXO]
Ponto: [Rótulo do botão] em [arquivo:linha]
Padrão: [Desfazer Sequencial / Race Assíncrona / Fechamento Obsoleto / Transição Ausente / Caminho Morto / Interferência de useEffect]
Handler: [nome da função ou inline]
Rastreio:
1. [chamada] → define {campo: valor}
2. [chamada] → REDEFINE {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 é cara. Escope-a adequadamente:
- Auditoria completa do app: Use no lançamento 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 relatar um botão quebrado.
- Auditoria focada em store: Use após modificar uma store Zustand — audite todos os consumidores das ações alteradas.
Divisão recomendada de agentes para auditoria completa:
Agente 1: Mapear TODAS as stores de estado (Etapa 1) — este é o contexto compartilhado para todos os outros agentes
Agente 2: Dashboard (Tarefas, Notas, Diário, Ideias)
Agente 3: Chat (DanteChatColumn, JustChatPage)
Agente 4: E-mails (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 Gestão (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 UI 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 para isso
Quando NÃO Usar
- Para bugs de nível de API (formato de resposta errado, endpoint faltante) — use systematic-debugging
- Para problemas de estilo/layout — inspeção visual
- Para problemas de desempenho — ferramentas de profiling
Integração com Outras Skills
- Execute APÓS
/superpowers:systematic-debugging(que encontra os outros 54 tipos de bug) - Execute ANTES de
/superpowers:verification-before-completion(que verifica se as correções funcionam) - Alimenta
/superpowers:test-driven-development— todo bug encontrado aqui deve ganhar um teste
Exemplo: O Bug Que Inspirou Esta Skill
ThreadList.tsx Botão "Novo E-mail":
onClick={() => {
useEmailStore.getState().setComposeMode(true) // ✓ define composeMode = true
useEmailStore.getState().selectThread(null) // ✗ REDEFINE composeMode = false
}}
Definição da store:
selectThread: (thread) => set({
selectedThread: thread,
selectedThreadId: thread?.id ?? null,
messages: [],
drafts: [],
selectedDraft: null,
summary: null,
composeMode: false, // ← esta redefinição silenciosa matou o botão
composeData: null,
redraftOpen: false,
})
A depuração sistemática perdeu porque:
- O botão tem um handler onClick (não está morto)
- Ambas as funções existem (sem fiação faltante)
- Nenhuma função trava (sem erro de runtime)
- Os tipos de dados estão corretos (sem incompatibilidade de tipo)
A auditoria de caminho de clique captura porque:
- Etapa 1 mapeia
selectThreadredefinecomposeMode - Etapa 2 rastreia o handler: chamada 1 define true, chamada 2 redefine false
- Veredito: Desfazer Sequencial — estado final contradiz a intenção do botão