@luanpdd/kit-mcp 1.29.0 → 1.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +168 -168
- package/gates/agent-no-recursive-dispatch.md +82 -82
- package/kit/COMANDOS.md +138 -138
- package/kit/README.md +76 -76
- package/kit/agents/advisor-researcher.md +106 -106
- package/kit/agents/assumptions-analyzer.md +107 -107
- package/kit/agents/audit-log-implementer.md +313 -313
- package/kit/agents/auditor-consistencia-isolamento.md +413 -413
- package/kit/agents/b2b-saas-architect.md +156 -156
- package/kit/agents/cascading-failures-auditor.md +298 -298
- package/kit/agents/codebase-mapper.md +768 -768
- package/kit/agents/crm-pipeline-implementer.md +256 -256
- package/kit/agents/debugger.md +813 -813
- package/kit/agents/detector-tenant-quente.md +337 -337
- package/kit/agents/evolution-go-integrator.md +200 -200
- package/kit/agents/example-reviewer.md +21 -21
- package/kit/agents/executor.md +564 -564
- package/kit/agents/integration-checker.md +200 -200
- package/kit/agents/invite-flow-implementer.md +189 -189
- package/kit/agents/legacy-characterizer.md +368 -368
- package/kit/agents/lgpd-compliance-auditor.md +295 -295
- package/kit/agents/multi-tenant-isolation-auditor.md +253 -253
- package/kit/agents/multi-tenant-rls-writer.md +340 -340
- package/kit/agents/nyquist-auditor.md +178 -178
- package/kit/agents/observability-coverage-auditor.md +315 -315
- package/kit/agents/org-onboarding-implementer.md +223 -223
- package/kit/agents/payload-capture-instrumenter.md +273 -273
- package/kit/agents/phase-researcher.md +696 -696
- package/kit/agents/plan-checker.md +272 -272
- package/kit/agents/planner.md +922 -922
- package/kit/agents/project-researcher.md +652 -652
- package/kit/agents/refactor-safety-auditor.md +404 -404
- package/kit/agents/research-synthesizer.md +245 -245
- package/kit/agents/roadmapper.md +677 -677
- package/kit/agents/seam-finder.md +359 -359
- package/kit/agents/shotgun-surgery-detector.md +349 -349
- package/kit/agents/supabase-branching-architect.md +562 -562
- package/kit/agents/supabase-cicd-pipeline-implementer.md +777 -777
- package/kit/agents/supabase-column-privileges-writer.md +399 -399
- package/kit/agents/supabase-edge-fn-tester.md +287 -0
- package/kit/agents/supabase-edge-fn-writer.md +239 -210
- package/kit/agents/supabase-migration-writer.md +385 -385
- package/kit/agents/supabase-rbac-implementer.md +392 -392
- package/kit/agents/supabase-realtime-implementer.md +363 -267
- package/kit/agents/supabase-rls-hardener.md +521 -521
- package/kit/agents/supabase-rls-writer.md +323 -323
- package/kit/agents/supabase-roles-implementer.md +355 -355
- package/kit/agents/super-admin-implementer.md +281 -281
- package/kit/agents/ui-auditor.md +437 -437
- package/kit/agents/ui-checker.md +302 -302
- package/kit/agents/ui-researcher.md +355 -355
- package/kit/agents/user-profiler.md +175 -175
- package/kit/agents/validador-evolucao-schema.md +335 -335
- package/kit/agents/verifier.md +728 -728
- package/kit/commands/adicionar-backlog.md +75 -75
- package/kit/commands/adicionar-fase.md +42 -42
- package/kit/commands/adicionar-tarefa.md +45 -45
- package/kit/commands/adicionar-testes.md +41 -41
- package/kit/commands/ajuda.md +21 -21
- package/kit/commands/atualizar.md +37 -37
- package/kit/commands/auditar-cascading.md +111 -111
- package/kit/commands/auditar-marco.md +179 -179
- package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
- package/kit/commands/auditar-refactor.md +219 -219
- package/kit/commands/auditar-release.md +109 -109
- package/kit/commands/auditar-uat.md +23 -23
- package/kit/commands/autonomo.md +40 -40
- package/kit/commands/branch-pr.md +24 -24
- package/kit/commands/burn-rate-status.md +408 -408
- package/kit/commands/capturar-payloads.md +193 -193
- package/kit/commands/caracterizar.md +212 -212
- package/kit/commands/concluir-marco.md +247 -247
- package/kit/commands/configuracoes.md +36 -36
- package/kit/commands/dados-distribuidos.md +188 -188
- package/kit/commands/definir-perfil.md +10 -10
- package/kit/commands/depurar.md +190 -190
- package/kit/commands/detectar-duplicacao.md +197 -197
- package/kit/commands/discutir-fase.md +131 -131
- package/kit/commands/encontrar-seams.md +136 -136
- package/kit/commands/entrar-discord.md +17 -17
- package/kit/commands/estatisticas.md +18 -18
- package/kit/commands/example-greeting.md +33 -33
- package/kit/commands/executar-fase.md +58 -58
- package/kit/commands/expresso.md +56 -56
- package/kit/commands/fase-ui.md +34 -34
- package/kit/commands/fazer.md +57 -57
- package/kit/commands/fio.md +125 -125
- package/kit/commands/fluxos-trabalho.md +64 -64
- package/kit/commands/forense.md +176 -176
- package/kit/commands/gerenciador.md +38 -38
- package/kit/commands/inserir-fase.md +31 -31
- package/kit/commands/legacy.md +263 -263
- package/kit/commands/limpeza.md +17 -17
- package/kit/commands/listar-hipoteses-fase.md +45 -45
- package/kit/commands/listar-workspaces.md +18 -18
- package/kit/commands/load-shedding.md +117 -117
- package/kit/commands/mapear-codebase.md +70 -70
- package/kit/commands/multi-tenant.md +163 -163
- package/kit/commands/nota.md +33 -33
- package/kit/commands/novo-marco.md +43 -43
- package/kit/commands/novo-projeto.md +41 -41
- package/kit/commands/novo-workspace.md +43 -43
- package/kit/commands/pausar-trabalho.md +37 -37
- package/kit/commands/perfil-usuario.md +45 -45
- package/kit/commands/pesquisar-fase.md +195 -195
- package/kit/commands/planejar-fase.md +67 -67
- package/kit/commands/planejar-lacunas.md +33 -33
- package/kit/commands/plantar-ideia.md +25 -25
- package/kit/commands/progresso.md +24 -24
- package/kit/commands/proximo.md +30 -30
- package/kit/commands/publicar.md +490 -490
- package/kit/commands/rapido.md +35 -35
- package/kit/commands/reaplicar-patches.md +124 -124
- package/kit/commands/refactor-seguro.md +321 -321
- package/kit/commands/relatorio-sessao.md +19 -19
- package/kit/commands/remover-fase.md +31 -31
- package/kit/commands/remover-workspace.md +26 -26
- package/kit/commands/resumo-marco.md +50 -50
- package/kit/commands/retomar-trabalho.md +40 -40
- package/kit/commands/revisar-backlog.md +60 -60
- package/kit/commands/revisar-ui.md +32 -32
- package/kit/commands/revisar.md +37 -37
- package/kit/commands/saude.md +21 -21
- package/kit/commands/setup-notion.md +93 -93
- package/kit/commands/storytelling.md +179 -179
- package/kit/commands/supabase.md +30 -7
- package/kit/commands/sync-main.md +68 -68
- package/kit/commands/validar-fase.md +35 -35
- package/kit/commands/verificar-tarefas.md +44 -44
- package/kit/commands/verificar-trabalho.md +64 -64
- package/kit/file-manifest.json +15 -8
- package/kit/framework/bin/lib/commands.cjs +959 -959
- package/kit/framework/bin/lib/config.cjs +442 -442
- package/kit/framework/bin/lib/core.cjs +1230 -1230
- package/kit/framework/bin/lib/frontmatter.cjs +336 -336
- package/kit/framework/bin/lib/init.cjs +1442 -1442
- package/kit/framework/bin/lib/milestone.cjs +252 -252
- package/kit/framework/bin/lib/model-profiles.cjs +68 -68
- package/kit/framework/bin/lib/phase.cjs +888 -888
- package/kit/framework/bin/lib/profile-output.cjs +952 -952
- package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
- package/kit/framework/bin/lib/roadmap.cjs +329 -329
- package/kit/framework/bin/lib/security.cjs +382 -382
- package/kit/framework/bin/lib/state.cjs +1031 -1031
- package/kit/framework/bin/lib/template.cjs +222 -222
- package/kit/framework/bin/lib/uat.cjs +282 -282
- package/kit/framework/bin/lib/verify.cjs +888 -888
- package/kit/framework/bin/lib/workstream.cjs +491 -491
- package/kit/framework/bin/tools.cjs +918 -918
- package/kit/framework/commands/workstreams.md +63 -63
- package/kit/framework/references/checkpoints.md +778 -778
- package/kit/framework/references/continuation-format.md +249 -249
- package/kit/framework/references/decimal-phase-calculation.md +64 -64
- package/kit/framework/references/git-integration.md +295 -295
- package/kit/framework/references/git-planning-commit.md +38 -38
- package/kit/framework/references/model-profile-resolution.md +36 -36
- package/kit/framework/references/model-profiles.md +139 -139
- package/kit/framework/references/phase-argument-parsing.md +61 -61
- package/kit/framework/references/planning-config.md +202 -202
- package/kit/framework/references/questioning.md +162 -162
- package/kit/framework/references/tdd.md +263 -263
- package/kit/framework/references/ui-brand.md +160 -160
- package/kit/framework/references/user-profiling.md +657 -657
- package/kit/framework/references/verification-patterns.md +612 -612
- package/kit/framework/references/workstream-flag.md +58 -58
- package/kit/framework/templates/DEBUG.md +164 -164
- package/kit/framework/templates/UAT.md +265 -265
- package/kit/framework/templates/UI-SPEC.md +100 -100
- package/kit/framework/templates/VALIDATION.md +76 -76
- package/kit/framework/templates/claude-md.md +122 -122
- package/kit/framework/templates/codebase/architecture.md +185 -185
- package/kit/framework/templates/codebase/concerns.md +205 -205
- package/kit/framework/templates/codebase/conventions.md +204 -204
- package/kit/framework/templates/codebase/integrations.md +192 -192
- package/kit/framework/templates/codebase/stack.md +158 -158
- package/kit/framework/templates/codebase/structure.md +199 -199
- package/kit/framework/templates/codebase/testing.md +301 -301
- package/kit/framework/templates/config.json +44 -44
- package/kit/framework/templates/context.md +352 -352
- package/kit/framework/templates/continue-here.md +78 -78
- package/kit/framework/templates/copilot-instructions.md +7 -7
- package/kit/framework/templates/debug-subagent-prompt.md +91 -91
- package/kit/framework/templates/dev-preferences.md +20 -20
- package/kit/framework/templates/discovery.md +146 -146
- package/kit/framework/templates/discussion-log.md +63 -63
- package/kit/framework/templates/milestone-archive.md +123 -123
- package/kit/framework/templates/milestone.md +115 -115
- package/kit/framework/templates/phase-prompt.md +610 -610
- package/kit/framework/templates/planner-subagent-prompt.md +117 -117
- package/kit/framework/templates/project.md +186 -186
- package/kit/framework/templates/requirements.md +231 -231
- package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
- package/kit/framework/templates/research-project/FEATURES.md +147 -147
- package/kit/framework/templates/research-project/PITFALLS.md +200 -200
- package/kit/framework/templates/research-project/STACK.md +120 -120
- package/kit/framework/templates/research-project/SUMMARY.md +170 -170
- package/kit/framework/templates/research.md +419 -419
- package/kit/framework/templates/retrospective.md +54 -54
- package/kit/framework/templates/roadmap.md +202 -202
- package/kit/framework/templates/state.md +176 -176
- package/kit/framework/templates/summary-complex.md +59 -59
- package/kit/framework/templates/summary-minimal.md +41 -41
- package/kit/framework/templates/summary-standard.md +48 -48
- package/kit/framework/templates/summary.md +209 -209
- package/kit/framework/templates/user-profile.md +146 -146
- package/kit/framework/templates/user-setup.md +256 -256
- package/kit/framework/templates/verification-report.md +258 -258
- package/kit/framework/workflows/add-phase.md +112 -112
- package/kit/framework/workflows/add-tests.md +351 -351
- package/kit/framework/workflows/add-todo.md +158 -158
- package/kit/framework/workflows/audit-milestone.md +340 -340
- package/kit/framework/workflows/audit-uat.md +109 -109
- package/kit/framework/workflows/autonomous.md +891 -891
- package/kit/framework/workflows/check-todos.md +177 -177
- package/kit/framework/workflows/cleanup.md +152 -152
- package/kit/framework/workflows/complete-milestone.md +696 -696
- package/kit/framework/workflows/diagnose-issues.md +231 -231
- package/kit/framework/workflows/discovery-phase.md +289 -289
- package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
- package/kit/framework/workflows/discuss-phase.md +784 -784
- package/kit/framework/workflows/do.md +104 -104
- package/kit/framework/workflows/execute-phase.md +838 -838
- package/kit/framework/workflows/execute-plan.md +510 -510
- package/kit/framework/workflows/fast.md +102 -102
- package/kit/framework/workflows/forensics.md +265 -265
- package/kit/framework/workflows/health.md +181 -181
- package/kit/framework/workflows/help.md +619 -619
- package/kit/framework/workflows/insert-phase.md +130 -130
- package/kit/framework/workflows/list-phase-assumptions.md +178 -178
- package/kit/framework/workflows/list-workspaces.md +56 -56
- package/kit/framework/workflows/manager.md +362 -362
- package/kit/framework/workflows/map-codebase.md +377 -377
- package/kit/framework/workflows/milestone-summary.md +223 -223
- package/kit/framework/workflows/new-milestone.md +486 -486
- package/kit/framework/workflows/new-project.md +1159 -1159
- package/kit/framework/workflows/new-workspace.md +237 -237
- package/kit/framework/workflows/next.md +97 -97
- package/kit/framework/workflows/node-repair.md +92 -92
- package/kit/framework/workflows/note.md +156 -156
- package/kit/framework/workflows/pause-work.md +176 -176
- package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
- package/kit/framework/workflows/plan-phase.md +765 -765
- package/kit/framework/workflows/plant-seed.md +169 -169
- package/kit/framework/workflows/pr-branch.md +129 -129
- package/kit/framework/workflows/profile-user.md +450 -450
- package/kit/framework/workflows/progress.md +507 -507
- package/kit/framework/workflows/quick.md +757 -757
- package/kit/framework/workflows/remove-phase.md +155 -155
- package/kit/framework/workflows/remove-workspace.md +90 -90
- package/kit/framework/workflows/research-phase.md +82 -82
- package/kit/framework/workflows/resume-project.md +326 -326
- package/kit/framework/workflows/review.md +228 -228
- package/kit/framework/workflows/session-report.md +146 -146
- package/kit/framework/workflows/settings.md +283 -283
- package/kit/framework/workflows/ship.md +228 -228
- package/kit/framework/workflows/stats.md +60 -60
- package/kit/framework/workflows/transition.md +671 -671
- package/kit/framework/workflows/ui-phase.md +302 -302
- package/kit/framework/workflows/ui-review.md +165 -165
- package/kit/framework/workflows/update.md +323 -323
- package/kit/framework/workflows/validate-phase.md +174 -174
- package/kit/framework/workflows/verify-phase.md +252 -252
- package/kit/framework/workflows/verify-work.md +637 -637
- package/kit/hooks/check-update.js +118 -118
- package/kit/hooks/context-monitor.js +163 -163
- package/kit/hooks/kit-attribution-reminder.cjs +98 -0
- package/kit/hooks/prompt-guard.js +103 -103
- package/kit/hooks/statusline.js +125 -125
- package/kit/hooks/workflow-guard.js +101 -101
- package/kit/settings.json +45 -45
- package/kit/skills/_shared-supabase/glossary.md +17 -0
- package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
- package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
- package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
- package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
- package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
- package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
- package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
- package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
- package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
- package/kit/skills/example-skill/SKILL.md +42 -42
- package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
- package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
- package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
- package/kit/skills/legacy-extract-class/SKILL.md +203 -203
- package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
- package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
- package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
- package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
- package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
- package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
- package/kit/skills/member-invite-flow/SKILL.md +305 -305
- package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
- package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
- package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
- package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
- package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
- package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
- package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
- package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
- package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
- package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
- package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
- package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
- package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
- package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
- package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
- package/kit/skills/supabase-edge-functions/SKILL.md +229 -141
- package/kit/skills/supabase-edge-functions-auth/SKILL.md +309 -0
- package/kit/skills/supabase-edge-functions-limits/SKILL.md +302 -0
- package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +279 -0
- package/kit/skills/supabase-edge-functions-testing/SKILL.md +277 -0
- package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +357 -0
- package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
- package/kit/skills/supabase-migrations/SKILL.md +297 -297
- package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
- package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
- package/kit/skills/supabase-realtime/SKILL.md +460 -236
- package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
- package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
- package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
- package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
- package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
- package/package.json +1 -1
- package/src/core/kit.js +216 -216
- package/src/core/reflect.js +247 -247
- package/src/core/reverse-sync.js +372 -372
- package/src/core/sync.js +418 -418
- package/src/core/watch.js +121 -121
- package/src/mcp-server/index.js +715 -693
|
@@ -1,330 +1,330 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: legacy-characterization-tests
|
|
3
|
-
description: Use ao refatorar código legado SEM testes prévios — characterization tests (cap 13 Feathers) capturam comportamento atual como golden snapshot, viram oracle imutável durante o refactor.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Legacy — Characterization Tests
|
|
7
|
-
|
|
8
|
-
## Quando usar
|
|
9
|
-
|
|
10
|
-
LLM carrega esta skill quando o user vai modificar código sem suite de testes adequada e o objetivo é refactor (não bug fix). Trigger phrases:
|
|
11
|
-
|
|
12
|
-
- "refatorar [arquivo grande]", "extract method de", "quebrar essa classe"
|
|
13
|
-
- "esse arquivo não tem testes", "como começo testando isso?"
|
|
14
|
-
- "preservar comportamento", "snapshot test", "golden master"
|
|
15
|
-
- "cap 13 Feathers", "characterization test"
|
|
16
|
-
- "código legado", "legacy code", "edit and pray"
|
|
17
|
-
- arquivo > 500 linhas que será modificado
|
|
18
|
-
- arquivo com contrato externo (webhook, API, integração) sendo modificado
|
|
19
|
-
|
|
20
|
-
Carrega antes de planejar/executar refactor. **Bloqueia execução** até characterization existir.
|
|
21
|
-
|
|
22
|
-
## Regras absolutas
|
|
23
|
-
|
|
24
|
-
- **Legacy code = código sem testes** (definição Feathers, não emocional). Idade não importa. Estética não importa. **Cobertura comportamental** importa.
|
|
25
|
-
- **Characterize first, refactor second.** Sempre. Sem exceção. Pular esse passo é "edit and pray" — o modo default que o livro existe para combater.
|
|
26
|
-
- **Capture o que o código FAZ, não o que DEVERIA fazer.** Se há bug, o teste preserva o bug. Bug fix é commit separado, depois do refactor, com seu próprio teste.
|
|
27
|
-
- **Mínimo de 5-10 inputs cobrindo grupos de equivalência** — null/vazio, válido típico, válido extremo, inválido recoverable, inválido fatal. Menos que isso = baseline frágil.
|
|
28
|
-
- **Behavioral coverage ≥ 70-80% antes de qualquer extract/move/rename**. Coverage % de linha NÃO É proxy de safety — verifique branches via mutation testing.
|
|
29
|
-
- **Golden master/snapshot é decisão, não copy-paste.** Leia output capturado linha por linha antes de salvar. Bugs conhecidos viram comentários inline (`// BUG #X: deveria Y, é Z`). PII/secrets/UUIDs locais → redact deterministic (hash, mask).
|
|
30
|
-
- **Vermelho em characterization test = regressão até prova ao contrário.** Nunca "atualize o expected" sem investigar e documentar a mudança comportamental no commit.
|
|
31
|
-
- **Bug fix dentro de refactor PR = veto.** Misturar invalida o oracle e torna PR não-revisável. Single-goal editing (cap 22) — uma intenção por commit.
|
|
32
|
-
|
|
33
|
-
## Patterns canônicos
|
|
34
|
-
|
|
35
|
-
### Pattern 1: Workflow de characterization (cap 13)
|
|
36
|
-
|
|
37
|
-
```text
|
|
38
|
-
1. Identificar o método/classe/arquivo a refatorar
|
|
39
|
-
2. Inventariar entradas e saídas:
|
|
40
|
-
- Inputs: parâmetros + globals lidos + I/O (DB read, API call)
|
|
41
|
-
- Outputs: return + parâmetros mutados + I/O (DB write, log, API call)
|
|
42
|
-
3. Para cada grupo de equivalência (5+ inputs):
|
|
43
|
-
a. Construir input ("arrange")
|
|
44
|
-
b. Executar código real ("act") — sem mocks ainda; isole I/O com seam mínimo se necessário
|
|
45
|
-
c. Capturar output completo ("snapshot")
|
|
46
|
-
d. REVISAR output linha por linha — marcar bugs conhecidos como comments
|
|
47
|
-
e. Salvar como `expected.txt` ou `__snapshots__/foo.test.ts.snap`
|
|
48
|
-
4. Escrever teste:
|
|
49
|
-
- Arrange = mesmo input
|
|
50
|
-
- Act = mesmo código
|
|
51
|
-
- Assert = output igual ao salvo (deep equal OR snapshot match)
|
|
52
|
-
5. Rodar suite — TODOS verdes → BASELINE estabelecido
|
|
53
|
-
6. Refactor pode começar
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Pattern 2: Grupos de equivalência canônicos
|
|
57
|
-
|
|
58
|
-
Cobertura mínima — pelo menos 1 caso por grupo:
|
|
59
|
-
|
|
60
|
-
| Grupo | Definição | Exemplo (função `parseOrder(input)`) |
|
|
61
|
-
|---|---|---|
|
|
62
|
-
| **Empty** | Input ausente/zero/vazio | `parseOrder(null)`, `parseOrder({})` |
|
|
63
|
-
| **Typical valid** | Caso comum esperado | `parseOrder({ id: 'O123', items: [...] })` |
|
|
64
|
-
| **Boundary valid** | Limites superiores/inferiores válidos | `parseOrder({ ..., items: [singleItem] })`, `parseOrder({ ..., items: [maxItems_x_50] })` |
|
|
65
|
-
| **Recoverable invalid** | Erro que código trata graceful | `parseOrder({ id: 'O123', items: 'malformed' })` — espera-se exceção tipada |
|
|
66
|
-
| **Fatal invalid** | Erro que código não trata (vai propagar/crashar) | `parseOrder(undefined)` — espera-se NPE/crash |
|
|
67
|
-
| **Side-effect heavy** | Input que dispara muitos side effects (logs, DB writes) | Ordem grande que escreve em audit log + cache + queue |
|
|
68
|
-
| **Edge case histórico** | Cases conhecidos que já causaram bugs (consultar git log/issues) | Input com encoding UTF-16, timestamp negativo |
|
|
69
|
-
|
|
70
|
-
### Pattern 3: Snapshot tooling por linguagem
|
|
71
|
-
|
|
72
|
-
| Linguagem | Framework | Snapshot syntax |
|
|
73
|
-
|---|---|---|
|
|
74
|
-
| **JavaScript/TypeScript** | Jest, Vitest | `expect(output).toMatchSnapshot()` ou `toMatchInlineSnapshot()` |
|
|
75
|
-
| **Python** | pytest + pytest-snapshot OR syrupy | `snapshot.assert_match(output)` ou `assert output == snapshot` |
|
|
76
|
-
| **Java** | JUnit + ApprovalTests | `Approvals.verify(output)` |
|
|
77
|
-
| **Ruby** | RSpec + rspec-snapshot | `expect(output).to match_snapshot('foo_bar')` |
|
|
78
|
-
| **Go** | go-cmp + cupaloy/snaps | `cupaloy.SnapshotT(t, output)` |
|
|
79
|
-
| **C#** | Verify, Snapshooter | `await Verifier.Verify(output)` |
|
|
80
|
-
| **Rust** | insta | `insta::assert_yaml_snapshot!(output)` |
|
|
81
|
-
|
|
82
|
-
**Anti-tooling:** evitar diff visual cru (eyeballed) — snapshot framework gera diff legível e atualiza expected via flag (`--updateSnapshot` no Jest, `--snapshot-update` em pytest). Sem framework, refactor de "atualizar oracle" vira manual e propenso a erro.
|
|
83
|
-
|
|
84
|
-
### Pattern 4: Captura de outputs com side effects
|
|
85
|
-
|
|
86
|
-
Quando código tem side effects (DB writes, HTTP calls, logs), o snapshot deve incluir **todos** os efeitos observáveis, não só return. Estratégia:
|
|
87
|
-
|
|
88
|
-
```ts
|
|
89
|
-
// PT-BR: capturar return + lista canônica de efeitos
|
|
90
|
-
async function characterize_placeOrder() {
|
|
91
|
-
const sideEffects = {
|
|
92
|
-
dbWrites: [] as Array<{ table: string, op: string, row: any }>,
|
|
93
|
-
httpCalls: [] as Array<{ url: string, method: string, body: any }>,
|
|
94
|
-
logs: [] as Array<{ level: string, msg: string, fields: any }>,
|
|
95
|
-
queueMsgs: [] as Array<{ queue: string, payload: any }>,
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Wire fakes que populam sideEffects ao invés de fazer real I/O
|
|
99
|
-
const db = makeFakeDb(sideEffects.dbWrites)
|
|
100
|
-
const http = makeFakeHttp(sideEffects.httpCalls)
|
|
101
|
-
const log = makeFakeLogger(sideEffects.logs)
|
|
102
|
-
const queue = makeFakeQueue(sideEffects.queueMsgs)
|
|
103
|
-
|
|
104
|
-
const input = { customerId: 'C-42', items: [{ sku: 'SKU-1', qty: 2 }] }
|
|
105
|
-
const result = await placeOrder(input, { db, http, log, queue })
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
return: result,
|
|
109
|
-
sideEffects,
|
|
110
|
-
}
|
|
111
|
-
// ↑ ESSE objeto é o que vira snapshot
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Test
|
|
115
|
-
test('placeOrder — typical valid input', async () => {
|
|
116
|
-
const captured = await characterize_placeOrder()
|
|
117
|
-
expect(captured).toMatchSnapshot()
|
|
118
|
-
})
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Snapshot resultante captura return E efeitos, ambos congelados.
|
|
122
|
-
|
|
123
|
-
### Pattern 5: Determinismo — eliminar non-determinism antes de capturar
|
|
124
|
-
|
|
125
|
-
Datas, UUIDs, random, nanos — todos não-determinísticos por default. Capture-os como dependência injetada:
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
// PT-BR: dependências injetadas tornam snapshot reproduzível
|
|
129
|
-
const fakeClock = () => new Date('2024-01-15T10:00:00Z') // congelado
|
|
130
|
-
const fakeUuid = (() => { let n = 0; return () => `uuid-${++n}` })() // determinístico
|
|
131
|
-
const fakeRandom = (() => { let n = 0; return () => (n++ % 1000) / 1000 })() // ciclico
|
|
132
|
-
|
|
133
|
-
const result = await placeOrder(input, {
|
|
134
|
-
...realDeps,
|
|
135
|
-
clock: fakeClock,
|
|
136
|
-
uuidGen: fakeUuid,
|
|
137
|
-
random: fakeRandom,
|
|
138
|
-
})
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Sem isso, cada run produz snapshot diferente → "flaky tests" → ninguém confia → suite ignorada.
|
|
142
|
-
|
|
143
|
-
### Pattern 6: Sanitização para snapshot
|
|
144
|
-
|
|
145
|
-
Output cru pode incluir dados sensíveis ou voláteis. Sanitize ANTES de salvar:
|
|
146
|
-
|
|
147
|
-
```ts
|
|
148
|
-
function sanitizeForSnapshot(o: any): any {
|
|
149
|
-
return JSON.parse(
|
|
150
|
-
JSON.stringify(o, (key, value) => {
|
|
151
|
-
if (key === 'apiKey' || key === 'password' || key === 'token') return '***REDACTED***'
|
|
152
|
-
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) return '<TIMESTAMP>'
|
|
153
|
-
if (typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}/.test(value)) return '<UUID>'
|
|
154
|
-
return value
|
|
155
|
-
})
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Aplicar **antes** de `expect(...).toMatchSnapshot()`. Documentar quais campos foram sanitized para que reviewer entenda.
|
|
161
|
-
|
|
162
|
-
### Pattern 7: Behavioral coverage check (mutation testing)
|
|
163
|
-
|
|
164
|
-
Coverage de linha NÃO É proxy de safety. Para confirmar que characterization realmente cobre comportamento:
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
# JavaScript/TypeScript
|
|
168
|
-
npx stryker run
|
|
169
|
-
|
|
170
|
-
# Python
|
|
171
|
-
mutmut run
|
|
172
|
-
mutmut results
|
|
173
|
-
|
|
174
|
-
# Java
|
|
175
|
-
mvn pitest:mutationCoverage
|
|
176
|
-
|
|
177
|
-
# Métrica desejada: ≥ 70% de mutants killed
|
|
178
|
-
# Survived mutants = comportamento NÃO observado pelos tests = ponto cego
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Survived mutant tipicamente indica que falta um observation point. Adicione um test que exercita o branch correspondente.
|
|
182
|
-
|
|
183
|
-
### Pattern 8: Effort budget para characterization
|
|
184
|
-
|
|
185
|
-
Dados empíricos baseados em arquivos típicos:
|
|
186
|
-
|
|
187
|
-
| Tamanho do alvo | Inputs a gerar | Esforço típico | Cobertura esperada |
|
|
188
|
-
|---|---|---|---|
|
|
189
|
-
| Método 20-50 linhas, 1-3 branches | 5-7 inputs | 1-2h | 80-90% behavioral |
|
|
190
|
-
| Método 50-150 linhas, 3-7 branches | 8-12 inputs | 3-6h | 70-85% behavioral |
|
|
191
|
-
| Método 150+ linhas (monster) | 15-25 inputs | 1-3 dias | 60-75% behavioral (exigir cap 22 antes) |
|
|
192
|
-
| Classe inteira 300-500 linhas | 20-40 inputs | 2-5 dias | 65-80% behavioral |
|
|
193
|
-
| Arquivo > 500 linhas | proibido refatorar sem split first | depende | exigir extract class antes |
|
|
194
|
-
|
|
195
|
-
**Não negocie cobertura para baixo "para ganhar tempo".** Cobertura insuficiente = false sense of safety, pior que ausência total.
|
|
196
|
-
|
|
197
|
-
## Anti-patterns
|
|
198
|
-
|
|
199
|
-
### ANTI: testar o "comportamento esperado"
|
|
200
|
-
|
|
201
|
-
```text
|
|
202
|
-
ANTI: "Vou escrever um teste do que o método deveria fazer e refatorar
|
|
203
|
-
até passar".
|
|
204
|
-
|
|
205
|
-
PROBLEMA: o método tem bugs. Teste-do-esperado falha imediato porque o
|
|
206
|
-
estado atual É buggy. Você não consegue rodar nem 1 verde.
|
|
207
|
-
Frustrado, "ajusta" expected para o atual — perdeu o ponto
|
|
208
|
-
inteiro do exercício.
|
|
209
|
-
|
|
210
|
-
CERTO: characterize first. Capture o que o código faz HOJE, com bugs.
|
|
211
|
-
Refactor preserva isso. Bug fix vem depois, em commit separado.
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### ANTI: 1 teste cobrindo "happy path"
|
|
215
|
-
|
|
216
|
-
```text
|
|
217
|
-
ANTI: "Adicionei 1 test do caso comum, vai dar".
|
|
218
|
-
|
|
219
|
-
PROBLEMA: branches raras (null, vazio, edge case) são exatamente onde
|
|
220
|
-
regressão se esconde. Refactor "verde" no test de happy path
|
|
221
|
-
mas quebra null handling silencioso → bug em prod no primeiro
|
|
222
|
-
input null real (1% do tráfego, mas existe).
|
|
223
|
-
|
|
224
|
-
CERTO: 5+ inputs cobrindo grupos canônicos de equivalência. 1h a mais
|
|
225
|
-
de teste = N horas a menos de incident.
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### ANTI: snapshot sem revisão
|
|
229
|
-
|
|
230
|
-
```text
|
|
231
|
-
ANTI: rodar code → toMatchSnapshot() → CI verde → commit. "Funcionou".
|
|
232
|
-
|
|
233
|
-
PROBLEMA: snapshot pode incluir bug, PII, secret, UUID local. CI
|
|
234
|
-
"verde" só significa "snapshot está consistente com captura
|
|
235
|
-
anterior" — não que o conteúdo está certo.
|
|
236
|
-
|
|
237
|
-
CERTO: ler snapshot inteiro antes de commit. Marcar bugs com comments,
|
|
238
|
-
redact PII com sanitize fn, verificar que não há secrets. Commit
|
|
239
|
-
de snapshot é decisão de produto, não automation.
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### ANTI: mocks excessivos = teste de mock, não de código
|
|
243
|
-
|
|
244
|
-
```text
|
|
245
|
-
ANTI: tudo mockado — DB, HTTP, log, queue, clock, random. Test passa.
|
|
246
|
-
|
|
247
|
-
PROBLEMA: você testou que o método chama os mocks na ordem certa, não
|
|
248
|
-
que o método produz output correto para entrada real. Refactor
|
|
249
|
-
que muda ORDEM de chamadas (igualmente correto) quebra mock
|
|
250
|
-
assertion mas é regressão zero.
|
|
251
|
-
|
|
252
|
-
CERTO: minimize mocks. Use fakes que coletam side effects observáveis
|
|
253
|
-
(lista, counter), assert sobre o STATE final dos fakes, não
|
|
254
|
-
sobre sequência de invocações. Snapshot do state pós-execução
|
|
255
|
-
é mais resiliente que assertion de invocation order.
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### ANTI: pular characterization "porque o método é simples"
|
|
259
|
-
|
|
260
|
-
```text
|
|
261
|
-
ANTI: "esse método tem 30 linhas, é óbvio o que faz, vou refatorar
|
|
262
|
-
direto".
|
|
263
|
-
|
|
264
|
-
PROBLEMA: 30 linhas têm ~5-10 branches implícitas (early return, &&
|
|
265
|
-
short-circuit, exceções, type coercion). Cada branch é uma
|
|
266
|
-
assumption não-verificada. "Óbvio" é ilusão de quem escreveu
|
|
267
|
-
o código original — você está lendo, é diferente.
|
|
268
|
-
|
|
269
|
-
CERTO: SEMPRE characterize, mesmo métodos curtos. 30 linhas → 5 inputs
|
|
270
|
-
→ 30 minutos. Custo trivial. Benefício: zero "wait, eu não sabia
|
|
271
|
-
que isso retornava undefined em X". Descobre-se durante captura,
|
|
272
|
-
não em prod.
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### ANTI: characterization em fase de bug fix
|
|
276
|
-
|
|
277
|
-
```text
|
|
278
|
-
ANTI: "Estou consertando bug X, vou aproveitar e characterize tudo
|
|
279
|
-
enquanto estou aqui".
|
|
280
|
-
|
|
281
|
-
PROBLEMA: scope creep. PR vira inrevisável (bug fix + 50 testes novos
|
|
282
|
-
+ redesenho mental). Linha entre "preservei comportamento" e
|
|
283
|
-
"modifiquei comportamento" desaparece.
|
|
284
|
-
|
|
285
|
-
CERTO: bug fix é bug fix. Escreva 1 teste do COMPORTAMENTO CORRETO
|
|
286
|
-
(TDD agora, porque você está mudando intenção). Characterize é
|
|
287
|
-
fase prévia ao refactor — separa em PR/sprint próprio.
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
## Verificação
|
|
291
|
-
|
|
292
|
-
Antes de iniciar refactor de código legado:
|
|
293
|
-
|
|
294
|
-
1. **Inventário completo de inputs/outputs** — todos os parâmetros, globals lidos, I/O capturados
|
|
295
|
-
2. **5+ inputs cobrindo grupos de equivalência** — empty, typical, boundary, invalid recoverable, invalid fatal
|
|
296
|
-
3. **Snapshots revisados linha por linha** — bugs marcados, PII/secrets redacted
|
|
297
|
-
4. **Determinismo garantido** — clock/uuid/random injetáveis, fakes substituem em teste
|
|
298
|
-
5. **Side effects capturados** — DB writes, HTTP calls, logs, queue msgs incluídos no snapshot
|
|
299
|
-
6. **Suite verde** — todos characterization tests rodam OK no main branch
|
|
300
|
-
7. **Behavioral coverage medida** — mutation testing rodado, ≥ 70% mutants killed
|
|
301
|
-
8. **Documentação no PR** — link para snapshots, lista de bugs preservados, fonte do oracle
|
|
302
|
-
|
|
303
|
-
## Limiar de "pronto para refactor"
|
|
304
|
-
|
|
305
|
-
```text
|
|
306
|
-
Total inputs cobertos: ≥ 5 (mínimo); 10+ recomendado
|
|
307
|
-
Behavioral coverage (mutation kill): ≥ 70%
|
|
308
|
-
Branches conhecidas testadas: 100% (todas as branches do código que será tocado)
|
|
309
|
-
Side effects capturados: 100% (zero side effect "esquecido")
|
|
310
|
-
Snapshots revisados: 100% (cada arquivo lido por humano)
|
|
311
|
-
Bugs documentados como TODO: lista no PR
|
|
312
|
-
Determinismo: OK em 10 runs consecutivos sem flaky
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
Se algum item < limiar → não inicie refactor. Volte para characterization.
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## Ver também
|
|
320
|
-
|
|
321
|
-
- [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário canônico
|
|
322
|
-
- [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — quando characterization requer quebrar dependência primeiro
|
|
323
|
-
- [`legacy-effect-analysis`](../legacy-effect-analysis/SKILL.md) — quais inputs escolher? effect sketch identifica
|
|
324
|
-
- [`legacy-monster-methods`](../legacy-monster-methods/SKILL.md) — método > 100 linhas? characterization tem trato especial
|
|
325
|
-
- [`legacy-sprout-wrap-techniques`](../legacy-sprout-wrap-techniques/SKILL.md) — alternativa quando characterization é caro demais (sprout side-steps)
|
|
326
|
-
- [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate auto-trigger que bloqueia refactor sem characterization
|
|
327
|
-
- [`event-based-slos`](../event-based-slos/SKILL.md) (v1.9) — refactor pode regredir SLO; characterization protege
|
|
328
|
-
- [`production-readiness-review`](../production-readiness-review/SKILL.md) (v1.10) — PRR Axe 5 (Change Management) verifica characterization antes de aceitar mudança em prod
|
|
329
|
-
|
|
330
|
-
*Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 13: "I Need to Make a Change, But I Don't Know What Tests to Write" + Cap 23: "How Do I Know That I'm Not Breaking Anything?".*
|
|
1
|
+
---
|
|
2
|
+
name: legacy-characterization-tests
|
|
3
|
+
description: Use ao refatorar código legado SEM testes prévios — characterization tests (cap 13 Feathers) capturam comportamento atual como golden snapshot, viram oracle imutável durante o refactor.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Legacy — Characterization Tests
|
|
7
|
+
|
|
8
|
+
## Quando usar
|
|
9
|
+
|
|
10
|
+
LLM carrega esta skill quando o user vai modificar código sem suite de testes adequada e o objetivo é refactor (não bug fix). Trigger phrases:
|
|
11
|
+
|
|
12
|
+
- "refatorar [arquivo grande]", "extract method de", "quebrar essa classe"
|
|
13
|
+
- "esse arquivo não tem testes", "como começo testando isso?"
|
|
14
|
+
- "preservar comportamento", "snapshot test", "golden master"
|
|
15
|
+
- "cap 13 Feathers", "characterization test"
|
|
16
|
+
- "código legado", "legacy code", "edit and pray"
|
|
17
|
+
- arquivo > 500 linhas que será modificado
|
|
18
|
+
- arquivo com contrato externo (webhook, API, integração) sendo modificado
|
|
19
|
+
|
|
20
|
+
Carrega antes de planejar/executar refactor. **Bloqueia execução** até characterization existir.
|
|
21
|
+
|
|
22
|
+
## Regras absolutas
|
|
23
|
+
|
|
24
|
+
- **Legacy code = código sem testes** (definição Feathers, não emocional). Idade não importa. Estética não importa. **Cobertura comportamental** importa.
|
|
25
|
+
- **Characterize first, refactor second.** Sempre. Sem exceção. Pular esse passo é "edit and pray" — o modo default que o livro existe para combater.
|
|
26
|
+
- **Capture o que o código FAZ, não o que DEVERIA fazer.** Se há bug, o teste preserva o bug. Bug fix é commit separado, depois do refactor, com seu próprio teste.
|
|
27
|
+
- **Mínimo de 5-10 inputs cobrindo grupos de equivalência** — null/vazio, válido típico, válido extremo, inválido recoverable, inválido fatal. Menos que isso = baseline frágil.
|
|
28
|
+
- **Behavioral coverage ≥ 70-80% antes de qualquer extract/move/rename**. Coverage % de linha NÃO É proxy de safety — verifique branches via mutation testing.
|
|
29
|
+
- **Golden master/snapshot é decisão, não copy-paste.** Leia output capturado linha por linha antes de salvar. Bugs conhecidos viram comentários inline (`// BUG #X: deveria Y, é Z`). PII/secrets/UUIDs locais → redact deterministic (hash, mask).
|
|
30
|
+
- **Vermelho em characterization test = regressão até prova ao contrário.** Nunca "atualize o expected" sem investigar e documentar a mudança comportamental no commit.
|
|
31
|
+
- **Bug fix dentro de refactor PR = veto.** Misturar invalida o oracle e torna PR não-revisável. Single-goal editing (cap 22) — uma intenção por commit.
|
|
32
|
+
|
|
33
|
+
## Patterns canônicos
|
|
34
|
+
|
|
35
|
+
### Pattern 1: Workflow de characterization (cap 13)
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
1. Identificar o método/classe/arquivo a refatorar
|
|
39
|
+
2. Inventariar entradas e saídas:
|
|
40
|
+
- Inputs: parâmetros + globals lidos + I/O (DB read, API call)
|
|
41
|
+
- Outputs: return + parâmetros mutados + I/O (DB write, log, API call)
|
|
42
|
+
3. Para cada grupo de equivalência (5+ inputs):
|
|
43
|
+
a. Construir input ("arrange")
|
|
44
|
+
b. Executar código real ("act") — sem mocks ainda; isole I/O com seam mínimo se necessário
|
|
45
|
+
c. Capturar output completo ("snapshot")
|
|
46
|
+
d. REVISAR output linha por linha — marcar bugs conhecidos como comments
|
|
47
|
+
e. Salvar como `expected.txt` ou `__snapshots__/foo.test.ts.snap`
|
|
48
|
+
4. Escrever teste:
|
|
49
|
+
- Arrange = mesmo input
|
|
50
|
+
- Act = mesmo código
|
|
51
|
+
- Assert = output igual ao salvo (deep equal OR snapshot match)
|
|
52
|
+
5. Rodar suite — TODOS verdes → BASELINE estabelecido
|
|
53
|
+
6. Refactor pode começar
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Pattern 2: Grupos de equivalência canônicos
|
|
57
|
+
|
|
58
|
+
Cobertura mínima — pelo menos 1 caso por grupo:
|
|
59
|
+
|
|
60
|
+
| Grupo | Definição | Exemplo (função `parseOrder(input)`) |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| **Empty** | Input ausente/zero/vazio | `parseOrder(null)`, `parseOrder({})` |
|
|
63
|
+
| **Typical valid** | Caso comum esperado | `parseOrder({ id: 'O123', items: [...] })` |
|
|
64
|
+
| **Boundary valid** | Limites superiores/inferiores válidos | `parseOrder({ ..., items: [singleItem] })`, `parseOrder({ ..., items: [maxItems_x_50] })` |
|
|
65
|
+
| **Recoverable invalid** | Erro que código trata graceful | `parseOrder({ id: 'O123', items: 'malformed' })` — espera-se exceção tipada |
|
|
66
|
+
| **Fatal invalid** | Erro que código não trata (vai propagar/crashar) | `parseOrder(undefined)` — espera-se NPE/crash |
|
|
67
|
+
| **Side-effect heavy** | Input que dispara muitos side effects (logs, DB writes) | Ordem grande que escreve em audit log + cache + queue |
|
|
68
|
+
| **Edge case histórico** | Cases conhecidos que já causaram bugs (consultar git log/issues) | Input com encoding UTF-16, timestamp negativo |
|
|
69
|
+
|
|
70
|
+
### Pattern 3: Snapshot tooling por linguagem
|
|
71
|
+
|
|
72
|
+
| Linguagem | Framework | Snapshot syntax |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| **JavaScript/TypeScript** | Jest, Vitest | `expect(output).toMatchSnapshot()` ou `toMatchInlineSnapshot()` |
|
|
75
|
+
| **Python** | pytest + pytest-snapshot OR syrupy | `snapshot.assert_match(output)` ou `assert output == snapshot` |
|
|
76
|
+
| **Java** | JUnit + ApprovalTests | `Approvals.verify(output)` |
|
|
77
|
+
| **Ruby** | RSpec + rspec-snapshot | `expect(output).to match_snapshot('foo_bar')` |
|
|
78
|
+
| **Go** | go-cmp + cupaloy/snaps | `cupaloy.SnapshotT(t, output)` |
|
|
79
|
+
| **C#** | Verify, Snapshooter | `await Verifier.Verify(output)` |
|
|
80
|
+
| **Rust** | insta | `insta::assert_yaml_snapshot!(output)` |
|
|
81
|
+
|
|
82
|
+
**Anti-tooling:** evitar diff visual cru (eyeballed) — snapshot framework gera diff legível e atualiza expected via flag (`--updateSnapshot` no Jest, `--snapshot-update` em pytest). Sem framework, refactor de "atualizar oracle" vira manual e propenso a erro.
|
|
83
|
+
|
|
84
|
+
### Pattern 4: Captura de outputs com side effects
|
|
85
|
+
|
|
86
|
+
Quando código tem side effects (DB writes, HTTP calls, logs), o snapshot deve incluir **todos** os efeitos observáveis, não só return. Estratégia:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// PT-BR: capturar return + lista canônica de efeitos
|
|
90
|
+
async function characterize_placeOrder() {
|
|
91
|
+
const sideEffects = {
|
|
92
|
+
dbWrites: [] as Array<{ table: string, op: string, row: any }>,
|
|
93
|
+
httpCalls: [] as Array<{ url: string, method: string, body: any }>,
|
|
94
|
+
logs: [] as Array<{ level: string, msg: string, fields: any }>,
|
|
95
|
+
queueMsgs: [] as Array<{ queue: string, payload: any }>,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Wire fakes que populam sideEffects ao invés de fazer real I/O
|
|
99
|
+
const db = makeFakeDb(sideEffects.dbWrites)
|
|
100
|
+
const http = makeFakeHttp(sideEffects.httpCalls)
|
|
101
|
+
const log = makeFakeLogger(sideEffects.logs)
|
|
102
|
+
const queue = makeFakeQueue(sideEffects.queueMsgs)
|
|
103
|
+
|
|
104
|
+
const input = { customerId: 'C-42', items: [{ sku: 'SKU-1', qty: 2 }] }
|
|
105
|
+
const result = await placeOrder(input, { db, http, log, queue })
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
return: result,
|
|
109
|
+
sideEffects,
|
|
110
|
+
}
|
|
111
|
+
// ↑ ESSE objeto é o que vira snapshot
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Test
|
|
115
|
+
test('placeOrder — typical valid input', async () => {
|
|
116
|
+
const captured = await characterize_placeOrder()
|
|
117
|
+
expect(captured).toMatchSnapshot()
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Snapshot resultante captura return E efeitos, ambos congelados.
|
|
122
|
+
|
|
123
|
+
### Pattern 5: Determinismo — eliminar non-determinism antes de capturar
|
|
124
|
+
|
|
125
|
+
Datas, UUIDs, random, nanos — todos não-determinísticos por default. Capture-os como dependência injetada:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// PT-BR: dependências injetadas tornam snapshot reproduzível
|
|
129
|
+
const fakeClock = () => new Date('2024-01-15T10:00:00Z') // congelado
|
|
130
|
+
const fakeUuid = (() => { let n = 0; return () => `uuid-${++n}` })() // determinístico
|
|
131
|
+
const fakeRandom = (() => { let n = 0; return () => (n++ % 1000) / 1000 })() // ciclico
|
|
132
|
+
|
|
133
|
+
const result = await placeOrder(input, {
|
|
134
|
+
...realDeps,
|
|
135
|
+
clock: fakeClock,
|
|
136
|
+
uuidGen: fakeUuid,
|
|
137
|
+
random: fakeRandom,
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Sem isso, cada run produz snapshot diferente → "flaky tests" → ninguém confia → suite ignorada.
|
|
142
|
+
|
|
143
|
+
### Pattern 6: Sanitização para snapshot
|
|
144
|
+
|
|
145
|
+
Output cru pode incluir dados sensíveis ou voláteis. Sanitize ANTES de salvar:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
function sanitizeForSnapshot(o: any): any {
|
|
149
|
+
return JSON.parse(
|
|
150
|
+
JSON.stringify(o, (key, value) => {
|
|
151
|
+
if (key === 'apiKey' || key === 'password' || key === 'token') return '***REDACTED***'
|
|
152
|
+
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) return '<TIMESTAMP>'
|
|
153
|
+
if (typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}/.test(value)) return '<UUID>'
|
|
154
|
+
return value
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Aplicar **antes** de `expect(...).toMatchSnapshot()`. Documentar quais campos foram sanitized para que reviewer entenda.
|
|
161
|
+
|
|
162
|
+
### Pattern 7: Behavioral coverage check (mutation testing)
|
|
163
|
+
|
|
164
|
+
Coverage de linha NÃO É proxy de safety. Para confirmar que characterization realmente cobre comportamento:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# JavaScript/TypeScript
|
|
168
|
+
npx stryker run
|
|
169
|
+
|
|
170
|
+
# Python
|
|
171
|
+
mutmut run
|
|
172
|
+
mutmut results
|
|
173
|
+
|
|
174
|
+
# Java
|
|
175
|
+
mvn pitest:mutationCoverage
|
|
176
|
+
|
|
177
|
+
# Métrica desejada: ≥ 70% de mutants killed
|
|
178
|
+
# Survived mutants = comportamento NÃO observado pelos tests = ponto cego
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Survived mutant tipicamente indica que falta um observation point. Adicione um test que exercita o branch correspondente.
|
|
182
|
+
|
|
183
|
+
### Pattern 8: Effort budget para characterization
|
|
184
|
+
|
|
185
|
+
Dados empíricos baseados em arquivos típicos:
|
|
186
|
+
|
|
187
|
+
| Tamanho do alvo | Inputs a gerar | Esforço típico | Cobertura esperada |
|
|
188
|
+
|---|---|---|---|
|
|
189
|
+
| Método 20-50 linhas, 1-3 branches | 5-7 inputs | 1-2h | 80-90% behavioral |
|
|
190
|
+
| Método 50-150 linhas, 3-7 branches | 8-12 inputs | 3-6h | 70-85% behavioral |
|
|
191
|
+
| Método 150+ linhas (monster) | 15-25 inputs | 1-3 dias | 60-75% behavioral (exigir cap 22 antes) |
|
|
192
|
+
| Classe inteira 300-500 linhas | 20-40 inputs | 2-5 dias | 65-80% behavioral |
|
|
193
|
+
| Arquivo > 500 linhas | proibido refatorar sem split first | depende | exigir extract class antes |
|
|
194
|
+
|
|
195
|
+
**Não negocie cobertura para baixo "para ganhar tempo".** Cobertura insuficiente = false sense of safety, pior que ausência total.
|
|
196
|
+
|
|
197
|
+
## Anti-patterns
|
|
198
|
+
|
|
199
|
+
### ANTI: testar o "comportamento esperado"
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
ANTI: "Vou escrever um teste do que o método deveria fazer e refatorar
|
|
203
|
+
até passar".
|
|
204
|
+
|
|
205
|
+
PROBLEMA: o método tem bugs. Teste-do-esperado falha imediato porque o
|
|
206
|
+
estado atual É buggy. Você não consegue rodar nem 1 verde.
|
|
207
|
+
Frustrado, "ajusta" expected para o atual — perdeu o ponto
|
|
208
|
+
inteiro do exercício.
|
|
209
|
+
|
|
210
|
+
CERTO: characterize first. Capture o que o código faz HOJE, com bugs.
|
|
211
|
+
Refactor preserva isso. Bug fix vem depois, em commit separado.
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### ANTI: 1 teste cobrindo "happy path"
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
ANTI: "Adicionei 1 test do caso comum, vai dar".
|
|
218
|
+
|
|
219
|
+
PROBLEMA: branches raras (null, vazio, edge case) são exatamente onde
|
|
220
|
+
regressão se esconde. Refactor "verde" no test de happy path
|
|
221
|
+
mas quebra null handling silencioso → bug em prod no primeiro
|
|
222
|
+
input null real (1% do tráfego, mas existe).
|
|
223
|
+
|
|
224
|
+
CERTO: 5+ inputs cobrindo grupos canônicos de equivalência. 1h a mais
|
|
225
|
+
de teste = N horas a menos de incident.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### ANTI: snapshot sem revisão
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
ANTI: rodar code → toMatchSnapshot() → CI verde → commit. "Funcionou".
|
|
232
|
+
|
|
233
|
+
PROBLEMA: snapshot pode incluir bug, PII, secret, UUID local. CI
|
|
234
|
+
"verde" só significa "snapshot está consistente com captura
|
|
235
|
+
anterior" — não que o conteúdo está certo.
|
|
236
|
+
|
|
237
|
+
CERTO: ler snapshot inteiro antes de commit. Marcar bugs com comments,
|
|
238
|
+
redact PII com sanitize fn, verificar que não há secrets. Commit
|
|
239
|
+
de snapshot é decisão de produto, não automation.
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### ANTI: mocks excessivos = teste de mock, não de código
|
|
243
|
+
|
|
244
|
+
```text
|
|
245
|
+
ANTI: tudo mockado — DB, HTTP, log, queue, clock, random. Test passa.
|
|
246
|
+
|
|
247
|
+
PROBLEMA: você testou que o método chama os mocks na ordem certa, não
|
|
248
|
+
que o método produz output correto para entrada real. Refactor
|
|
249
|
+
que muda ORDEM de chamadas (igualmente correto) quebra mock
|
|
250
|
+
assertion mas é regressão zero.
|
|
251
|
+
|
|
252
|
+
CERTO: minimize mocks. Use fakes que coletam side effects observáveis
|
|
253
|
+
(lista, counter), assert sobre o STATE final dos fakes, não
|
|
254
|
+
sobre sequência de invocações. Snapshot do state pós-execução
|
|
255
|
+
é mais resiliente que assertion de invocation order.
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### ANTI: pular characterization "porque o método é simples"
|
|
259
|
+
|
|
260
|
+
```text
|
|
261
|
+
ANTI: "esse método tem 30 linhas, é óbvio o que faz, vou refatorar
|
|
262
|
+
direto".
|
|
263
|
+
|
|
264
|
+
PROBLEMA: 30 linhas têm ~5-10 branches implícitas (early return, &&
|
|
265
|
+
short-circuit, exceções, type coercion). Cada branch é uma
|
|
266
|
+
assumption não-verificada. "Óbvio" é ilusão de quem escreveu
|
|
267
|
+
o código original — você está lendo, é diferente.
|
|
268
|
+
|
|
269
|
+
CERTO: SEMPRE characterize, mesmo métodos curtos. 30 linhas → 5 inputs
|
|
270
|
+
→ 30 minutos. Custo trivial. Benefício: zero "wait, eu não sabia
|
|
271
|
+
que isso retornava undefined em X". Descobre-se durante captura,
|
|
272
|
+
não em prod.
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### ANTI: characterization em fase de bug fix
|
|
276
|
+
|
|
277
|
+
```text
|
|
278
|
+
ANTI: "Estou consertando bug X, vou aproveitar e characterize tudo
|
|
279
|
+
enquanto estou aqui".
|
|
280
|
+
|
|
281
|
+
PROBLEMA: scope creep. PR vira inrevisável (bug fix + 50 testes novos
|
|
282
|
+
+ redesenho mental). Linha entre "preservei comportamento" e
|
|
283
|
+
"modifiquei comportamento" desaparece.
|
|
284
|
+
|
|
285
|
+
CERTO: bug fix é bug fix. Escreva 1 teste do COMPORTAMENTO CORRETO
|
|
286
|
+
(TDD agora, porque você está mudando intenção). Characterize é
|
|
287
|
+
fase prévia ao refactor — separa em PR/sprint próprio.
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Verificação
|
|
291
|
+
|
|
292
|
+
Antes de iniciar refactor de código legado:
|
|
293
|
+
|
|
294
|
+
1. **Inventário completo de inputs/outputs** — todos os parâmetros, globals lidos, I/O capturados
|
|
295
|
+
2. **5+ inputs cobrindo grupos de equivalência** — empty, typical, boundary, invalid recoverable, invalid fatal
|
|
296
|
+
3. **Snapshots revisados linha por linha** — bugs marcados, PII/secrets redacted
|
|
297
|
+
4. **Determinismo garantido** — clock/uuid/random injetáveis, fakes substituem em teste
|
|
298
|
+
5. **Side effects capturados** — DB writes, HTTP calls, logs, queue msgs incluídos no snapshot
|
|
299
|
+
6. **Suite verde** — todos characterization tests rodam OK no main branch
|
|
300
|
+
7. **Behavioral coverage medida** — mutation testing rodado, ≥ 70% mutants killed
|
|
301
|
+
8. **Documentação no PR** — link para snapshots, lista de bugs preservados, fonte do oracle
|
|
302
|
+
|
|
303
|
+
## Limiar de "pronto para refactor"
|
|
304
|
+
|
|
305
|
+
```text
|
|
306
|
+
Total inputs cobertos: ≥ 5 (mínimo); 10+ recomendado
|
|
307
|
+
Behavioral coverage (mutation kill): ≥ 70%
|
|
308
|
+
Branches conhecidas testadas: 100% (todas as branches do código que será tocado)
|
|
309
|
+
Side effects capturados: 100% (zero side effect "esquecido")
|
|
310
|
+
Snapshots revisados: 100% (cada arquivo lido por humano)
|
|
311
|
+
Bugs documentados como TODO: lista no PR
|
|
312
|
+
Determinismo: OK em 10 runs consecutivos sem flaky
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Se algum item < limiar → não inicie refactor. Volte para characterization.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Ver também
|
|
320
|
+
|
|
321
|
+
- [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário canônico
|
|
322
|
+
- [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — quando characterization requer quebrar dependência primeiro
|
|
323
|
+
- [`legacy-effect-analysis`](../legacy-effect-analysis/SKILL.md) — quais inputs escolher? effect sketch identifica
|
|
324
|
+
- [`legacy-monster-methods`](../legacy-monster-methods/SKILL.md) — método > 100 linhas? characterization tem trato especial
|
|
325
|
+
- [`legacy-sprout-wrap-techniques`](../legacy-sprout-wrap-techniques/SKILL.md) — alternativa quando characterization é caro demais (sprout side-steps)
|
|
326
|
+
- [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate auto-trigger que bloqueia refactor sem characterization
|
|
327
|
+
- [`event-based-slos`](../event-based-slos/SKILL.md) (v1.9) — refactor pode regredir SLO; characterization protege
|
|
328
|
+
- [`production-readiness-review`](../production-readiness-review/SKILL.md) (v1.10) — PRR Axe 5 (Change Management) verifica characterization antes de aceitar mudança em prod
|
|
329
|
+
|
|
330
|
+
*Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 13: "I Need to Make a Change, But I Don't Know What Tests to Write" + Cap 23: "How Do I Know That I'm Not Breaking Anything?".*
|