@luanpdd/kit-mcp 1.10.0 → 1.11.0

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.
Files changed (63) hide show
  1. package/gates/ai-prompt-stability.md +120 -0
  2. package/gates/legacy-refactor-safety.md +178 -0
  3. package/gates/observability-coverage.md +151 -0
  4. package/gates/release-pipeline-policy.md +132 -0
  5. package/kit/COMANDOS.md +15 -0
  6. package/kit/agents/ai-mutation-tester.md +298 -0
  7. package/kit/agents/cascading-failures-auditor.md +306 -0
  8. package/kit/agents/executor.md +13 -0
  9. package/kit/agents/legacy-characterizer.md +378 -0
  10. package/kit/agents/load-shedding-instrumenter.md +297 -0
  11. package/kit/agents/observability-coverage-auditor.md +325 -0
  12. package/kit/agents/omm-auditor.md +47 -0
  13. package/kit/agents/payload-capture-instrumenter.md +283 -0
  14. package/kit/agents/planner.md +29 -0
  15. package/kit/agents/prr-conductor.md +8 -0
  16. package/kit/agents/refactor-safety-auditor.md +414 -0
  17. package/kit/agents/release-pipeline-auditor.md +360 -0
  18. package/kit/agents/seam-finder.md +367 -0
  19. package/kit/agents/shotgun-surgery-detector.md +359 -0
  20. package/kit/agents/storytelling-analyst.md +309 -0
  21. package/kit/agents/supabase-edge-fn-writer.md +12 -0
  22. package/kit/agents/verifier.md +30 -0
  23. package/kit/commands/auditar-cascading.md +111 -0
  24. package/kit/commands/auditar-marco.md +44 -1
  25. package/kit/commands/auditar-observabilidade-cobertura.md +183 -0
  26. package/kit/commands/auditar-refactor.md +219 -0
  27. package/kit/commands/auditar-release.md +109 -0
  28. package/kit/commands/capturar-payloads.md +193 -0
  29. package/kit/commands/caracterizar-prompt.md +195 -0
  30. package/kit/commands/caracterizar.md +212 -0
  31. package/kit/commands/concluir-marco.md +41 -1
  32. package/kit/commands/detectar-duplicacao.md +197 -0
  33. package/kit/commands/discutir-fase.md +41 -0
  34. package/kit/commands/encontrar-seams.md +136 -0
  35. package/kit/commands/forense.md +40 -1
  36. package/kit/commands/legacy.md +263 -0
  37. package/kit/commands/load-shedding.md +117 -0
  38. package/kit/commands/observabilidade.md +2 -0
  39. package/kit/commands/refactor-seguro.md +321 -0
  40. package/kit/commands/sre.md +3 -0
  41. package/kit/commands/storytelling.md +179 -0
  42. package/kit/skills/_shared-legacy/glossary.md +389 -0
  43. package/kit/skills/_shared-sre/glossary.md +139 -0
  44. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -0
  45. package/kit/skills/cascading-failures/SKILL.md +307 -0
  46. package/kit/skills/four-golden-signals/SKILL.md +17 -0
  47. package/kit/skills/hermetic-builds/SKILL.md +323 -0
  48. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -0
  49. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -0
  50. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -0
  51. package/kit/skills/legacy-extract-class/SKILL.md +203 -0
  52. package/kit/skills/legacy-monster-methods/SKILL.md +444 -0
  53. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -0
  54. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -0
  55. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -0
  56. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -0
  57. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -0
  58. package/kit/skills/llm-as-dependency/SKILL.md +436 -0
  59. package/kit/skills/load-shedding-graceful-degradation/SKILL.md +396 -0
  60. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -0
  61. package/kit/skills/release-engineering/SKILL.md +367 -0
  62. package/kit/skills/retry-strategies/SKILL.md +372 -0
  63. package/package.json +2 -2
@@ -0,0 +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. Bloqueador para legacy 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?".*
@@ -0,0 +1,331 @@
1
+ ---
2
+ name: legacy-effect-analysis
3
+ description: Use ao decidir quais testes escrever em código sem testes — effect sketch (cap 11-12 Feathers) rastreia propagação de efeitos do change point para inflection/pinch points onde 1 teste cobre N caminhos.
4
+ ---
5
+
6
+ # Legacy — Effect Analysis
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando user precisa decidir QUAIS testes escrever em código legado. Trigger phrases:
11
+
12
+ - "que tests preciso escrever para essa mudança?"
13
+ - "como sei que cobri tudo?"
14
+ - "effect sketch", "rastrear efeitos"
15
+ - "cap 11 Feathers", "cap 12 Feathers"
16
+ - "inflection point", "pinch point", "ponto de interceptação"
17
+ - "muitas mudanças na mesma área"
18
+ - "efeito propaga", "ripple effect"
19
+
20
+ Carrega entre `legacy-seams-and-test-harness` (já tenho seam) e `legacy-characterization-tests` (vou escrever quais testes?). Resposta: testes nos pinch points.
21
+
22
+ ## Regras absolutas
23
+
24
+ - **Effect = qualquer mudança observável após chamar o código.** 4 vetores: return value, parâmetros mutados, globals/state estático, side effects via colaborador (DB, HTTP, FS, log, queue).
25
+ - **Effect sketch SEMPRE em papel/whiteboard primeiro.** Diagramar à mão, não em ferramenta. Velocidade > formato. Goal é entender, não documentar.
26
+ - **Inflection point = funil estreito.** Lugar onde N caminhos convergem antes de divergir. 1 teste lá cobre N branches a montante. Foque tempo de teste aqui.
27
+ - **Pinch point é definido pelo problema, não pela arquitetura.** "É onde os efeitos da minha mudança convergem", pode ou não coincidir com camadas/módulos.
28
+ - **Effect-narrowing precede characterization.** Se sketch tem 30 efeitos, primeiro reduza superfície (encapsular variáveis, eliminar globals). Depois characterize a fronteira reduzida.
29
+ - **Shotgun surgery = mesmo change espalhado em N lugares.** Effect sketch detecta. Resposta canônica: extrair para 1 lugar antes de mudar (cap 21 — Single Responsibility Principle aplicado retroativamente).
30
+ - **Não confunda effect com call graph.** Call graph mostra QUEM chama QUEM. Effect sketch mostra O QUE MUDA. Função pode ser chamada 100 vezes e mudar nada (pure); função chamada 1 vez pode mudar 50 coisas.
31
+
32
+ ## Patterns canônicos
33
+
34
+ ### Pattern 1: 4 vetores de propagação de efeito
35
+
36
+ ```text
37
+ 1. RETURN VALUE
38
+ foo() retorna X → callers usam X em decisões/cálculos
39
+ Trace: grep callers de foo() + grep usos do resultado
40
+
41
+ 2. MUTATED PARAMETERS (output params)
42
+ foo(list) faz list.push(x) → caller continua a usar list mutada
43
+ Trace: parâmetros não-primitivos (objetos, arrays, ponteiros)
44
+
45
+ 3. GLOBAL / STATIC STATE
46
+ foo() faz globalCounter++ ou Foo.lastResult = X
47
+ Trace: writes em variáveis fora do escopo da função
48
+
49
+ 4. SIDE EFFECTS VIA COLLABORATOR
50
+ foo() chama db.save(), http.post(), log.warn(), fs.writeFile()
51
+ Trace: identificar colaboradores injetados/globais e verificar writes
52
+ ```
53
+
54
+ **Heurística:** se um vetor é vazio, ótimo (efeito mais contido). Se múltiplos, sketch é necessário antes de qualquer change.
55
+
56
+ ### Pattern 2: Workflow de effect sketch (cap 11)
57
+
58
+ ```text
59
+ 1. DESENHAR change point
60
+ Caixa central com nome do método/variável que vai mudar.
61
+
62
+ 2. ENUMERAR EFEITOS DIRETOS (1 nível)
63
+ Setas saindo da caixa central para tudo que MUDA quando aquele
64
+ change point muda:
65
+ - retorno (caixa "return")
66
+ - cada parâmetro mutado
67
+ - cada global escrito
68
+ - cada side effect
69
+
70
+ 3. ENUMERAR EFEITOS DERIVADOS (2 níveis)
71
+ Para cada efeito direto, perguntar: "quem usa isso?"
72
+ - return é consumido por callers — desenhe callers
73
+ - parâmetros mutados — quem inspeciona estado?
74
+ - globals — todos os leitores
75
+ - side effects — todos os observadores (queue consumers, DB readers)
76
+
77
+ 4. CONTINUE até bordas naturais
78
+ - Outro processo/serviço (effect cruza process boundary)
79
+ - Caller que não inspeciona resultado
80
+ - Side effect terminal (log persistido, sem ler de volta)
81
+
82
+ 5. IDENTIFICAR INFLECTION POINTS
83
+ Pontos onde múltiplas setas convergem ou divergem.
84
+ Inflection point é "o gargalo" — testar ali cobre subgrafos inteiros.
85
+
86
+ 6. PRIORIZAR TESTES
87
+ Test escrito num inflection point cobre N efeitos a montante.
88
+ 1 teste em pinch point > 10 testes em folhas.
89
+ ```
90
+
91
+ ### Pattern 3: Exemplo concreto — sketch de OrderProcessor.processOrder()
92
+
93
+ ```text
94
+ ┌──────────────────┐
95
+ │ Order.discount │ ← change point (mudando lógica)
96
+ └────────┬─────────┘
97
+
98
+ ┌────────────────────────┼─────────────────────────┐
99
+ ▼ ▼ ▼
100
+ Order.totalCents OrderEvent.payload AuditLog.entry
101
+ │ │ │
102
+ ▼ ▼ ▼
103
+ PaymentRequest.amount EventBus.publish AuditDB.write
104
+ │ │ │
105
+ ▼ ▼ ▼
106
+ Stripe.charge QueueConsumers (3 svcs) ComplianceReader
107
+
108
+
109
+ bank ledger
110
+ ```
111
+
112
+ **Inflection point óbvio:** `Order.totalCents` — TODOS os efeitos no payment side passam por essa propriedade.
113
+
114
+ **Estratégia de testes:**
115
+ - 1 test que asserta `Order.totalCents` para 5+ inputs (boundary, typical, etc.) → cobre subárvore Stripe inteira sem precisar mockar Stripe
116
+ - 1 test que asserta `OrderEvent.payload` shape → cobre EventBus side
117
+ - 1 test que asserta `AuditLog.entry` shape → cobre Audit side
118
+
119
+ 3 testes de 5 minutos cada cobrem 15+ pontos de propagação. Sem sketch, escreveria 15 testes em 5 lugares diferentes.
120
+
121
+ ### Pattern 4: Heurísticas para encontrar inflection points
122
+
123
+ | Sintoma no sketch | Provável inflection |
124
+ |---|---|
125
+ | Várias setas convergem em 1 nó antes de irradiar | Esse nó (estado intermediário) |
126
+ | Bordas de processo (call externo) | Antes da borda (assertar mensagem enviada) |
127
+ | Pontos de serialização/desserialização | Bem ali — congele forma serializada |
128
+ | Persistência (DB write) | Snapshot do row escrito |
129
+ | Eventos publicados em queue/bus | Snapshot do event payload |
130
+ | Function purity (sem side effect) | Return value direto |
131
+
132
+ ### Pattern 5: Effect-narrowing (cap 12)
133
+
134
+ Quando sketch tem 20+ efeitos, primeiro REDUZA antes de testar. Técnicas:
135
+
136
+ | Técnica | Reduz | Trade-off |
137
+ |---|---|---|
138
+ | **Encapsular variável global** | Globals viram fields → menos vetor 3 (state) | Caller pode precisar passar instância |
139
+ | **Imutabilidade no parâmetro** | Mutated params viram return values → menos vetor 2 | Allocation a cada call |
140
+ | **Extract method para state mutation** | Side effect concentrado em 1 método (pinch criado) | + 1 método na classe |
141
+ | **Replace temp with query** | Variável local → método; reduz dispersão | Computação repetida |
142
+ | **Move method (fora de classe X para Y)** | Effect sai do escopo de X → menos efeito a rastrear em X | Pode quebrar outros sketches |
143
+
144
+ **Princípio:** narrowing é PRECEDENTE a refactor. Faça-o com características de "pure mechanical" — pequena, reversível, comportamento idêntico.
145
+
146
+ ### Pattern 6: 4 perguntas canônicas antes de change
147
+
148
+ ```text
149
+ 1. "Que efeitos esse change tem?"
150
+ → desenhe sketch (vetores 1-4)
151
+
152
+ 2. "Onde converge antes de divergir?"
153
+ → inflection point(s)
154
+
155
+ 3. "O que preciso testar para SENSE essas convergências?"
156
+ → 1 test por inflection (input variado), assertando estado intermediário
157
+
158
+ 4. "Quais são as bordas naturais?"
159
+ → onde termina o sketch; depois disso é território de outras teams/serviços
160
+ ```
161
+
162
+ ### Pattern 7: Detect shotgun surgery via sketch
163
+
164
+ Se ao desenhar sketch para mudança X, você encontra que X aparece em N lugares idênticos espalhados:
165
+
166
+ ```text
167
+ (mudança X)
168
+
169
+ ┌───────┬────────┬───┴───┬────────┬───────┐
170
+ ▼ ▼ ▼ ▼ ▼ ▼
171
+ FileA FileB FileC FileD FileE FileF
172
+ linha linha linha linha linha linha
173
+ 42 189 67 23 104 56
174
+ ```
175
+
176
+ **Sintoma:** mesma lógica copiada em N pontos.
177
+ **Resposta:** ANTES de mudar, extrair para função/classe única (cap 21). Depois, mudança vira UM ponto. Effect sketch original com 6 setas vira 1 seta para 1 ponto.
178
+
179
+ ### Pattern 8: Effect sketch tooling
180
+
181
+ Não precisa de ferramenta sofisticada. Em ordem de preferência:
182
+
183
+ 1. **Papel + caneta** (sempre primeiro — velocidade)
184
+ 2. **Whiteboard físico** (se mais que 1 pessoa)
185
+ 3. **Whiteboard digital** (Excalidraw, Miro) — quando precisa salvar/compartilhar
186
+ 4. **Texto ASCII em PR description** (quando vira artefato persistente)
187
+ 5. **Mermaid graph** (last resort — overhead de syntax > benefit visual)
188
+
189
+ **Anti-tooling:** UML "official", Visio, ferramentas que exigem layout perfeito. Sketch é descartável e iterativo.
190
+
191
+ ### Pattern 9: Heurística de cobertura via inflection
192
+
193
+ Para mudança em legacy code, cobertura mínima = 1 test por inflection point.
194
+
195
+ ```text
196
+ N inflection points identificados via sketch
197
+ × 5+ inputs cobrindo grupos de equivalência (ver legacy-characterization-tests Pattern 2)
198
+ = N × 5 testes mínimos para refactor
199
+
200
+ Se muito alto (50+), considere effect-narrowing primeiro.
201
+ Se muito baixo (1 inflection × 5 inputs = 5), provavelmente sketch
202
+ incompleto — verifique se cobriu todos os 4 vetores.
203
+ ```
204
+
205
+ ## Anti-patterns
206
+
207
+ ### ANTI: pular sketch "porque é óbvio"
208
+
209
+ ```text
210
+ ANTI: "esse método tem 50 linhas, eu vejo o que ele faz, vou testar
211
+ direto".
212
+
213
+ PROBLEMA: efeitos não-óbvios são exatamente os que escapam:
214
+ - mutação de parâmetro objeto que caller depende
215
+ - side effect via dependency injetada (parece pure mas não é)
216
+ - global lido condicionalmente em caminho raro
217
+ Bug em prod 3 semanas depois pelos efeitos não-vistos.
218
+
219
+ CERTO: SEMPRE 5 minutos de sketch. Mesmo método "óbvio" → desenhe,
220
+ confirme, então teste. 5 min poupam horas.
221
+ ```
222
+
223
+ ### ANTI: testar todas as folhas do sketch
224
+
225
+ ```text
226
+ ANTI: 30 setas no sketch → 30 testes (1 por folha).
227
+
228
+ PROBLEMA: massive test suite, slow CI, alto custo de manutenção,
229
+ tests redundantes (várias folhas testam mesma branch a
230
+ montante). Sinal de "test theatre" — looks safe but isn't.
231
+
232
+ CERTO: 1 teste por inflection point. Se inflection cobre 10 folhas,
233
+ teste lá. Folhas só ganham teste próprio se há comportamento
234
+ distinto (folha = inflection menor naquele contexto).
235
+ ```
236
+
237
+ ### ANTI: testar APENAS na superfície (1 nível de sketch)
238
+
239
+ ```text
240
+ ANTI: testar só `processOrder` retorno, sem verificar side effects.
241
+
242
+ PROBLEMA: side effects são tipicamente onde regressão escapa.
243
+ processOrder pode retornar valor correto MAS escrever no
244
+ DB errado, publicar event errado, logar PII. Test verde
245
+ mascara breakage.
246
+
247
+ CERTO: percorrer sketch completo. Side effect significativo
248
+ (DB write, event publish, log) recebe assertion no test
249
+ relevante. Use fakes que coletam side effects, asserte sobre
250
+ state final do fake.
251
+ ```
252
+
253
+ ### ANTI: confundir call graph com effect sketch
254
+
255
+ ```text
256
+ ANTI: "fiz um call graph IDE-generated, isso é meu effect sketch".
257
+
258
+ PROBLEMA: call graph mostra "fooCallsBar". Não mostra que `bar`
259
+ modifica global lido por `baz` que retorna decisão para
260
+ `qux`. Effect path passa por chamadas + state, não só
261
+ chamadas.
262
+
263
+ CERTO: sketch tem que ser feito por pessoa, lendo código, perguntando
264
+ "o que muda?". Call graph é input ao sketch, não substituto.
265
+ ```
266
+
267
+ ### ANTI: change com sketch não-validado
268
+
269
+ ```text
270
+ ANTI: desenhei sketch, achei 3 inflection points, escrevi tests,
271
+ mudei código. Sketch não foi validado contra o código real
272
+ por outra pessoa.
273
+
274
+ PROBLEMA: effect omitido (vetor 4 — side effect via colaborador raro)
275
+ significa teste ausente significa regressão escapa.
276
+
277
+ CERTO: sketch validado por 2ª pessoa OU validado contra mutation
278
+ testing. Mutants survived = pontos não-cobertos = potencial
279
+ gap no sketch original.
280
+ ```
281
+
282
+ ### ANTI: shotgun surgery sem extract first
283
+
284
+ ```text
285
+ ANTI: "mesma lógica em 6 lugares, vou alterar todos os 6".
286
+
287
+ PROBLEMA: 6 chances de errar. PR de 600 linhas. Test exigiria 6×
288
+ characterization. Próxima mudança = mesma cirurgia.
289
+
290
+ CERTO: extract first (cap 21). PR1 — extrair para função única,
291
+ cada chamada vira call para a função. PR2 — alterar a função.
292
+ Sketch original encolhe de 6 setas → 1 seta. Tests também.
293
+ ```
294
+
295
+ ## Verificação
296
+
297
+ Antes de declarar effect analysis completa:
298
+
299
+ 1. **Sketch desenhado** — papel/whiteboard com change point central + 4 vetores explorados
300
+ 2. **Todos os 4 vetores considerados** — return, mutated params, globals, side effects via collaborator
301
+ 3. **Pelo menos 1 inflection point identificado** — se não, sketch incompleto OU change é trivial demais
302
+ 4. **Effect-narrowing aplicado** se sketch tem > 15 efeitos
303
+ 5. **Plano de testes derivado do sketch** — N testes em inflection points, NÃO em folhas
304
+ 6. **Sketch validado** — 2ª pessoa OR mutation testing pós-implementação
305
+ 7. **Bordas naturais identificadas** — onde para de rastrear (process boundary, terminal side effect)
306
+
307
+ ## Limiar de "pronto para testar/refatorar"
308
+
309
+ ```text
310
+ Sketch desenhado: sim
311
+ Vetores 1-4 enumerados: todos cobertos
312
+ Inflection points identificados: ≥ 1 (1-3 típico)
313
+ Plano de testes mapeado para sketch: sim (1 teste por inflection)
314
+ Cobertura esperada com tests planejados: ≥ 70% behavioral
315
+ Effect-narrowing aplicado: se necessário
316
+ ```
317
+
318
+ Se algum item incompleto → não inicie testes/refactor. Volte ao sketch.
319
+
320
+ ---
321
+
322
+ ## Ver também
323
+
324
+ - [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário (effect sketch, inflection point, pinch point, shotgun surgery)
325
+ - [`legacy-characterization-tests`](../legacy-characterization-tests/SKILL.md) — characterization rodada NOS inflection points achados aqui
326
+ - [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — break-deps em inflection points = test harness mínimo
327
+ - [`legacy-monster-methods`](../legacy-monster-methods/SKILL.md) — monster method tem sketch interno (em uma única função)
328
+ - [`legacy-sprout-wrap-techniques`](../legacy-sprout-wrap-techniques/SKILL.md) — sprout point ideal = inflection point pré-existente
329
+ - [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate exige effect sketch para refactor de arquivos grandes/críticos
330
+
331
+ *Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 11: "I Need to Make a Change. What Methods Should I Test?" + Cap 12: "I Need to Make Many Changes in One Area" + Cap 16: "I Don't Understand the Code Well Enough to Change It".*