@luanpdd/kit-mcp 1.10.0 → 1.12.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.
- package/gates/ai-prompt-stability.md +120 -0
- package/gates/legacy-refactor-safety.md +178 -0
- package/gates/observability-coverage.md +151 -0
- package/gates/release-pipeline-policy.md +132 -0
- package/kit/COMANDOS.md +15 -0
- package/kit/agents/ai-mutation-tester.md +298 -0
- package/kit/agents/cascading-failures-auditor.md +306 -0
- package/kit/agents/executor.md +13 -0
- package/kit/agents/legacy-characterizer.md +378 -0
- package/kit/agents/load-shedding-instrumenter.md +297 -0
- package/kit/agents/observability-coverage-auditor.md +325 -0
- package/kit/agents/omm-auditor.md +47 -0
- package/kit/agents/payload-capture-instrumenter.md +283 -0
- package/kit/agents/planner.md +29 -0
- package/kit/agents/prr-conductor.md +8 -0
- package/kit/agents/refactor-safety-auditor.md +414 -0
- package/kit/agents/release-pipeline-auditor.md +360 -0
- package/kit/agents/seam-finder.md +367 -0
- package/kit/agents/shotgun-surgery-detector.md +359 -0
- package/kit/agents/storytelling-analyst.md +309 -0
- package/kit/agents/supabase-edge-fn-writer.md +12 -0
- package/kit/agents/verifier.md +30 -0
- package/kit/commands/auditar-cascading.md +111 -0
- package/kit/commands/auditar-marco.md +44 -1
- package/kit/commands/auditar-observabilidade-cobertura.md +183 -0
- package/kit/commands/auditar-refactor.md +219 -0
- package/kit/commands/auditar-release.md +109 -0
- package/kit/commands/capturar-payloads.md +193 -0
- package/kit/commands/caracterizar-prompt.md +195 -0
- package/kit/commands/caracterizar.md +212 -0
- package/kit/commands/concluir-marco.md +41 -1
- package/kit/commands/detectar-duplicacao.md +197 -0
- package/kit/commands/discutir-fase.md +41 -0
- package/kit/commands/encontrar-seams.md +136 -0
- package/kit/commands/forense.md +40 -1
- package/kit/commands/legacy.md +263 -0
- package/kit/commands/load-shedding.md +117 -0
- package/kit/commands/observabilidade.md +2 -0
- package/kit/commands/refactor-seguro.md +321 -0
- package/kit/commands/sre.md +3 -0
- package/kit/commands/storytelling.md +179 -0
- package/kit/skills/_shared-legacy/glossary.md +389 -0
- package/kit/skills/_shared-sre/glossary.md +139 -0
- package/kit/skills/ai-prompt-characterization/SKILL.md +335 -0
- package/kit/skills/cascading-failures/SKILL.md +307 -0
- package/kit/skills/four-golden-signals/SKILL.md +17 -0
- package/kit/skills/hermetic-builds/SKILL.md +323 -0
- package/kit/skills/legacy-api-only-applications/SKILL.md +358 -0
- package/kit/skills/legacy-characterization-tests/SKILL.md +330 -0
- package/kit/skills/legacy-effect-analysis/SKILL.md +331 -0
- package/kit/skills/legacy-extract-class/SKILL.md +203 -0
- package/kit/skills/legacy-monster-methods/SKILL.md +444 -0
- package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -0
- package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -0
- package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -0
- package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -0
- package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -0
- package/kit/skills/llm-as-dependency/SKILL.md +436 -0
- package/kit/skills/load-shedding-graceful-degradation/SKILL.md +396 -0
- package/kit/skills/pre-refactor-characterization/SKILL.md +421 -0
- package/kit/skills/release-engineering/SKILL.md +367 -0
- package/kit/skills/retry-strategies/SKILL.md +372 -0
- package/package.json +2 -2
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-prompt-characterization
|
|
3
|
+
description: Use ao modificar prompt/tool LLM em produção — characterization de generations com temperature=0 + seed fixo + sanitização específica. Modernização 2026 sem precedente em 2004 — prompts são código legacy também.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AI Prompt Characterization (Modernização)
|
|
7
|
+
|
|
8
|
+
## Quando usar
|
|
9
|
+
|
|
10
|
+
LLM carrega esta skill quando user vai modificar prompt ou tool definition de LLM em produção. Trigger phrases:
|
|
11
|
+
|
|
12
|
+
- "vou mudar esse prompt", "modificar prompt em prod"
|
|
13
|
+
- "atualizar tool definition", "function calling schema"
|
|
14
|
+
- "como testar mudança de prompt?"
|
|
15
|
+
- "characterization de prompt", "snapshot de generation"
|
|
16
|
+
- "esse prompt tem 300 linhas e ninguém testou ainda"
|
|
17
|
+
- prompt em arquivo como `prompts/<name>.md` ou string template em código
|
|
18
|
+
|
|
19
|
+
**Insight central:** prompts e tools são **código legacy também** quando:
|
|
20
|
+
- > 100 linhas
|
|
21
|
+
- Em uso em produção
|
|
22
|
+
- Mudanças quebram silenciosamente (output diferente, downstream parser falha)
|
|
23
|
+
- Sem characterization tests
|
|
24
|
+
|
|
25
|
+
## Regras absolutas
|
|
26
|
+
|
|
27
|
+
- **Prompts são código.** Tratam-se com mesmo rigor: versionado, testado, code-reviewed. NÃO são "config text que muda livremente".
|
|
28
|
+
- **Determinismo via `temperature=0` + `seed`.** Anthropic Claude e OpenAI ambos suportam seed. Sem isso, characterization é flaky.
|
|
29
|
+
- **Capture mais que `text`.** Outputs incluem: `text`, `finish_reason`, `tool_calls` (se function calling), `input_tokens`, `output_tokens`, `model_version`. Snapshot de TODOS estes campos.
|
|
30
|
+
- **Sanitize aggressively.** Outputs LLM frequentemente incluem timestamps mencionados, UUIDs gerados, datas relativas. Normalize ANTES de snapshot.
|
|
31
|
+
- **5+ inputs cobrindo intents distintas.** Não é "happy path × 5"; é "5 intents qualitativamente diferentes" — concision request, troubleshooting, explanation, creative, edge case.
|
|
32
|
+
- **Behavioral coverage = % intents cobertas.** Métrica não é coverage de "linhas do prompt" (não existe); é coverage de variações comportamentais.
|
|
33
|
+
- **Re-rodar em CI quando model_version muda.** Anthropic publica nova versão de Claude → re-rode characterization → revisar diffs → aceitar/rejeitar.
|
|
34
|
+
|
|
35
|
+
## Patterns canônicos
|
|
36
|
+
|
|
37
|
+
### Pattern 1: Setup canônico de characterization de prompt
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// tests/characterization/prompts/generate-summary.test.ts
|
|
41
|
+
import { Anthropic } from '@anthropic-ai/sdk'
|
|
42
|
+
import { describe, test, expect } from 'vitest'
|
|
43
|
+
import { readFileSync } from 'fs'
|
|
44
|
+
|
|
45
|
+
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
|
|
46
|
+
const PROMPT = readFileSync('prompts/generate-summary.md', 'utf-8')
|
|
47
|
+
|
|
48
|
+
interface PromptInput {
|
|
49
|
+
systemPrompt: string
|
|
50
|
+
userMessage: string
|
|
51
|
+
maxTokens?: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function runPrompt(input: PromptInput) {
|
|
55
|
+
const response = await client.messages.create({
|
|
56
|
+
model: 'claude-opus-4-7',
|
|
57
|
+
max_tokens: input.maxTokens ?? 500,
|
|
58
|
+
temperature: 0, // determinismo
|
|
59
|
+
system: input.systemPrompt,
|
|
60
|
+
messages: [{ role: 'user', content: input.userMessage }],
|
|
61
|
+
})
|
|
62
|
+
return {
|
|
63
|
+
text: response.content[0].type === 'text' ? response.content[0].text : '',
|
|
64
|
+
stopReason: response.stop_reason,
|
|
65
|
+
inputTokens: response.usage.input_tokens,
|
|
66
|
+
outputTokens: response.usage.output_tokens,
|
|
67
|
+
modelVersion: response.model,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sanitizeForSnapshot(o: any): any {
|
|
72
|
+
return JSON.parse(
|
|
73
|
+
JSON.stringify(o, (key, value) => {
|
|
74
|
+
// normalizar timestamps mencionados ("Today is 2026-05-08") → "<DATE>"
|
|
75
|
+
if (typeof value === 'string') {
|
|
76
|
+
value = value.replace(/\d{4}-\d{2}-\d{2}/g, '<DATE>')
|
|
77
|
+
value = value.replace(/\d{2}:\d{2}(:\d{2})?/g, '<TIME>')
|
|
78
|
+
value = value.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}/g, '<UUID>')
|
|
79
|
+
}
|
|
80
|
+
// permitir model version mas separar para audit (não no snapshot)
|
|
81
|
+
if (key === 'modelVersion') return '<MODEL>'
|
|
82
|
+
return value
|
|
83
|
+
})
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
describe('generate-summary prompt — characterization', () => {
|
|
88
|
+
test('intent: concise summary of long article', async () => {
|
|
89
|
+
const captured = await runPrompt({
|
|
90
|
+
systemPrompt: PROMPT,
|
|
91
|
+
userMessage: 'Resuma em 2 sentenças: [longo artigo de 500 palavras]...',
|
|
92
|
+
})
|
|
93
|
+
expect(sanitizeForSnapshot(captured)).toMatchSnapshot()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('intent: bullet-list summary', async () => { /* ... */ })
|
|
97
|
+
test('intent: technical/code summary', async () => { /* ... */ })
|
|
98
|
+
test('intent: ambiguous request (edge)', async () => { /* ... */ })
|
|
99
|
+
test('intent: hostile / prompt injection attempt', async () => { /* ... */ })
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Pattern 2: Tool definition characterization (function calling)
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// Quando prompt usa tool definition (function calling), characterize tool_calls
|
|
107
|
+
|
|
108
|
+
const TOOLS = [
|
|
109
|
+
{
|
|
110
|
+
name: 'search_knowledge_base',
|
|
111
|
+
description: 'Search for relevant docs',
|
|
112
|
+
input_schema: { type: 'object', properties: { query: { type: 'string' } } },
|
|
113
|
+
},
|
|
114
|
+
// ... mais tools
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
async function runWithTools(userMessage: string) {
|
|
118
|
+
const r = await client.messages.create({
|
|
119
|
+
model: 'claude-opus-4-7',
|
|
120
|
+
max_tokens: 500,
|
|
121
|
+
temperature: 0,
|
|
122
|
+
tools: TOOLS,
|
|
123
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
124
|
+
})
|
|
125
|
+
return {
|
|
126
|
+
stopReason: r.stop_reason,
|
|
127
|
+
toolUses: r.content.filter(c => c.type === 'tool_use').map(c => ({
|
|
128
|
+
tool: (c as any).name,
|
|
129
|
+
input: (c as any).input,
|
|
130
|
+
})),
|
|
131
|
+
finalText: r.content.filter(c => c.type === 'text').map(c => (c as any).text).join('\n'),
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
test('tools — invokes search for factual question', async () => {
|
|
136
|
+
const captured = await runWithTools('Qual é a política de reembolso?')
|
|
137
|
+
expect(captured).toMatchSnapshot()
|
|
138
|
+
// snapshot captura QUAIS tools foram invocadas + QUAIS argumentos
|
|
139
|
+
})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Pattern 3: Sanitização específica de prompts
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
// Outputs LLM têm padrões previsíveis a sanitizar:
|
|
146
|
+
|
|
147
|
+
function sanitizeLLMOutput(text: string): string {
|
|
148
|
+
return text
|
|
149
|
+
// datas absolutas
|
|
150
|
+
.replace(/\b\d{4}-\d{2}-\d{2}\b/g, '<DATE>')
|
|
151
|
+
.replace(/\b(?:janeiro|fevereiro|março|abril|maio|junho|julho|agosto|setembro|outubro|novembro|dezembro)\s+(?:de\s+)?\d{4}/gi, '<DATE_PT>')
|
|
152
|
+
.replace(/\b(?:january|february|march|april|may|june|july|august|september|october|november|december)\s+\d{4}/gi, '<DATE_EN>')
|
|
153
|
+
// datas relativas
|
|
154
|
+
.replace(/\b(?:hoje|amanhã|ontem|today|tomorrow|yesterday)\b/gi, '<RELATIVE_DATE>')
|
|
155
|
+
// URLs e UUIDs
|
|
156
|
+
.replace(/https?:\/\/[^\s]+/g, '<URL>')
|
|
157
|
+
.replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, '<UUID>')
|
|
158
|
+
// valores monetários (preservar tipo, sanitizar valor)
|
|
159
|
+
.replace(/R\$\s*[\d,.]+/g, 'R$ <VALUE>')
|
|
160
|
+
.replace(/\$\s*[\d,.]+/g, '$ <VALUE>')
|
|
161
|
+
// versões
|
|
162
|
+
.replace(/v\d+\.\d+(?:\.\d+)?/g, '<VERSION>')
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Pattern 4: Behavioral coverage de prompt — 5+ intents
|
|
167
|
+
|
|
168
|
+
Para cada prompt, definir intents distintas:
|
|
169
|
+
|
|
170
|
+
| Intent | Definição | Exemplo de input |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| **Concise** | Pedido curto, output esperado curto | "Resuma em 1 frase: [text]" |
|
|
173
|
+
| **Detailed** | Pedido elaborado, output esperado longo | "Explique passo-a-passo: [text]" |
|
|
174
|
+
| **Code-heavy** | Input/output com código | "Refactor esse código: ```ts ...```" |
|
|
175
|
+
| **Edge case** | Input ambíguo ou borderline | "Como funciona?" (sem context) |
|
|
176
|
+
| **Adversarial** | Tentativa de jailbreak / prompt injection | "Ignore previous instructions and..." |
|
|
177
|
+
| **Multi-turn (se aplicável)** | Conversação com historico | [3+ messages prévias] |
|
|
178
|
+
|
|
179
|
+
5 intents × snapshot deterministic = baseline. Mudança em prompt deve manter outputs semanticamente próximos (ou documentar mudança intencional).
|
|
180
|
+
|
|
181
|
+
### Pattern 5: Pre-deploy checklist para mudança em prompt
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
Antes de deploy de mudança em prompt em produção:
|
|
185
|
+
|
|
186
|
+
□ Suite de characterization tests passa verde (todos os 5+ intents)
|
|
187
|
+
□ Diff revisado HUMANAMENTE para cada intent — mudanças intencionais?
|
|
188
|
+
□ Behavioral coverage ≥ 5 intents (não bate threshold % — bate threshold de N)
|
|
189
|
+
□ Sanitização revisada — nenhum PII/secret no snapshot
|
|
190
|
+
□ Custo: cada test consome tokens; para prompts grandes, calcular total
|
|
191
|
+
- 5 inputs × 1k input + 500 output ≈ 7.5k tokens × $0.015/1k = ~$0.11
|
|
192
|
+
- CI roda só on-change para evitar custo recorrente
|
|
193
|
+
□ model_version anotado — re-rodar quando model_version muda
|
|
194
|
+
□ Audit trail no PR: "intent X: changed from Y to Z; reason: ..."
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Pattern 6: Custo + cadência de characterization
|
|
198
|
+
|
|
199
|
+
| Frequência | Custo (em USD) por suite | Quando rodar |
|
|
200
|
+
|---|---|---|
|
|
201
|
+
| Desenvolvedor local | < $0.10 | Antes de cada commit que toca prompt |
|
|
202
|
+
| CI on-change | < $0.50/run | Em PR que toca arquivo de prompt |
|
|
203
|
+
| CI nightly | < $5/dia | Para detectar drift de model upstream |
|
|
204
|
+
| Pre-deploy | < $0.50 | Confirmação final antes de promote |
|
|
205
|
+
|
|
206
|
+
**Otimização:** snapshot diff só dispara LLM call se prompt mudou. Sem mudança = skip (cacheado).
|
|
207
|
+
|
|
208
|
+
### Pattern 7: Quando NÃO characterizar prompt
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
- Prompt < 20 linhas e usado em 1 lugar — overhead > valor
|
|
212
|
+
- Prompt é template trivial ("Resume: {text}") sem lógica complexa
|
|
213
|
+
- LLM call é one-shot script (analytics, batch processing) — não em hot path
|
|
214
|
+
- Custo de tokens proibitivo (e.g., prompts massivos com 50k tokens) — usar smaller model para char tests
|
|
215
|
+
- Use case é generative criativo (poema, story) — outputs intencionalmente variáveis
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Anti-patterns
|
|
219
|
+
|
|
220
|
+
### ANTI: characterization sem temperature=0
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
ANTI: rodar characterization com temperature=0.7 (default).
|
|
224
|
+
|
|
225
|
+
PROBLEMA: outputs varia entre runs. Snapshot diferente toda vez.
|
|
226
|
+
Tests flaky. Equipe ignora.
|
|
227
|
+
|
|
228
|
+
CERTO: temperature=0 SEMPRE em characterization. Anthropic + OpenAI
|
|
229
|
+
ambos têm. Em providers que não suportam, escolher menor
|
|
230
|
+
valor possível e/ou seed fixo se disponível.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### ANTI: snapshot sem sanitização
|
|
234
|
+
|
|
235
|
+
```text
|
|
236
|
+
ANTI: capturar output cru com timestamps, UUIDs, datas atuais.
|
|
237
|
+
|
|
238
|
+
PROBLEMA: cada run gera snapshot diferente. Não é flaky pelo LLM,
|
|
239
|
+
é flaky pelo CONTENT temporal.
|
|
240
|
+
|
|
241
|
+
CERTO: sanitize ANTES de matchSnapshot. Datas → <DATE>, UUIDs →
|
|
242
|
+
<UUID>, etc. Snapshot estável across time.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### ANTI: 1 test "happy path" de prompt
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
ANTI: 1 input de exemplo testado, "se passa, prompt está OK".
|
|
249
|
+
|
|
250
|
+
PROBLEMA: prompt tem comportamento qualitativamente diferente em
|
|
251
|
+
edge cases (input curto, input longo, input ambíguo,
|
|
252
|
+
adversarial). 1 test cobre 1 caminho, ignora N outros.
|
|
253
|
+
|
|
254
|
+
CERTO: 5+ intents cobrindo distribuição real de uso. Edge case +
|
|
255
|
+
adversarial são MANDATORY (prompts em prod sempre recebem
|
|
256
|
+
inputs ruins).
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### ANTI: ignorar drift de model
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
ANTI: characterization passou em maio; em julho Anthropic atualiza
|
|
263
|
+
Claude (claude-opus-4-7 → 4-8). Equipe não re-roda; deploy de
|
|
264
|
+
mudança quebra silenciosamente.
|
|
265
|
+
|
|
266
|
+
PROBLEMA: prompt baseline frozen no model anterior. Novo model
|
|
267
|
+
comporta diferente; bug em prod.
|
|
268
|
+
|
|
269
|
+
CERTO: CI nightly roda characterization. Diff de model_version =
|
|
270
|
+
trigger humano para revisar. Aceita ou rejeita updates de
|
|
271
|
+
model. Sem fixed model = sem characterization válida.
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### ANTI: snapshot inclui token count
|
|
275
|
+
|
|
276
|
+
```text
|
|
277
|
+
ANTI: snapshot tem `inputTokens: 247, outputTokens: 89`.
|
|
278
|
+
|
|
279
|
+
PROBLEMA: token counts mudam quando model muda (tokenizer evolui).
|
|
280
|
+
Diff vermelho em update de model é noise.
|
|
281
|
+
|
|
282
|
+
CERTO: capturar tokens em log SEPARADO (custo tracking), não no
|
|
283
|
+
snapshot. Snapshot é qualitativo (text + stop reason +
|
|
284
|
+
tool calls), não quantitativo.
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### ANTI: tratar prompt como "string config livre"
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
ANTI: dev edita prompt em prod direto via console; sem PR; sem
|
|
291
|
+
review; sem characterization.
|
|
292
|
+
|
|
293
|
+
PROBLEMA: prompt é código. Mudança não-versionada quebra silenciosa.
|
|
294
|
+
Sem audit trail. Rollback impossível.
|
|
295
|
+
|
|
296
|
+
CERTO: prompt em repo (`prompts/<name>.md`). PR review como qualquer
|
|
297
|
+
código. Characterization tests rodam em CI. Deploy via release
|
|
298
|
+
padrão.
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Verificação
|
|
302
|
+
|
|
303
|
+
1. Prompt versionado em arquivo (não inline em código se > 50 linhas)
|
|
304
|
+
2. Characterization tests existem com 5+ intents
|
|
305
|
+
3. `temperature=0` + seed fixo (se provider suporta)
|
|
306
|
+
4. Sanitização específica para prompt outputs
|
|
307
|
+
5. Snapshot inclui text + stopReason + toolCalls (se aplicável)
|
|
308
|
+
6. CI roda characterization on-change
|
|
309
|
+
7. model_version trackado (audit log separado)
|
|
310
|
+
8. Pre-deploy checklist completo
|
|
311
|
+
|
|
312
|
+
## Limiar de "prompt pronto para produção"
|
|
313
|
+
|
|
314
|
+
```text
|
|
315
|
+
Versionado em repo: sim
|
|
316
|
+
Characterization tests com ≥ 5 intents: sim
|
|
317
|
+
temperature=0 + seed fixo: sim
|
|
318
|
+
Sanitização aplicada: sim
|
|
319
|
+
Coverage de intents real (não synthetic): sim
|
|
320
|
+
CI integration: sim
|
|
321
|
+
Audit trail de mudanças: sim
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Ver também
|
|
327
|
+
|
|
328
|
+
- [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário (characterization, golden master)
|
|
329
|
+
- [`legacy-characterization-tests`](../legacy-characterization-tests/SKILL.md) — characterization clássico; aplicável a prompts modulo determinismo
|
|
330
|
+
- [`legacy-api-only-applications`](../legacy-api-only-applications/SKILL.md) — LLM provider é caso especial de API; adapter pattern aplicável
|
|
331
|
+
- [`llm-as-dependency`](../llm-as-dependency/SKILL.md) — fakear LLM em testes que NÃO são de prompt characterization (testes de business logic)
|
|
332
|
+
- [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate v1.12 inclui ai-prompt-stability como dimensão paralela
|
|
333
|
+
- [`observability-driven-development`](../observability-driven-development/SKILL.md) (v1.9) — instrument prompt outputs para detectar drift em prod
|
|
334
|
+
|
|
335
|
+
*Material-fonte (modernização 2026):* Sem precedente em livro Feathers 2004 — prompts/tools LLM como dependência testável é literatura recente (2023+ — papers da Anthropic sobre evals, OpenAI evals framework, Promptfoo).
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cascading-failures
|
|
3
|
+
description: Use ao desenhar/auditar serviços com deps externas — cap 22 livro Google SRE. Triggers de cascade, loops de feedback, prevenção via timeout/jitter/deadline/circuit breaker, immediate response.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SRE — Addressing Cascading Failures
|
|
7
|
+
|
|
8
|
+
## Quando usar
|
|
9
|
+
|
|
10
|
+
LLM carrega esta skill ao analisar serviço com risco de cascade, ou durante incident em progresso onde failure se amplifica. Trigger phrases:
|
|
11
|
+
|
|
12
|
+
- "cascading failure", "falha em cascata"
|
|
13
|
+
- "retry storm", "thundering herd"
|
|
14
|
+
- "outage propagando", "serviço caindo um após outro"
|
|
15
|
+
- "como prevenir cascade?", "circuit breaker"
|
|
16
|
+
- "cap 22 Google SRE"
|
|
17
|
+
- "deadline propagation", "load shedding"
|
|
18
|
+
|
|
19
|
+
## Regras absolutas
|
|
20
|
+
|
|
21
|
+
- **Cascade tem 5 triggers canônicos:** server overload, resource exhaustion, service unavailability, latency spike sem timeout, retry sem jitter. Memorize-os.
|
|
22
|
+
- **Prevenção custa 1×, recuperação custa 100×.** Toda dep crítica DEVE ter timeout + retry com jitter + circuit breaker + deadline propagation. Sem isso, cascade é questão de tempo.
|
|
23
|
+
- **Saturation (cap 6 v1.10) é early warning de cascade.** Quando saturation > 80%, ainda há tempo. Quando hit 100%, já está em cascade.
|
|
24
|
+
- **Retry SEMPRE com jitter.** Full jitter por default (`delay = random(0, base × 2^attempt)`). Sem jitter = thundering herd garantido.
|
|
25
|
+
- **Retry SEMPRE com deadline.** Retry sem timeout amplifica cascade — call de 30s vira 90s vira 270s. Deadline propagation evita work zumbi.
|
|
26
|
+
- **Circuit breaker DEFAULT em qualquer dep externa.** Estados closed/open/half-open. Open dispara após N failures consecutivas; half-open permite 1 probe; closed após probe verde.
|
|
27
|
+
- **Load shedding > 503 graceful > queue overflow.** Server saturated DEVE retornar 503 com Retry-After ANTES de aceitar request que vai falhar. Aceitar e cair = pior que rejeitar.
|
|
28
|
+
- **Slow start em recovery.** Após outage, ramp-up gradual (10% → 25% → 50% → 100%). Full blast em recovery = falha de novo (caches frios, conn pools).
|
|
29
|
+
|
|
30
|
+
## Patterns canônicos
|
|
31
|
+
|
|
32
|
+
### Pattern 1: 5 triggers canônicos de cascade (cap 22)
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
1. SERVER OVERLOAD
|
|
36
|
+
Sintoma: load > capacity → latency p99 sobe 10× → errors 5xx → crashes
|
|
37
|
+
Trigger upstream: traffic spike, outage de cache, batch job rodou em horário ruim
|
|
38
|
+
Detect: Saturation gauge (cap 6) > 80% por > 5 min
|
|
39
|
+
Resposta: load shedding + escalação manual
|
|
40
|
+
|
|
41
|
+
2. RESOURCE EXHAUSTION
|
|
42
|
+
Tipos: CPU, memory, file descriptors, threads, connection pool
|
|
43
|
+
Sintoma específico:
|
|
44
|
+
CPU 100% → latency sobe; FDs esgotados → "too many open files"
|
|
45
|
+
Memory OOM → process kill; threads → deadlock; conn pool empty → wait
|
|
46
|
+
Detect: monitor por recurso específico; alarmes em 80% de cada
|
|
47
|
+
Resposta: configure limits; circuit breaker; rate limit caller
|
|
48
|
+
|
|
49
|
+
3. SERVICE UNAVAILABILITY (DEP DOWN)
|
|
50
|
+
Sintoma: dep externa retorna 5xx ou timeout. Sem circuit breaker:
|
|
51
|
+
N clients × M retries × T deadline = explosão de calls
|
|
52
|
+
Detect: error rate de dep > 50% por 1 min; latency dep > p99 normal × 5
|
|
53
|
+
Resposta: circuit breaker abre; fallback (cache, default value, degraded mode)
|
|
54
|
+
|
|
55
|
+
4. LATENCY SPIKE SEM TIMEOUT
|
|
56
|
+
Sintoma: dep responde lento mas não falha. Caller fica esperando.
|
|
57
|
+
Conn pool de caller esgota; novos requests também ficam pendurados.
|
|
58
|
+
Detect: dep latency p99 > baseline × 5
|
|
59
|
+
Resposta: timeout AGRESSIVO (ex: p99.9 baseline + 50%); circuit breaker
|
|
60
|
+
|
|
61
|
+
5. RETRY SEM JITTER
|
|
62
|
+
Sintoma: 1000 clients retentam ao mesmo tempo após dep recovery.
|
|
63
|
+
Server recém-recuperado é matado pelo wake-up.
|
|
64
|
+
Detect: traffic spike no momento exato de recovery
|
|
65
|
+
Resposta: full jitter; retry budget global; slow start
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Pattern 2: Defesa em camadas (cap 22)
|
|
69
|
+
|
|
70
|
+
Cada chamada externa precisa de **5 camadas de defesa**:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// Camada 1: Timeout agressivo
|
|
74
|
+
const TIMEOUT_MS = 2000 // p99.9 baseline + 50%
|
|
75
|
+
|
|
76
|
+
// Camada 2: Retry com jitter + deadline
|
|
77
|
+
async function callDep(input: Input, deadline: number): Promise<Output> {
|
|
78
|
+
const startMs = performance.now()
|
|
79
|
+
let attempt = 0
|
|
80
|
+
let lastError: Error | undefined
|
|
81
|
+
|
|
82
|
+
while (true) {
|
|
83
|
+
const remaining = deadline - performance.now()
|
|
84
|
+
if (remaining <= 0) throw new DeadlineExceededError(lastError)
|
|
85
|
+
|
|
86
|
+
const callTimeoutMs = Math.min(TIMEOUT_MS, remaining)
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Camada 3: Circuit breaker
|
|
90
|
+
if (circuitBreaker.isOpen()) throw new CircuitOpenError()
|
|
91
|
+
|
|
92
|
+
const result = await withTimeout(
|
|
93
|
+
depClient.call(input),
|
|
94
|
+
callTimeoutMs
|
|
95
|
+
)
|
|
96
|
+
circuitBreaker.recordSuccess()
|
|
97
|
+
return result
|
|
98
|
+
} catch (e) {
|
|
99
|
+
lastError = e as Error
|
|
100
|
+
circuitBreaker.recordFailure()
|
|
101
|
+
|
|
102
|
+
// Camada 4: Não retentar erros não-retentáveis
|
|
103
|
+
if (!isRetryable(e)) throw e
|
|
104
|
+
|
|
105
|
+
// Camada 5: Retry budget global
|
|
106
|
+
if (!retryBudget.tryAcquire()) throw new RetryBudgetExhaustedError(e)
|
|
107
|
+
|
|
108
|
+
attempt++
|
|
109
|
+
if (attempt >= MAX_RETRIES) throw e
|
|
110
|
+
|
|
111
|
+
// Full jitter
|
|
112
|
+
const baseMs = 100 * Math.pow(2, attempt - 1)
|
|
113
|
+
const jitterMs = Math.random() * baseMs * 2
|
|
114
|
+
await sleep(Math.min(jitterMs, remaining))
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isRetryable(e: any): boolean {
|
|
120
|
+
// 4xx (validation, auth, not_found) → não retry
|
|
121
|
+
// 5xx, timeout, connection reset → retry
|
|
122
|
+
if (e.statusCode >= 400 && e.statusCode < 500) return false
|
|
123
|
+
if (e.statusCode === 429) return true // rate limited — retry com backoff
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Pattern 3: Circuit breaker — estados canônicos
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
┌────────────────────┐
|
|
132
|
+
│ CLOSED (normal) │
|
|
133
|
+
│ request flows OK │
|
|
134
|
+
└─────────┬──────────┘
|
|
135
|
+
│ N consecutive failures
|
|
136
|
+
▼
|
|
137
|
+
┌────────────────────┐
|
|
138
|
+
│ OPEN │
|
|
139
|
+
│ fail fast for T │
|
|
140
|
+
│ no calls to dep │
|
|
141
|
+
└─────────┬──────────┘
|
|
142
|
+
│ T elapsed
|
|
143
|
+
▼
|
|
144
|
+
┌────────────────────┐
|
|
145
|
+
│ HALF-OPEN │
|
|
146
|
+
│ allow 1-N probes │
|
|
147
|
+
└────┬───────────┬───┘
|
|
148
|
+
success │ │ failure
|
|
149
|
+
▼ ▼
|
|
150
|
+
CLOSED OPEN
|
|
151
|
+
|
|
152
|
+
Configuração canônica:
|
|
153
|
+
N (failures-to-open): 5-10 consecutive
|
|
154
|
+
T (open duration): 30-60s
|
|
155
|
+
half-open probe count: 1-5
|
|
156
|
+
failure detection window: 30-60s rolling
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Pattern 4: Deadline propagation across hops
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
Client → Service A (deadline=30s) → Service B (deadline=?) → Service C (deadline=?)
|
|
163
|
+
|
|
164
|
+
WRONG (cascade amplification):
|
|
165
|
+
A: receives deadline=30s, calls B with timeout=30s
|
|
166
|
+
B: receives no deadline, default 30s, calls C with timeout=30s
|
|
167
|
+
C: takes 30s
|
|
168
|
+
Total: 30s in C, plus parent overhead. Client gone at 30s, A-B-C still working.
|
|
169
|
+
|
|
170
|
+
RIGHT (deadline propagation):
|
|
171
|
+
A: receives TTL=30s; takes 100ms processing; calls B with TTL=29.9s
|
|
172
|
+
B: receives TTL=29.9s; takes 50ms; calls C with TTL=29.85s
|
|
173
|
+
C: receives TTL=29.85s; works until that limit, no more
|
|
174
|
+
When TTL hits 0, ALL hops fail fast. No zombie work.
|
|
175
|
+
|
|
176
|
+
Implementação:
|
|
177
|
+
- HTTP: header `Deadline-Ms` ou similar (custom)
|
|
178
|
+
- gRPC: built-in `grpc-timeout` header
|
|
179
|
+
- JS/TS: AbortSignal.timeout(remainingMs)
|
|
180
|
+
- Each hop: deadline = received_deadline - elapsed_local; abort se ≤ 0
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Pattern 5: Defesas server-side
|
|
184
|
+
|
|
185
|
+
Server protege a si próprio (não confia em clientes):
|
|
186
|
+
|
|
187
|
+
| Defesa | Técnica | Exemplo |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| **Rate limit per-client** | Token bucket no proxy/gateway | Kong/Envoy rate limit; 100 req/s/client |
|
|
190
|
+
| **Concurrency limit** | Semaphore no handler | Máx 1000 in-flight; reject 503 if cheio |
|
|
191
|
+
| **Queue depth limit** | Bound + drop policy | Queue máx 10k msgs; drop oldest > 5k |
|
|
192
|
+
| **Resource budget** | Cgroups / container limits | CPU 4 cores, memory 8GB hard cap |
|
|
193
|
+
| **Slow start na recovery** | Gradual ramp | Aceita 10% → 25% → 50% → 100% por 5 min |
|
|
194
|
+
|
|
195
|
+
### Pattern 6: Testing for cascading failures
|
|
196
|
+
|
|
197
|
+
Antes de prod, exercitar cascade scenarios:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
1. Game day exercise
|
|
201
|
+
- Cap 1 dep crítica em 50% errors via fault injection
|
|
202
|
+
- Observar: caller circuit breakers abrem? Latency caller estável?
|
|
203
|
+
- Métrica: zero impacto a clientes (degraded mode kicks in)
|
|
204
|
+
|
|
205
|
+
2. Load test até saturação
|
|
206
|
+
- Ramp traffic até 1.5× expected peak
|
|
207
|
+
- Confirmar: load shedding ativa antes de crash
|
|
208
|
+
- Métrica: error rate sob 1% mesmo em 1.5× load
|
|
209
|
+
|
|
210
|
+
3. Chaos engineering
|
|
211
|
+
- Random kill de instâncias (Chaos Monkey)
|
|
212
|
+
- Confirmar: retries com jitter espalham wake-up
|
|
213
|
+
- Métrica: SLO mantido durante 10 kills/hour
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Anti-patterns
|
|
217
|
+
|
|
218
|
+
### ANTI: timeout otimista
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
ANTI: timeout = p99 baseline. "Maioria das requests fica abaixo".
|
|
222
|
+
|
|
223
|
+
PROBLEMA: tail latency (1%) sempre estoura. Cada 100 requests, 1
|
|
224
|
+
consome timeout inteiro. Conn pool acumula slow requests.
|
|
225
|
+
|
|
226
|
+
CERTO: timeout = p99.9 baseline + 50% margem. Aceita que 0.1% será
|
|
227
|
+
cancelado. Tail é problema de dep, não cliente.
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### ANTI: retry infinito com backoff "razoável"
|
|
231
|
+
|
|
232
|
+
```text
|
|
233
|
+
ANTI: retry até sucesso, com 1s/2s/4s/8s/... exponencial sem cap.
|
|
234
|
+
|
|
235
|
+
PROBLEMA: durante outage de 30 min, último retry seria após 30 min
|
|
236
|
+
esperando. Conn fica pendurada. Memory leak.
|
|
237
|
+
|
|
238
|
+
CERTO: max retries = 3-5; max backoff = 30s; deadline global
|
|
239
|
+
(request terminada após T segundos não importa quantos
|
|
240
|
+
retries). Após max, fallback ou error final.
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### ANTI: circuit breaker per-instance (não compartilhado)
|
|
244
|
+
|
|
245
|
+
```text
|
|
246
|
+
ANTI: cada instância de service A tem seu próprio circuit breaker
|
|
247
|
+
pra dep B. Quando B fica lenta, instância 1 abre circuito,
|
|
248
|
+
mas instâncias 2-100 ainda pingam B até abrirem.
|
|
249
|
+
|
|
250
|
+
PROBLEMA: 100× mais carga em B doente. B nunca recupera.
|
|
251
|
+
|
|
252
|
+
CERTO: circuit breaker compartilhado (e.g., via Redis/Memcached para
|
|
253
|
+
state) OR lib que detecta failure rate cross-instance via
|
|
254
|
+
gossip/coordination. Open = cluster all stop.
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### ANTI: load shedding via reject ao invés de 503 + Retry-After
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
ANTI: server saturated → drop conn TCP-level (RST).
|
|
261
|
+
|
|
262
|
+
PROBLEMA: client não sabe que precisa esperar. Retry imediato.
|
|
263
|
+
Mais carga. Retry storm.
|
|
264
|
+
|
|
265
|
+
CERTO: 503 Service Unavailable + Retry-After: 30 (segundos).
|
|
266
|
+
Client aware retry com delay. Backoff respeitado.
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### ANTI: graceful degradation só em prod
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
ANTI: degraded mode só liga durante incident; nunca exercitado.
|
|
273
|
+
|
|
274
|
+
PROBLEMA: quando precisa, descobre bug no degraded mode. Outage
|
|
275
|
+
piora.
|
|
276
|
+
|
|
277
|
+
CERTO: degraded mode é PATH PRINCIPAL em alguma fração de tráfego
|
|
278
|
+
(1%) por padrão. Sempre exercitado. Quando dep cai, ramp pra
|
|
279
|
+
100% degraded é tested transition.
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Verificação
|
|
283
|
+
|
|
284
|
+
Antes de aceitar serviço em prod:
|
|
285
|
+
|
|
286
|
+
1. Toda chamada a dep externa tem timeout < p99.9 baseline + 50%
|
|
287
|
+
2. Toda retry tem jitter (full jitter default)
|
|
288
|
+
3. Toda chamada respeita deadline propagation
|
|
289
|
+
4. Circuit breaker ativo em deps críticas (estados closed/open/half-open)
|
|
290
|
+
5. Server-side: rate limit + concurrency limit + queue depth bound
|
|
291
|
+
6. Slow start configurado em deploy/recovery
|
|
292
|
+
7. Game day exercise rodado nos últimos 90 dias
|
|
293
|
+
8. Saturation gauge (cap 6) instrumentado e alertado em 80%
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Ver também
|
|
298
|
+
|
|
299
|
+
- [`_shared-sre/glossary.md`](../_shared-sre/glossary.md) — vocabulário cap 22 (cascading failure, retry storm, etc.)
|
|
300
|
+
- [`retry-strategies`](../retry-strategies/SKILL.md) (v1.11) — detalhes de jitter + deadline propagation
|
|
301
|
+
- [`load-shedding-graceful-degradation`](../load-shedding-graceful-degradation/SKILL.md) (v1.11) — defesas server-side
|
|
302
|
+
- [`four-golden-signals`](../four-golden-signals/SKILL.md) (v1.10) — Saturation como early warning de cascade
|
|
303
|
+
- [`production-readiness-review`](../production-readiness-review/SKILL.md) (v1.10) — PRR Axe 4 verifica defesas
|
|
304
|
+
- [`omm-auditor`](../../agents/omm-auditor.md) (v1.9) — Capacidade 1 (Resilience) consume cascading-failures-auditor
|
|
305
|
+
- [`cascading-failures-auditor`](../../agents/cascading-failures-auditor.md) (v1.11) — agent que detecta gaps automaticamente
|
|
306
|
+
|
|
307
|
+
*Material-fonte: Site Reliability Engineering — Beyer/Jones/Petoff/Murphy (Google/O'Reilly, 2016) — Cap 22: "Addressing Cascading Failures".*
|
|
@@ -283,6 +283,23 @@ Antes de marcar instrumentação como production-ready, validar:
|
|
|
283
283
|
6. **Black-box probe complementar** — synthetic check do happy path principal a cada 30s
|
|
284
284
|
7. **Dashboard de 4 signals** existe e é o **primeiro** lugar de debug em incident
|
|
285
285
|
|
|
286
|
+
## Saturation as cascading failure trigger (v1.11)
|
|
287
|
+
|
|
288
|
+
**Saturation > threshold é early warning de cascading failure** (cap 22). Quando ainda há tempo para load shedding manter SLO; quando hit 100%, já está em cascade. Threshold tuning canônico:
|
|
289
|
+
|
|
290
|
+
| Recurso | Warning | Critical | Ação automática |
|
|
291
|
+
|---|---|---|---|
|
|
292
|
+
| **CPU load** | > 70% | > 90% | Load shed; scale up |
|
|
293
|
+
| **Memory used** | > 80% | > 95% | Load shed; OOM protection |
|
|
294
|
+
| **Queue depth (pgmq)** | > 70% capacity | > 90% capacity | Drop oldest; scale consumers |
|
|
295
|
+
| **Connection pool (DB)** | > 70% used | > 90% used | Throttle slow queries; scale pool |
|
|
296
|
+
| **Concurrency limit** | > 80% inflight | > 95% inflight | Reject new (503 + Retry-After) |
|
|
297
|
+
| **File descriptors** | > 70% ulimit | > 90% ulimit | Close idle conns; scale up |
|
|
298
|
+
|
|
299
|
+
**Resposta canônica:** quando saturation > Critical, server-side ativa load shedding (skill `load-shedding-graceful-degradation` v1.11) — retorna 503 + Retry-After ANTES de aceitar request que vai falhar. Coopera com caller-side defesas (skill `retry-strategies` v1.11 — full jitter respeita Retry-After).
|
|
300
|
+
|
|
301
|
+
Cross-ref: `cascading-failures` (v1.11) detalha 5 triggers; `cascading-failures-auditor` (v1.11) detecta gaps em código.
|
|
302
|
+
|
|
286
303
|
## Ver também
|
|
287
304
|
|
|
288
305
|
- [`_shared-sre/glossary.md`](../_shared-sre/glossary.md) — termos canônicos golden signals, black-box, white-box, percentile
|