@luanpdd/kit-mcp 1.9.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 (84) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +58 -0
  3. package/gates/ai-prompt-stability.md +120 -0
  4. package/gates/golden-signals-coverage.md +133 -0
  5. package/gates/legacy-refactor-safety.md +178 -0
  6. package/gates/observability-coverage.md +151 -0
  7. package/gates/postmortem-template-required.md +127 -0
  8. package/gates/prr-checklist-coverage.md +128 -0
  9. package/gates/release-pipeline-policy.md +132 -0
  10. package/kit/COMANDOS.md +15 -0
  11. package/kit/agents/ai-mutation-tester.md +298 -0
  12. package/kit/agents/cascading-failures-auditor.md +306 -0
  13. package/kit/agents/executor.md +13 -0
  14. package/kit/agents/golden-signals-instrumenter.md +241 -0
  15. package/kit/agents/legacy-characterizer.md +378 -0
  16. package/kit/agents/load-shedding-instrumenter.md +297 -0
  17. package/kit/agents/observability-coverage-auditor.md +325 -0
  18. package/kit/agents/omm-auditor.md +99 -0
  19. package/kit/agents/payload-capture-instrumenter.md +283 -0
  20. package/kit/agents/planner.md +29 -0
  21. package/kit/agents/postmortem-writer.md +282 -0
  22. package/kit/agents/prr-conductor.md +296 -0
  23. package/kit/agents/refactor-safety-auditor.md +414 -0
  24. package/kit/agents/release-pipeline-auditor.md +360 -0
  25. package/kit/agents/seam-finder.md +367 -0
  26. package/kit/agents/shotgun-surgery-detector.md +359 -0
  27. package/kit/agents/storytelling-analyst.md +309 -0
  28. package/kit/agents/supabase-architect.md +49 -0
  29. package/kit/agents/supabase-edge-fn-writer.md +114 -0
  30. package/kit/agents/supabase-migration-writer.md +80 -0
  31. package/kit/agents/supabase-storage-implementer.md +156 -0
  32. package/kit/agents/toil-auditor.md +277 -0
  33. package/kit/agents/verifier.md +30 -0
  34. package/kit/commands/auditar-cascading.md +111 -0
  35. package/kit/commands/auditar-marco.md +124 -1
  36. package/kit/commands/auditar-observabilidade-cobertura.md +183 -0
  37. package/kit/commands/auditar-refactor.md +219 -0
  38. package/kit/commands/auditar-release.md +109 -0
  39. package/kit/commands/auditar-toil.md +129 -0
  40. package/kit/commands/capturar-payloads.md +193 -0
  41. package/kit/commands/caracterizar-prompt.md +195 -0
  42. package/kit/commands/caracterizar.md +212 -0
  43. package/kit/commands/concluir-marco.md +95 -1
  44. package/kit/commands/detectar-duplicacao.md +197 -0
  45. package/kit/commands/discutir-fase.md +41 -0
  46. package/kit/commands/encontrar-seams.md +136 -0
  47. package/kit/commands/forense.md +103 -1
  48. package/kit/commands/golden-signals.md +142 -0
  49. package/kit/commands/legacy.md +263 -0
  50. package/kit/commands/load-shedding.md +117 -0
  51. package/kit/commands/observabilidade.md +2 -0
  52. package/kit/commands/postmortem.md +179 -0
  53. package/kit/commands/prr.md +205 -0
  54. package/kit/commands/refactor-seguro.md +321 -0
  55. package/kit/commands/risk-budget.md +220 -0
  56. package/kit/commands/sre.md +230 -0
  57. package/kit/commands/storytelling.md +179 -0
  58. package/kit/skills/_shared-legacy/glossary.md +389 -0
  59. package/kit/skills/_shared-sre/glossary.md +712 -0
  60. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -0
  61. package/kit/skills/blameless-postmortems/SKILL.md +340 -0
  62. package/kit/skills/cascading-failures/SKILL.md +307 -0
  63. package/kit/skills/eliminating-toil/SKILL.md +243 -0
  64. package/kit/skills/event-based-slos/SKILL.md +22 -0
  65. package/kit/skills/four-golden-signals/SKILL.md +314 -0
  66. package/kit/skills/hermetic-builds/SKILL.md +323 -0
  67. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -0
  68. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -0
  69. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -0
  70. package/kit/skills/legacy-extract-class/SKILL.md +203 -0
  71. package/kit/skills/legacy-monster-methods/SKILL.md +444 -0
  72. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -0
  73. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -0
  74. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -0
  75. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -0
  76. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -0
  77. package/kit/skills/llm-as-dependency/SKILL.md +436 -0
  78. package/kit/skills/load-shedding-graceful-degradation/SKILL.md +396 -0
  79. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -0
  80. package/kit/skills/production-readiness-review/SKILL.md +305 -0
  81. package/kit/skills/release-engineering/SKILL.md +367 -0
  82. package/kit/skills/retry-strategies/SKILL.md +372 -0
  83. package/kit/skills/sre-risk-management/SKILL.md +221 -0
  84. package/package.json +2 -2
@@ -0,0 +1,436 @@
1
+ ---
2
+ name: llm-as-dependency
3
+ description: Use ao escrever código que depende de LLM (OpenAI/Anthropic) — adapter pattern + FakeLLMProvider para testes determinísticos sem custo. Modernização 2026 sem precedente em 2004.
4
+ ---
5
+
6
+ # LLM as Dependency (Modernização)
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando user vai escrever ou refatorar código que chama LLM provider em produção. Trigger phrases:
11
+
12
+ - "como testar essa função que chama OpenAI?"
13
+ - "fake do client Anthropic", "mock LLM"
14
+ - "tornar testável código com LLM"
15
+ - "deterministic test mode para LLM"
16
+ - "DI de OpenAI/Anthropic client"
17
+ - "edge function usa LLM, como caracterizar?"
18
+ - "function calling em testes"
19
+
20
+ ## Regras absolutas
21
+
22
+ - **LLM é DEPENDÊNCIA EXTERNA igual a DB ou HTTP API.** Mesmas regras: nunca acoplar handler ao SDK específico; sempre via interface; sempre com fake disponível.
23
+ - **Adapter pattern (do skill `legacy-api-only-applications`) é a aplicação correta.** Interface mínima `LLMProvider`; adapter por vendor; fake por testes.
24
+ - **Testes de BUSINESS LOGIC nunca chamam LLM real.** FakeLLMProvider canned responses. LLM real é exclusivamente em characterization tests de PROMPT (skill `ai-prompt-characterization`).
25
+ - **Determinismo via fake.** FakeLLMProvider retorna outputs fixos em ordem. Não há non-determinism em test de business logic.
26
+ - **Token tracking é cross-cutting concern do adapter.** Handler não precisa saber tokens. Adapter loga + opcionalmente reporta para observability.
27
+ - **Tradução de erros: vendor → domain.** Rate limit do OpenAI ≠ Rate limit do Anthropic ≠ Rate limit do Claude API. Adapter traduz para enum próprio (`LLMError = 'rate_limit' | 'timeout' | 'context_too_long' | 'content_filter' | 'auth' | 'unknown'`).
28
+ - **Fake suporta múltiplos modos:** (a) canned responses, (b) function-based responses (`(input) => output`), (c) error injection (`shouldFail: 'rate_limit'`).
29
+
30
+ ## Patterns canônicos
31
+
32
+ ### Pattern 1: Interface canônica `LLMProvider`
33
+
34
+ ```ts
35
+ // Interface mínima — handler não vê SDK específico
36
+ interface LLMProvider {
37
+ generate(input: GenerateInput): Promise<GenerateResult>
38
+ embed?(input: EmbedInput): Promise<EmbedResult> // opcional, só para providers que suportam
39
+ }
40
+
41
+ interface GenerateInput {
42
+ prompt: string // ou messages, dependendo do provider
43
+ systemPrompt?: string
44
+ maxTokens: number
45
+ temperature?: number
46
+ seed?: number // determinismo
47
+ tools?: ToolDefinition[]
48
+ toolChoice?: 'auto' | 'none' | { type: 'tool'; name: string }
49
+ }
50
+
51
+ interface GenerateResult {
52
+ text: string
53
+ finishReason: 'stop' | 'length' | 'tool_use' | 'content_filter'
54
+ toolUses: Array<{ name: string; input: any }>
55
+ inputTokens: number
56
+ outputTokens: number
57
+ modelVersion: string
58
+ }
59
+
60
+ interface EmbedInput {
61
+ texts: string[]
62
+ model?: string // canônico: 'text-embedding-3-small' default; provider mapeia
63
+ }
64
+
65
+ interface EmbedResult {
66
+ embeddings: number[][] // 1 vector por input
67
+ inputTokens: number
68
+ }
69
+
70
+ // LLM error canônico (anti-corruption layer)
71
+ type LLMError = 'rate_limit' | 'timeout' | 'context_too_long' | 'content_filter' | 'auth' | 'invalid_request' | 'server_error' | 'unknown'
72
+ class LLMException extends Error {
73
+ constructor(public code: LLMError, message: string, public retryable: boolean) {
74
+ super(message)
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Pattern 2: Adapters por vendor
80
+
81
+ ```ts
82
+ class OpenAIAdapter implements LLMProvider {
83
+ constructor(private client: OpenAI) {}
84
+
85
+ async generate(input: GenerateInput): Promise<GenerateResult> {
86
+ try {
87
+ const r = await this.client.chat.completions.create({
88
+ model: 'gpt-4',
89
+ messages: [
90
+ ...(input.systemPrompt ? [{ role: 'system' as const, content: input.systemPrompt }] : []),
91
+ { role: 'user' as const, content: input.prompt },
92
+ ],
93
+ max_tokens: input.maxTokens,
94
+ temperature: input.temperature ?? 0,
95
+ seed: input.seed,
96
+ tools: input.tools?.map(t => ({ type: 'function' as const, function: t })),
97
+ })
98
+ return this.toDomain(r)
99
+ } catch (e: any) {
100
+ throw this.translateError(e)
101
+ }
102
+ }
103
+
104
+ private toDomain(r: any): GenerateResult { /* ... */ }
105
+ private translateError(e: any): LLMException {
106
+ if (e.status === 429) return new LLMException('rate_limit', e.message, true)
107
+ if (e.status === 401) return new LLMException('auth', e.message, false)
108
+ if (e.code === 'context_length_exceeded') return new LLMException('context_too_long', e.message, false)
109
+ if (e.code === 'content_filter') return new LLMException('content_filter', e.message, false)
110
+ return new LLMException('unknown', e.message, false)
111
+ }
112
+ }
113
+
114
+ class AnthropicAdapter implements LLMProvider {
115
+ constructor(private client: Anthropic) {}
116
+
117
+ async generate(input: GenerateInput): Promise<GenerateResult> {
118
+ try {
119
+ const r = await this.client.messages.create({
120
+ model: 'claude-opus-4-7',
121
+ max_tokens: input.maxTokens,
122
+ temperature: input.temperature ?? 0,
123
+ system: input.systemPrompt,
124
+ messages: [{ role: 'user' as const, content: input.prompt }],
125
+ tools: input.tools,
126
+ })
127
+ return this.toDomain(r)
128
+ } catch (e: any) {
129
+ throw this.translateError(e)
130
+ }
131
+ }
132
+
133
+ private toDomain(r: any): GenerateResult { /* ... */ }
134
+ private translateError(e: any): LLMException {
135
+ if (e.status === 429) return new LLMException('rate_limit', e.message, true)
136
+ if (e.status === 401) return new LLMException('auth', e.message, false)
137
+ return new LLMException('unknown', e.message, false)
138
+ }
139
+ }
140
+ ```
141
+
142
+ ### Pattern 3: FakeLLMProvider canônico para testes
143
+
144
+ ```ts
145
+ class FakeLLMProvider implements LLMProvider {
146
+ private responses: GenerateResult[] = []
147
+ private index = 0
148
+ private errorMode: LLMError | null = null
149
+ private callLog: GenerateInput[] = []
150
+
151
+ // Configuração
152
+ setResponses(r: Array<Partial<GenerateResult>>): void {
153
+ this.responses = r.map(p => ({
154
+ text: p.text ?? '',
155
+ finishReason: p.finishReason ?? 'stop',
156
+ toolUses: p.toolUses ?? [],
157
+ inputTokens: p.inputTokens ?? 100,
158
+ outputTokens: p.outputTokens ?? 50,
159
+ modelVersion: p.modelVersion ?? 'fake-model-1',
160
+ }))
161
+ this.index = 0
162
+ }
163
+
164
+ failNextWith(code: LLMError): void {
165
+ this.errorMode = code
166
+ }
167
+
168
+ // Inspeção (assertions)
169
+ callsLog(): GenerateInput[] { return [...this.callLog] }
170
+ callCount(): number { return this.callLog.length }
171
+
172
+ // LLMProvider interface
173
+ async generate(input: GenerateInput): Promise<GenerateResult> {
174
+ this.callLog.push(input)
175
+
176
+ if (this.errorMode) {
177
+ const code = this.errorMode
178
+ this.errorMode = null
179
+ throw new LLMException(code, `fake error: ${code}`, code === 'rate_limit' || code === 'timeout')
180
+ }
181
+
182
+ if (this.index < this.responses.length) {
183
+ return this.responses[this.index++]
184
+ }
185
+ return {
186
+ text: 'fake default response',
187
+ finishReason: 'stop',
188
+ toolUses: [],
189
+ inputTokens: input.prompt.length / 4, // rough estimate
190
+ outputTokens: 50,
191
+ modelVersion: 'fake-model-1',
192
+ }
193
+ }
194
+ }
195
+
196
+ // Uso em test
197
+ test('processOrder summary — handles LLM rate limit gracefully', async () => {
198
+ const llm = new FakeLLMProvider()
199
+ llm.failNextWith('rate_limit') // primeiro call falha
200
+ llm.setResponses([{ text: 'Successful summary' }]) // depois funciona
201
+
202
+ const handler = new OrderSummarizer(llm)
203
+ const result = await handler.summarizeOrder({ id: 'O-1', items: [...] })
204
+
205
+ expect(result.summary).toBe('Successful summary')
206
+ expect(llm.callCount()).toBe(2) // 1 falhou + 1 retry sucesso
207
+ })
208
+ ```
209
+
210
+ ### Pattern 4: Function-based fake (mais flexível)
211
+
212
+ ```ts
213
+ // Para tests onde fake response depende do input
214
+ class FunctionalFakeLLM implements LLMProvider {
215
+ constructor(
216
+ private generateFn: (input: GenerateInput) => Promise<GenerateResult> | GenerateResult,
217
+ ) {}
218
+ async generate(input: GenerateInput): Promise<GenerateResult> {
219
+ return this.generateFn(input)
220
+ }
221
+ }
222
+
223
+ // Uso
224
+ test('summarizer — handles long input via chunking', async () => {
225
+ const llm = new FunctionalFakeLLM(async (input) => {
226
+ // Simular comportamento real: prompt > 8000 chars retorna context_too_long
227
+ if (input.prompt.length > 8000) {
228
+ throw new LLMException('context_too_long', 'too long', false)
229
+ }
230
+ return {
231
+ text: `Summary of ${input.prompt.length} chars`,
232
+ finishReason: 'stop',
233
+ toolUses: [],
234
+ inputTokens: input.prompt.length / 4,
235
+ outputTokens: 30,
236
+ modelVersion: 'fake',
237
+ }
238
+ })
239
+
240
+ const handler = new OrderSummarizer(llm)
241
+ const longOrder = generateLongOrder(15000)
242
+ const result = await handler.summarizeOrder(longOrder)
243
+ expect(result.chunks).toBe(2) // verificou que chunking foi acionado
244
+ })
245
+ ```
246
+
247
+ ### Pattern 5: Adapter para Edge Function (Supabase + Deno)
248
+
249
+ ```ts
250
+ // supabase/functions/_shared/llm.ts
251
+ import { Anthropic } from 'npm:@anthropic-ai/sdk@0.30.0'
252
+
253
+ export interface LLMProvider {
254
+ generate(input: GenerateInput): Promise<GenerateResult>
255
+ }
256
+
257
+ export class AnthropicAdapter implements LLMProvider {
258
+ constructor(private client: Anthropic) {}
259
+ async generate(input: GenerateInput): Promise<GenerateResult> { /* ... */ }
260
+ }
261
+
262
+ export function createLLMProvider(): LLMProvider {
263
+ const apiKey = Deno.env.get('ANTHROPIC_API_KEY')
264
+ if (!apiKey) throw new Error('ANTHROPIC_API_KEY missing')
265
+ return new AnthropicAdapter(new Anthropic({ apiKey }))
266
+ }
267
+
268
+ // supabase/functions/summarize-order/index.ts
269
+ import { createLLMProvider } from '../_shared/llm.ts'
270
+
271
+ const llm = createLLMProvider()
272
+
273
+ Deno.serve(async (req) => {
274
+ return await handleRequest(req, llm)
275
+ })
276
+
277
+ export async function handleRequest(req: Request, llm: LLMProvider): Promise<Response> {
278
+ const order = await req.json()
279
+ const summary = await llm.generate({
280
+ prompt: `Resuma este pedido: ${JSON.stringify(order)}`,
281
+ maxTokens: 200,
282
+ })
283
+ return new Response(JSON.stringify({ summary: summary.text }))
284
+ }
285
+
286
+ // tests/handle-request.test.ts
287
+ import { handleRequest } from '../supabase/functions/summarize-order/index.ts'
288
+ import { FakeLLMProvider } from './fakes.ts'
289
+
290
+ test('summarize-order — typical request', async () => {
291
+ const llm = new FakeLLMProvider()
292
+ llm.setResponses([{ text: 'Pedido de R$ 50, 2 items.' }])
293
+
294
+ const req = new Request('http://x', {
295
+ method: 'POST',
296
+ body: JSON.stringify({ id: 'O-1', total: 5000, items: ['SKU-1', 'SKU-2'] }),
297
+ })
298
+ const res = await handleRequest(req, llm)
299
+ const body = await res.json()
300
+ expect(body.summary).toBe('Pedido de R$ 50, 2 items.')
301
+ })
302
+ ```
303
+
304
+ ### Pattern 6: Cross-cutting concerns no adapter
305
+
306
+ Adapter é o lugar canônico para retry, timeout, observability:
307
+
308
+ ```ts
309
+ class ResilientLLMAdapter implements LLMProvider {
310
+ constructor(
311
+ private inner: LLMProvider,
312
+ private logger: Logger,
313
+ private metrics: Metrics,
314
+ ) {}
315
+
316
+ async generate(input: GenerateInput): Promise<GenerateResult> {
317
+ const startMs = performance.now()
318
+ try {
319
+ const result = await retryWithJitter(
320
+ () => this.inner.generate(input),
321
+ { maxRetries: 3, baseMs: 500, retryOn: (e) => e instanceof LLMException && e.retryable }
322
+ )
323
+
324
+ const latency = performance.now() - startMs
325
+ this.metrics.histogram('llm.generate.latency_ms', latency, { result: 'success' })
326
+ this.metrics.counter('llm.generate.tokens', result.inputTokens + result.outputTokens)
327
+ this.logger.info('llm.generate.ok', { latency_ms: latency, model: result.modelVersion })
328
+ return result
329
+ } catch (e) {
330
+ const latency = performance.now() - startMs
331
+ this.metrics.histogram('llm.generate.latency_ms', latency, { result: 'error' })
332
+ this.metrics.counter('llm.errors', 1, { error_type: (e as LLMException).code })
333
+ this.logger.warn('llm.generate.failed', { error: (e as Error).message })
334
+ throw e
335
+ }
336
+ }
337
+ }
338
+
339
+ // Composição em produção
340
+ const llm = new ResilientLLMAdapter(
341
+ new AnthropicAdapter(new Anthropic({ apiKey })),
342
+ logger,
343
+ metrics,
344
+ )
345
+ ```
346
+
347
+ ## Anti-patterns
348
+
349
+ ### ANTI: handler chama OpenAI direto
350
+
351
+ ```text
352
+ ANTI: handler.ts: import OpenAI from 'openai'; const client = new OpenAI(...);
353
+ ... await client.chat.completions.create(...)
354
+
355
+ PROBLEMA: handler intestável sem mock global. Trocar Anthropic =
356
+ rewrite. Tests rodam contra API real (lento + custo +
357
+ flaky).
358
+
359
+ CERTO: handler depende de LLMProvider. Adapter encapsula. Tests
360
+ usam FakeLLMProvider.
361
+ ```
362
+
363
+ ### ANTI: tests rodam contra LLM real
364
+
365
+ ```text
366
+ ANTI: tests de business logic chamam OpenAI/Anthropic real.
367
+
368
+ PROBLEMA: testes lentos (5s por call), custosos ($X por suite),
369
+ flaky (rate limits, network), não-determinísticos
370
+ (mesma input pode gerar texto diferente entre runs).
371
+
372
+ CERTO: business logic tests usam FakeLLMProvider. LLM REAL apenas
373
+ em tests específicos de characterization de PROMPT (skill
374
+ `ai-prompt-characterization`). 99% dos tests = fakes.
375
+ ```
376
+
377
+ ### ANTI: fake retorna sempre mesma resposta
378
+
379
+ ```text
380
+ ANTI: FakeLLM hardcoded retorna `text: "fake response"` sempre.
381
+
382
+ PROBLEMA: tests não conseguem simular caminhos múltiplos
383
+ (sucesso/falha/edge case). Cobertura baixa.
384
+
385
+ CERTO: FakeLLM com setResponses() OR FunctionalFakeLLM. Cada teste
386
+ configura comportamento esperado para aquele caso.
387
+ ```
388
+
389
+ ### ANTI: adapter expõe tipos do vendor
390
+
391
+ ```text
392
+ ANTI: interface LLMProvider { generate(input): Promise<OpenAI.Chat.Completion> }
393
+
394
+ PROBLEMA: OpenAI.Chat.Completion atravessa camadas internas.
395
+ Mudança no SDK do OpenAI cascateia.
396
+
397
+ CERTO: GenerateResult é tipo PRÓPRIO do domínio. Adapter traduz
398
+ OpenAI.Chat.Completion → GenerateResult. Anti-corruption
399
+ layer.
400
+ ```
401
+
402
+ ### ANTI: tratar rate_limit como `any error`
403
+
404
+ ```text
405
+ ANTI: catch (e) { if (e.status === 429) retry; else throw }
406
+
407
+ PROBLEMA: lógica de erro acoplada à HTTP status do vendor. Outro
408
+ provider tem outros códigos. Lógica duplicada.
409
+
410
+ CERTO: adapter traduz error para LLMException com código domain.
411
+ Handler trata LLMException.code, não status HTTP.
412
+ ```
413
+
414
+ ## Verificação
415
+
416
+ 1. Handler depende de `LLMProvider` interface
417
+ 2. Adapter por vendor encapsula SDK
418
+ 3. FakeLLMProvider existe e tests usam
419
+ 4. Erros traduzidos para `LLMException` com código domain
420
+ 5. ResilientLLMAdapter (com retry + observability) usado em produção
421
+ 6. Tests de business logic NUNCA chamam LLM real (CI custa $0)
422
+ 7. Trocar provider = trocar 1 linha (constructor)
423
+
424
+ ---
425
+
426
+ ## Ver também
427
+
428
+ - [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário (adapter, anti-corruption)
429
+ - [`legacy-api-only-applications`](../legacy-api-only-applications/SKILL.md) — pattern canônico (LLM provider é caso especial)
430
+ - [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — extract-interface é técnica do cap 25
431
+ - [`ai-prompt-characterization`](../ai-prompt-characterization/SKILL.md) — characterization de PROMPT (LLM real); essa skill é para tudo MAIS LLM (LLM fake)
432
+ - [`supabase-edge-fn-writer`](../../agents/supabase-edge-fn-writer.md) (v1.8) — patch v1.12: detecta uso de LLM e oferece DI pattern
433
+ - [`four-golden-signals`](../four-golden-signals/SKILL.md) (v1.10) — adapter é onde golden signals são instrumentados
434
+ - [`retry-strategies`](../retry-strategies/SKILL.md) (v1.11) — retry com jitter aplicado em ResilientLLMAdapter
435
+
436
+ *Material-fonte (modernização 2026):* Sem precedente em livro Feathers 2004 — LLMs como dependência de produção é literatura recente (2023+).