| name | bug-diagnostics |
| description | Loop de diagnóstico para bugs difíceis e regressões de performance. Feedback loop → reprodução → hipóteses → instrumentação → fix + teste → cleanup. Use quando o usuário disser "diagnosticar"/"debugar isso", ou relatar algo quebrado/lançando exceção/falhando/lento. |
Bug Diagnostics
Uma disciplina para bugs difíceis. Pule fases apenas quando justificado explicitamente.
Créditos: Baseado em diagnosing-bugs por Matt Pocock. Adaptado para português BR pelo Lemon AI Hub.
Ao explorar o codebase, leia o CONTEXT.md (se existir) para construir um modelo mental claro dos módulos relevantes, e consulte ADRs na área que vai mexer.
Foco
Dois princípios não-negociáveis:
- Feedback loop tight — um sinal de pass/fail que fica vermelho neste bug. Sem loop, debugging é adivinhação.
- Determinismo — mesmo veredito a cada execução. Bugs não-determinísticos devem ter a taxa de reprodução elevada até serem debugáveis.
Fase 1 — Construir o feedback loop
Esta é a habilidade. Todo o resto é mecânico. Se você tem um sinal de pass/fail tight para o bug — um que fica vermelho neste bug — você vai encontrar a causa; bisection, teste de hipóteses e instrumentação apenas o consomem. Se não tem, nenhuma quantidade de olhar código vai salvar.
Invista esforço desproporcional aqui. Seja agressivo. Seja criativo. Recuse-se a desistir.
Maneiras de construir um loop — tente nesta ordem aproximada
- Teste falhando em qualquer seam que alcance o bug — unit, integration, e2e.
- Script curl/HTTP contra um dev server rodando.
- Invocação CLI com um input fixture, fazendo diff do stdout contra um snapshot conhecido-bom.
- Script headless browser (Playwright/Puppeteer) — drive a UI, asserta em DOM/console/network.
- Replay de trace capturado. Salve uma network request/payload/event log real em disco; faça replay pelo code path isolado.
- Harness descartável. Suba um subset mínimo do sistema (um serviço, deps mockadas) que exercita o code path do bug com uma única chamada de função.
- Loop property/fuzz. Se o bug é "output às vezes errado", rode 1000 inputs aleatórios e procure o failure mode.
- Harness de bisection. Se o bug apareceu entre dois estados conhecidos (commit, dataset, versão), automatize "boot no estado X, verifica, repete" para poder fazer
git bisect run.
- Loop diferencial. Rode o mesmo input em versão-antiga vs versão-nova (ou duas configs) e faça diff dos outputs.
- Script bash HITL. Último recurso. Se um humano precisa clicar, orquestre com
scripts/hitl-loop.template.sh para que o loop ainda seja estruturado. Output capturado alimenta de volta para você.
Construa o feedback loop certo, e o bug está 90% resolvido.
Tighten o loop
Trate o loop como um produto. Assim que tiver um loop, tighten:
- Posso deixar mais rápido? (Cache de setup, pular init irrelevante, estreitar escopo do teste.)
- Posso deixar o sinal mais nítido? (Assertar no sintoma específico, não em "não crashou".)
- Posso deixar mais determinístico? (Pinar tempo, seedar RNG, isolar filesystem, congelar network.)
Um loop flaky de 30 segundos é quase tão ruim quanto nenhum loop; um determinístico de 2 segundos é tight — um superpoder de debugging.
Bugs não-determinísticos
O objetivo não é uma repro limpa, mas sim uma taxa de reprodução mais alta. Loope o gatilho 100×, paralelize, adicione stress, estreite janelas de timing, injete sleeps. Um bug 50% flaky é debugável; 1% não é — continue elevando a taxa até ser debugável.
Quando você genuinamente não consegue construir um loop
Pare e diga explicitamente. Liste o que tentou. Peça ao usuário por: (a) acesso a qualquer ambiente que reproduza, (b) um artifact capturado (HAR file, log dump, core dump, screen recording com timestamps), ou (c) permissão para adicionar instrumentação temporária em produção. Não prossiga para hipóteses sem um loop.
Critério de conclusão — um loop tight que fica vermelho
A Fase 1 está completa quando o loop é tight e red-capable: você consegue nomear um comando — um path de script, uma invocação de teste, um curl — que você já executou pelo menos uma vez (cole a invocação e seu output), e que é:
Se se pegar lendo código para construir uma teoria antes deste comando existir, pare — pular direto para hipótese é a falha exata que esta skill previne. Sem comando red-capable, sem Fase 2.
Fase 2 — Reproduzir + minimizar
Rode o loop. Veja ficar vermelho — o bug aparece.
Confirme:
Minimizar
Assim que estiver vermelho, encolha a repro para o menor cenário que ainda fica vermelho. Corte inputs, callers, config, dados e steps um de cada vez, re-rodando o loop após cada corte — mantenha apenas o que é load-bearing para o failure.
Por que importa: uma repro mínima encolhe o espaço de hipóteses na Fase 3 (menos partes móveis para suspeitar) e se torna o teste de regressão limpo na Fase 5.
Completo quando todo elemento restante é load-bearing — remover qualquer um deles faz o loop ficar verde.
Não prossiga até ter reproduzido e minimizado.
Fase 3 — Hipóteses
Gere 3–5 hipóteses ranqueadas antes de testar qualquer uma. Geração de hipótese única ancora na primeira ideia plausível.
Cada hipótese deve ser falsificável: declare a predição que ela faz.
Formato: "Se é a causa, então vai fazer o bug desaparecer / vai piorar."
Se não consegue declarar a predição, a hipótese é um chute — descarte ou afie.
Mostre a lista ranqueada ao usuário antes de testar. Ele costuma ter conhecimento de domínio que re-ranqueia instantaneamente ("acabamos de deployar uma mudança em #3"), ou sabe hipóteses que já descartou. Checkpoint barato, grande economia de tempo. Não bloqueie — prossiga com seu ranking se o usuário estiver AFK.
Fase 4 — Instrumentar
Cada probe deve mapear para uma predição específica da Fase 3. Mude uma variável por vez.
Preferência de ferramenta:
- Inspeção via debugger/REPL se o ambiente suporta. Um breakpoint vale por dez logs.
- Logs direcionados nas fronteiras que distinguem hipóteses.
- Nunca "logar tudo e grep".
Tagueie cada debug log com um prefixo único, ex.: [DEBUG-a4f2]. Cleanup no final vira um único grep. Logs sem tag sobrevivem; logs tagueados morrem.
Branch de performance. Para regressões de performance, logs estão geralmente errados. Em vez disso: estabeleça uma medição baseline (timing harness, performance.now(), profiler, query plan), depois faça bisection. Meça primeiro, conserte depois.
Fase 5 — Fix + teste de regressão
Escreva o teste de regressão antes do fix — mas apenas se existir um seam correto para ele.
Um seam correto é um onde o teste exercita o padrão real do bug como ocorre no call site. Se o único seam disponível é raso demais (teste de caller único quando o bug precisa de múltiplos callers, teste unitário que não consegue replicar a chain que disparou o bug), um teste de regressão ali dá falsa confiança.
Se nenhum seam correto existe, isso em si é o finding. Anote. A arquitetura do codebase está impedindo o bug de ser travado. Flague para a próxima fase.
Se um seam correto existe:
- Transforme a repro minimizada em um teste falhando naquele seam.
- Veja falhar.
- Aplique o fix.
- Veja passar.
- Re-rode o feedback loop da Fase 1 contra o cenário original (não-minimizado).
Fase 6 — Cleanup + post-mortem
Obrigatório antes de declarar concluído:
Então pergunte: o que teria prevenido este bug? Se a resposta envolve mudança arquitetural (sem bom seam de teste, callers emaranhados, acoplamento escondido) encaminhe para a skill de melhoria de arquitetura com os specifics. Faça a recomendação depois do fix estar pronto, não antes — você tem mais informação agora do que quando começou.