| name | diagnosing-bugs |
| description | Loop de diagnóstico para bugs difíceis e regressões de performance. Use quando o usuário disser "diagnose"/"debug this", ou relatar algo quebrado/lançando/falhando/lento. |
Diagnosing Bugs
Uma disciplina para bugs difíceis. Pule fases só quando explicitamente justificado.
Ao explorar a codebase, leia CONTEXT.md (se existir) para ter um modelo mental claro dos módulos relevantes, e verifique ADRs na área que você está tocando.
Fase 1 — Construa um feedback loop
Esta é a skill. Todo o resto é mecânico. Se você tiver um sinal apertado de pass/fail para o bug — um que fica vermelho neste bug específico — você vai encontrar a causa; bisection, teste de hipóteses e instrumentação só consomem ele. Se não tiver, nenhuma quantidade de olhar código vai salvar você.
Dedique esforço desproporcional aqui. Seja agressivo. Seja criativo. Recuse desistir.
Formas de construir um — tente mais ou menos nesta ordem
- Teste falhando no seam que alcança o bug — unit, integration, e2e.
- Curl / script HTTP contra um dev server rodando.
- Invocação CLI com uma fixture de input, diffando stdout contra um snapshot known-good.
- Script de headless browser (Playwright / Puppeteer) — dirige a UI, afirma sobre DOM/console/network.
- Replay de um trace capturado. Salve uma requisição real de rede / payload / log de evento em disco; replay através do caminho do código em isolamento.
- Harness descartável. Suba um subconjunto mínimo do sistema (um serviço, deps mockadas) que exercita o caminho do código do bug com uma única chamada de função.
- Loop de property / fuzz. Se o bug é "às vezes output errado", rode 1000 inputs aleatórios e procure o modo de falha.
- Harness de bisection. Se o bug apareceu entre dois estados conhecidos (commit, dataset, versão), automatize "boot no estado X, cheque, repita" para poder
git bisect run.
- Loop diferencial. Rode o mesmo input em versão-antiga vs versão-nova (ou duas configs) e diff outputs.
- Script HITL bash. Último recurso. Se um humano precisa clicar, dirija ele com
scripts/hitl-loop.template.sh para o loop ainda ser estruturado. Output capturado volta para você.
Construa o feedback loop certo, e o bug está 90% resolvido.
Aperta o loop
Trate o loop como um produto. Uma vez que você tem um loop, aperte:
- Posso torná-lo mais rápido? (Cache setup, pule init irrelevante, estreite o escopo do teste.)
- Posso tornar o sinal mais nítido? (Afirme no sintoma específico, não "não crashou".)
- Posso torná-lo mais determinístico? (Pin time, seed RNG, isole filesystem, congele network.)
Um loop flaky de 30 segundos é pouco melhor que nenhum; um determinístico de 2 segundos é apertado — um superpoder de debug.
Bugs não-determinísticos
O objetivo não é um repro limpo, mas uma taxa de reprodução mais alta. Rode o trigger 100×, paralelize, adicione stress, estreite janelas de timing, injete sleeps. Um bug de 50% flake é debuggável; 1% não é — continue subindo a taxa até ser debuggável.
Quando você genuinamente não consegue construir um loop
Pare e diga explicitamente. Liste o que tentou. Peça ao usuário: (a) acesso ao ambiente que reproduz, (b) um artefato 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 hipotetizar sem um loop.
Critério de conclusão — um loop apertado que fica vermelho
Fase 1 está pronta quando o loop está apertado e red-capable: você consegue nomear um comando — um caminho de script, uma invocação de teste, um curl — que você já rodou pelo menos uma vez (cole a invocação e seu output), e que é:
Se você se pegar lendo código para construir uma teoria antes deste comando existir, pare — pular direto para hipótese é exatamente a falha que esta skill previne. Sem comando red-capable, sem Fase 2.
Fase 2 — Reproduza + minimize
Rode o loop. Veja ele ficar vermelho — o bug aparece.
Confirme:
Minimize
Uma vez vermelho, encolha o repro para o menor cenário que ainda fica vermelho. Corte inputs, callers, config, dados e passos um de cada vez, re-rodando o loop depois de cada corte — mantenha só o que é load-bearing para a falha.
Por que vale: um repro mínimo encolhe o espaço de hipóteses na Fase 3 (menos peças móveis para suspeitar) e vira o regression test limpo na Fase 5.
Pronto quando todo elemento restante for load-bearing — remover qualquer um faz o loop ficar verde.
Não prossiga até ter reproduzido e minimizado.
Fase 3 — Hipotetize
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 for a causa, então fará o bug desaparecer / vai piorar."
Se você não conseguir declarar a predição, a hipótese é um vibe — descarte ou afine.
Mostre a lista ranqueada ao usuário antes de testar. Eles frequentemente têm conhecimento de domínio que re-ranqueia instantaneamente ("acabamos de deployar uma mudança no #3"), ou sabem hipóteses que já foram descartadas. Checkpoint barato, grande economia de tempo. Não bloqueie por isso — prossiga com seu ranking se o usuário estiver AFK.
Fase 4 — Instrumente
Cada sonda deve mapear para uma predição específica da Fase 3. Mude uma variável por vez.
Preferência de ferramenta:
- Debugger / REPL inspection se o ambiente suportar. Um breakpoint ganha de dez logs.
- Logs direcionados nos boundaries que distinguem hipóteses.
- Nunca "logue tudo e grep".
Tag todo debug log com um prefixo único, ex. [DEBUG-a4f2]. Limpeza no final vira um único grep. Logs sem tag sobrevivem; logs tageados morrem.
Ramo de perf. Para regressões de performance, logs geralmente estão errados. Em vez disso: estabeleça uma medição baseline (harness de timing, performance.now(), profiler, query plan), depois bisect. Meça primeiro, corrija depois.
Fase 5 — Corrija + regression test
Escreva o regression test antes da correção — mas só se houver um seam correto para ele.
Um seam correto é aquele onde o teste exercita o padrão real do bug como ele ocorre no call site. Se o único seam disponível for muito shallow (teste de single-caller quando o bug precisa de múltiplos callers, unit test que não consegue replicar a cadeia que disparou o bug), um regression test ali dá confiança falsa.
Se nenhum seam correto existir, isso em si é o achado. Note. A arquitetura da codebase está impedindo que o bug seja trancado. Flag isso para a próxima fase.
Se um seam correto existir:
- Transforme o repro minimizado em um teste falhando naquele seam.
- Veja falhar.
- Aplique a correção.
- Veja passar.
- Re-execute o loop de feedback da Fase 1 contra o cenário original (não-minimizado).
Fase 6 — Cleanup + post-mortem
Obrigatório antes de declarar done:
Depois pergunte: o que teria prevenido este bug? Se a resposta envolver mudança arquitetural (sem bom seam de teste, callers emaranhados, acoplamento escondido) passe para a skill /improve-codebase-architecture com os detalhes. Faça a recomendação depois que a correção estiver, não antes — você tem mais informação agora do que quando começou.