@ronaldjdevfs/forge 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +36 -21
  2. package/package.json +7 -2
  3. package/skills/forge/SKILL.md +56 -122
  4. package/skills/forge/command/forge.md +59 -14
  5. package/skills/forge/reference/adr.md +242 -0
  6. package/skills/forge/reference/anti-corruption-layer.md +340 -0
  7. package/skills/forge/reference/api-design.md +7 -0
  8. package/skills/forge/reference/api-versioning.md +354 -0
  9. package/skills/forge/reference/architectural-depth-checklist.md +311 -0
  10. package/skills/forge/reference/architecture-template.md +41 -0
  11. package/skills/forge/reference/assay.md +6 -0
  12. package/skills/forge/reference/bounded-contexts.md +311 -0
  13. package/skills/forge/reference/chain.md +6 -0
  14. package/skills/forge/reference/cohesion-checklist.md +256 -0
  15. package/skills/forge/reference/cqrs.md +286 -0
  16. package/skills/forge/reference/data-patterns.md +6 -0
  17. package/skills/forge/reference/di-strategies.md +6 -0
  18. package/skills/forge/reference/errors.md +5 -0
  19. package/skills/forge/reference/events.md +8 -0
  20. package/skills/forge/reference/evolutionary-architecture.md +300 -0
  21. package/skills/forge/reference/forge.md +7 -0
  22. package/skills/forge/reference/hooks.md +6 -0
  23. package/skills/forge/reference/idempotency.md +283 -0
  24. package/skills/forge/reference/inscribe.md +5 -0
  25. package/skills/forge/reference/inspect.md +6 -0
  26. package/skills/forge/reference/modular-monolith.md +252 -0
  27. package/skills/forge/reference/observability.md +5 -0
  28. package/skills/forge/reference/quench.md +5 -0
  29. package/skills/forge/reference/relocate.md +6 -0
  30. package/skills/forge/reference/sagas.md +359 -0
  31. package/skills/forge/reference/security-patterns.md +6 -0
  32. package/skills/forge/reference/smelt.md +6 -0
  33. package/skills/forge/reference/temper.md +6 -0
  34. package/skills/forge/reference/testing-patterns.md +6 -0
  35. package/skills/forge/reference/transactional-outbox.md +311 -0
  36. package/skills/forge/scripts/architecture.mjs +10 -5
  37. package/skills/forge/scripts/assay.mjs +2 -2
  38. package/skills/forge/scripts/chain.mjs +31 -5
  39. package/skills/forge/scripts/context.mjs +24 -4
  40. package/skills/forge/scripts/detect.mjs +39 -30
  41. package/skills/forge/scripts/forge-boot.mjs +108 -0
  42. package/skills/forge/scripts/forge-config.mjs +182 -3
  43. package/skills/forge/scripts/forge-state.mjs +1 -1
  44. package/skills/forge/scripts/forgeSentinel-lib.mjs +86 -0
  45. package/skills/forge/scripts/forgeSentinel.mjs +184 -0
  46. package/skills/forge/scripts/forgeSmith-admin.mjs +104 -0
  47. package/skills/forge/scripts/forgeSmith.mjs +164 -0
  48. package/skills/forge/scripts/graph.mjs +65 -9
  49. package/skills/forge/scripts/hook.mjs +2 -2
  50. package/skills/forge/scripts/inspect.mjs +56 -48
  51. package/skills/forge/scripts/parse-imports.mjs +0 -2
  52. package/skills/forge/scripts/pin.mjs +10 -3
  53. package/skills/forge/scripts/posttool.mjs +2 -2
  54. package/skills/forge/scripts/recommendation-engine.mjs +125 -0
  55. package/skills/forge/scripts/rollback.mjs +5 -3
  56. package/skills/forge/templates/agents/SKILL.md.template +283 -0
  57. package/skills/forge/templates/agents/agents/hooks.json +18 -0
  58. package/skills/forge/templates/agents/claude/CLAUDE.md +17 -16
  59. package/skills/forge/templates/agents/claude/settings.local.json +18 -0
  60. package/skills/forge/templates/agents/codex/hooks.json +18 -0
  61. package/skills/forge/templates/agents/cursor/.cursorrules +22 -5
  62. package/skills/forge/templates/agents/cursor/hooks.json +11 -0
  63. package/skills/forge/templates/agents/gemini/SKILL.md +13 -0
  64. package/skills/forge/templates/feature/acl-gateway.ts.md +52 -0
  65. package/skills/forge/templates/feature/acl-repository.ts.md +36 -0
  66. package/skills/forge/templates/feature/acl-translator.ts.md +24 -0
  67. package/skills/forge/templates/feature/cqrs-query.ts.md +40 -0
  68. package/skills/forge/templates/feature/outbox-repository.ts.md +36 -0
  69. package/skills/forge/templates/feature/saga-orchestrator.ts.md +72 -0
  70. package/skills/forge/templates/platform/outbox-relayer.ts.md +80 -0
  71. package/skills/forge/tests/core.test.mjs +284 -0
  72. package/src/agents.mjs +35 -2
  73. package/src/cli.js +112 -39
  74. package/src/wizard.mjs +142 -90
@@ -0,0 +1,354 @@
1
+ # API Versioning — Evolución de Contratos
2
+
3
+ Las APIs evolucionan. Los clientes no siempre pueden actualizarse al mismo ritmo. El versionado de APIs permite cambiar contratos sin romper clientes existentes.
4
+
5
+ ---
6
+
7
+ ## Estrategias de Versionado
8
+
9
+ ### URL Path (más común)
10
+
11
+ ```
12
+ GET /api/v1/orders
13
+ GET /api/v2/orders
14
+ ```
15
+
16
+ ```ts
17
+ // features/orders/adapters/in/http/v1/OrderController.ts
18
+ @Router("/api/v1/orders")
19
+ export class OrderV1Controller {
20
+ @Get("/:id")
21
+ async getById(@Param("id") id: string) {
22
+ return this.useCase.execute(new GetOrderQuery(id));
23
+ }
24
+ }
25
+
26
+ // features/orders/adapters/in/http/v2/OrderController.ts
27
+ @Router("/api/v2/orders")
28
+ export class OrderV2Controller {
29
+ @Get("/:id")
30
+ async getById(@Param("id") id: string) {
31
+ const result = await this.useCase.execute(new GetOrderQuery(id));
32
+ // v2 devuelve datos adicionales que v1 no tenía
33
+ return { ...result, estimatedDelivery: result.shippingEstimate };
34
+ }
35
+ }
36
+ ```
37
+
38
+ **Pros:** explícito, fácil de rutear, fácil de cachear
39
+ **Contras:** URLs feas, versionado de toda la API (no granular)
40
+
41
+ ### Header (Accept / Content-Type)
42
+
43
+ ```
44
+ GET /api/orders
45
+ Accept: application/vnd.company.v1+json
46
+ ```
47
+
48
+ ```ts
49
+ // Middleware que selecciona versión según Accept header
50
+ export class VersionResolver {
51
+ resolve(req: Request): string {
52
+ const accept = req.headers["accept"] || "";
53
+ const match = accept.match(/application\/vnd\.company\.v(\d+)\+json/);
54
+ return match ? `v${match[1]}` : "v1"; // default v1
55
+ }
56
+ }
57
+ ```
58
+
59
+ **Pros:** URLs limpias, versionado granular por endpoint
60
+ **Contras:** complejidad en el cliente, difícil de debuggear, caché HTTP limitada
61
+
62
+ ### Query Parameter
63
+
64
+ ```
65
+ GET /api/orders?version=2
66
+ ```
67
+
68
+ **Pros:** simple, fácil de testear
69
+ **Contras:** contamina la semántica del query param, fácil de olvidar
70
+
71
+ ### Content Negotiation (recurso vs representación)
72
+
73
+ ```
74
+ GET /api/orders/123
75
+ Accept: application/json;version=2
76
+ ```
77
+
78
+ **Pros:** semánticamente correcto (negociación de contenido)
79
+ **Contras:** complejidad, poco soporte tooling
80
+
81
+ ### Recomendación de Forge
82
+
83
+ | Contexto | Estrategia |
84
+ |---|---|
85
+ | API pública (terceros) | URL path (`/api/v1/`) — la más explícita |
86
+ | API interna (entre features) | Header Accept — URLs limpias para el monolith |
87
+ | API móvil | URL path — los clientes móviles cachean URLs |
88
+ | BFF (Backend for Frontend) | Sin versionado (se despliega junto al frontend) |
89
+
90
+ ---
91
+
92
+ ## Compatibilidad
93
+
94
+ ### Cambios backward-compatible
95
+
96
+ | Cambio | Ejemplo |
97
+ |---|---|
98
+ | Añadir campo opcional en response | `{ name, email }` → `{ name, email, phone? }` |
99
+ | Añadir endpoint nuevo | `GET /v1/orders/:id/items` |
100
+ | Añadir campo en request con default | `{ name }` → `{ name, locale?: "en" }` |
101
+ | Extender enum | `Status.Active` → `Status.Active \| Status.Pending` |
102
+ | Relajar validación | Campo required → opcional |
103
+
104
+ ### Cambios breaking (requieren nueva versión)
105
+
106
+ | Cambio | Ejemplo |
107
+ |---|---|
108
+ | Eliminar campo del response | `{ name, email }` → `{ name }` |
109
+ | Renombrar campo | `{ email }` → `{ emailAddress }` |
110
+ | Cambiar tipo de campo | `{ price: string }` → `{ price: number }` |
111
+ | Hacer campo requerido | `{ locale? }` → `{ locale }` |
112
+ | Eliminar endpoint | `DELETE /v1/users` |
113
+ | Cambiar estructura | `{ address: string }` → `{ address: { street, city } }` |
114
+ | Cambiar error codes | `404` → `400` para mismo error |
115
+
116
+ ### Compatibilidad en TypeScript
117
+
118
+ ```ts
119
+ // shared/contracts/orders/v1/OrderDTO.ts
120
+ export interface OrderV1DTO {
121
+ id: string;
122
+ total: number;
123
+ status: string;
124
+ items: { sku: string; quantity: number }[];
125
+ }
126
+
127
+ // shared/contracts/orders/v2/OrderDTO.ts
128
+ export interface OrderV2DTO extends Omit<OrderV1DTO, "items"> {
129
+ total: number;
130
+ status: string;
131
+ items: { sku: string; quantity: number; name: string; imageUrl: string }[];
132
+ estimatedDelivery: string; // nuevo campo
133
+ }
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Versionado en el Modelo de Forge
139
+
140
+ ### Estructura de directorios
141
+
142
+ ```
143
+ src/features/orders/
144
+ adapters/
145
+ in/http/
146
+ v1/
147
+ OrderController.ts
148
+ OrderRoutes.ts
149
+ OrderValidator.ts ← validación de input v1
150
+ OrderPresenter.ts ← formateo de output v1
151
+ v2/
152
+ OrderController.ts
153
+ OrderRoutes.ts
154
+ OrderValidator.ts
155
+ OrderPresenter.ts
156
+ application/
157
+ use-cases/ ← los use cases NO se versionan
158
+ GetOrderUseCase.ts ← compartido entre v1 y v2
159
+ PlaceOrderUseCase.ts
160
+ mappers/ ← los mappers pueden tener versiones
161
+ v1/
162
+ OrderMapper.ts ← entidad de dominio → DTO v1
163
+ v2/
164
+ OrderMapper.ts ← entidad de dominio → DTO v2
165
+ ```
166
+
167
+ ### Principio: la lógica de negocio no se versiona
168
+
169
+ ```ts
170
+ // ✅ Los use cases son compartidos entre versiones
171
+ // v1 y v2 llaman al mismo use case
172
+ // Solo cambia el mapper (cómo se presenta el resultado)
173
+
174
+ // v1 mapper: devuelve el DTO original
175
+ export class OrderV1Mapper {
176
+ toDTO(order: OrderEntity): OrderV1DTO {
177
+ return {
178
+ id: order.id,
179
+ total: order.total,
180
+ status: order.status,
181
+ items: order.items.map((i) => ({ sku: i.sku, quantity: i.quantity })),
182
+ };
183
+ }
184
+ }
185
+
186
+ // v2 mapper: devuelve más información
187
+ export class OrderV2Mapper {
188
+ toDTO(order: OrderEntity): OrderV2DTO {
189
+ return {
190
+ id: order.id,
191
+ total: order.total,
192
+ status: order.status,
193
+ items: order.items.map((i) => ({
194
+ sku: i.sku,
195
+ quantity: i.quantity,
196
+ name: i.productName,
197
+ imageUrl: i.productImage,
198
+ })),
199
+ estimatedDelivery: this.calculateDelivery(order),
200
+ };
201
+ }
202
+ }
203
+ ```
204
+
205
+ ### Routing multi-versión
206
+
207
+ ```ts
208
+ // platform/http/Router.ts
209
+ export class ApiRouter {
210
+ constructor() {
211
+ this.registerV1Routes();
212
+ this.registerV2Routes();
213
+ this.registerVersionRedirect();
214
+ }
215
+
216
+ private registerVersionRedirect(): void {
217
+ // Si el cliente no especifica versión, redirigir a la más reciente estable
218
+ this.router.get("/api/orders", (req, res) => {
219
+ res.redirect("/api/v2/orders");
220
+ });
221
+ }
222
+ }
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Deprecación
228
+
229
+ ### Política de deprecación
230
+
231
+ ```
232
+ v1: lanzada 2025-01
233
+ v2: lanzada 2026-01 (v1 deprecated)
234
+ v3: lanzada 2027-01 (v1 sunset, v2 deprecated)
235
+ ```
236
+
237
+ ### Headers de deprecación
238
+
239
+ ```ts
240
+ // Middleware que añade headers de deprecación a versiones antiguas
241
+ export class DeprecationMiddleware {
242
+ private readonly sunsetVersions: Record<string, string> = {
243
+ v1: "2027-01-01", // fecha de retiro
244
+ };
245
+
246
+ handle(req: Request, res: Response, next: NextFunction): void {
247
+ const version = this.extractVersion(req.path);
248
+ const sunsetDate = this.sunsetVersions[version];
249
+ if (sunsetDate) {
250
+ res.setHeader("Sunset", sunsetDate);
251
+ res.setHeader("Deprecation", `true`);
252
+ res.setHeader(
253
+ "Link",
254
+ `</api/v2${req.path.replace(/\/api\/v[0-9]/, "")}>; rel="successor-version"`,
255
+ );
256
+ }
257
+ next();
258
+ }
259
+ }
260
+ ```
261
+
262
+ ### Política de retiro
263
+
264
+ 1. **Anunciar**: deprecación con 6 meses de anticipación (header `Deprecation: true` + `Sunset: fecha`)
265
+ 2. **Migrar**: soporte simultáneo durante el período de transición
266
+ 3. **Monitorear**: tracking de uso de versiones antiguas
267
+ 4. **Retirar**: cuando el tráfico de la versión antigua es < 1%, retirar con PR visible
268
+
269
+ ```bash
270
+ # Comando para monitorear uso de versiones
271
+ forge api --usage
272
+ # v1: 12% de requests
273
+ # v2: 88% de requests
274
+ # Sugerencia: v1 está lista para retiro (< 15%)
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Testing Multi-Versión
280
+
281
+ ```ts
282
+ // tests/integration/orders/OrderApi.test.ts
283
+ describe("Orders API", () => {
284
+ const versions = ["v1", "v2"];
285
+
286
+ versions.forEach((version) => {
287
+ describe(`${version} — getOrder`, () => {
288
+ it("returns 200 for existing order", async () => {
289
+ const response = await request(app)
290
+ .get(`/api/${version}/orders/test-id`)
291
+ .expect(200);
292
+
293
+ if (version === "v1") {
294
+ expect(response.body).not.toHaveProperty("estimatedDelivery");
295
+ }
296
+ if (version === "v2") {
297
+ expect(response.body).toHaveProperty("estimatedDelivery");
298
+ }
299
+ });
300
+
301
+ it("returns same core fields across versions", async () => {
302
+ const [v1Res, v2Res] = await Promise.all([
303
+ request(app).get("/api/v1/orders/test-id"),
304
+ request(app).get("/api/v2/orders/test-id"),
305
+ ]);
306
+
307
+ expect(v1Res.body.id).toBe(v2Res.body.id);
308
+ expect(v1Res.body.total).toBe(v2Res.body.total);
309
+ });
310
+ });
311
+ });
312
+ });
313
+ ```
314
+
315
+ ### Compatibilidad backward en CI
316
+
317
+ ```bash
318
+ # Verificar que v2 no rompe cambios backward
319
+ forge api --check-compatibility
320
+ # Checking orders v1 → v2... ✔ No breaking changes
321
+ # Checking users v1 → v2... ✖ Breaking: 'email' renamed to 'emailAddress'
322
+ # → Crear ADR y plan de deprecación
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Anti-patrones
328
+
329
+ | Anti-patrón | Problema | Solución |
330
+ |---|---|---|
331
+ | **Versionado global** | Versionar `v1/`, `v2/` para toda la API. Un cambio mínimo en un endpoint fuerza nueva versión de toda la API. | Versionado por recurso/feature. No toda la API tiene que estar en la misma versión. |
332
+ | **Mantener versiones para siempre** | "No podemos romper al cliente X". La deuda de mantener N versiones crece. | Política de retiro explícita (Sunset header + fecha). Máximo 2 versiones activas simultáneas. |
333
+ | **Versionado por "fecha"** | `/api/2025-01/orders`. Sin semántica de estabilidad. | Usar `v1`, `v2` (semver simplificado: major version only). |
334
+ | **Lógica de negocio versionada** | El use case v2 es distinto del v1. La lógica se duplica. | Los use cases son compartidos. Solo versionar la presentación (mappers + controllers). |
335
+ | **Versionado sin deprecación** | Se lanza v2 pero v1 no se depreca oficialmente. Los clientes nunca migran. | Header Deprecation + Sunset. Comunicación proactiva a clientes. |
336
+ | **API versionada pero eventos no** | La API versiona contratos HTTP, pero los eventos internos cambian sin versión. | Versionar schemas de eventos también (ej. `OrderPlaced.v2`). |
337
+
338
+ ---
339
+
340
+ ## Conexión con Forge
341
+
342
+ | Comando | Acción |
343
+ |---|---|
344
+ | `forge cast payments` | Crea feature con estructura v1/ en adapters/in/http/ |
345
+ | `forge api --check-compatibility` | Verifica que los cambios no rompen versiones anteriores |
346
+ | `forge api --usage` | Muestra distribución de tráfico entre versiones |
347
+ | `forge reforge` | Migra controllers de v1 a v2, depreca la anterior |
348
+ | `forge inspect` | Reporta versiones sin Deprecation header cuando hay versión superior |
349
+
350
+ ## Ver también
351
+
352
+ - `reference/api-design.md` — diseño de APIs REST/GraphQL
353
+ - `reference/adr.md` — ADRs para decisiones de versionado
354
+ - `reference/evolutionary-architecture.md` — evolución guiada de APIs
@@ -0,0 +1,311 @@
1
+ # Architectural Depth — Checklist de Referencias
2
+
3
+ Checklist de las 10 nuevas referencias estratégicas para Forge. Cada entrada define el alcance exacto, secciones obligatorias, relaciones con referencias existentes y criterio de completitud.
4
+
5
+ ---
6
+
7
+ ## Estado actual
8
+
9
+ ```
10
+ [x] 01 — bounded-contexts.md — DDD Estratégico
11
+ [x] 02 — modular-monolith.md — Estrategia de despliegue
12
+ [x] 03 — adr.md — Registro de decisiones
13
+ [x] 04 — anti-corruption-layer.md — Integración legacy
14
+ [x] 05 — evolutionary-architecture.md — Evolución arquitectónica
15
+ [x] 06 — cqrs.md — CQRS
16
+ [x] 07 — sagas.md — Transacciones distribuidas
17
+ [x] 08 — transactional-outbox.md — Fiabilidad de eventos
18
+ [x] 09 — idempotency.md — Idempotencia
19
+ [x] 10 — api-versioning.md — Evolución de contratos
20
+ ```
21
+
22
+ ---
23
+
24
+ ## 01 — `bounded-contexts.md`
25
+
26
+ **Propósito:** Fundamentar la capa `features/` en DDD Estratégico. Sin bounded contexts, las features son sólo directorios.
27
+
28
+ **Depende de:** nada (fundacional)
29
+ **Es usado por:** `modular-monolith.md`, `anti-corruption-layer.md`, `cqrs.md`, `sagas.md`
30
+
31
+ ### Secciones obligatorias
32
+
33
+ - [ ] **Fundamentos de DDD Estratégico**: dominio, subdominio (core/supporting/generic), bounded context
34
+ - [ ] **Ubiquitous Language**: por contexto, glosario compartido, conflictos de lenguaje
35
+ - [ ] **Context Mapping**: Partnership, Shared Kernel, Customer-Supplier, Conformist, Anti-Corruption Layer, Open-Host Service, Published Language, Separate Ways
36
+ - [ ] **Identificación de bounded contexts**: heurísticas (equipo, lenguaje, modelo, base de datos)
37
+ - [ ] **Mapeo visual**: diagrama de contexts con relaciones y flujos
38
+ - [ ] **Relación con el modelo de Forge**: cómo cada feature se corresponde con (parte de) un bounded context
39
+ - [ ] **Anti-patrones**: contextos gigantes (orphan core), contextos fantasma, contextos sin lenguaje
40
+ - [ ] **Ejemplo completo**: sistema de e-commerce con 4-5 bounded contexts mapeados
41
+ - [ ] **Conexión con reglas R8 y R9**: cómo los contextos prohíben acoplamiento directo y ciclos
42
+
43
+ ### Criterio de completitud
44
+
45
+ Un lector puede tomar cualquier feature existente y determinar su bounded context, su relación con otros contexts, y si el mapeo actual viola algún patrón de integridad.
46
+
47
+ ---
48
+
49
+ ## 02 — `modular-monolith.md`
50
+
51
+ **Propósito:** Proveer un marco de decisión para elegir entre monolith modular y microservicios, alineado con el modelo de 4 capas.
52
+
53
+ **Depende de:** `bounded-contexts.md`
54
+ **Es usado por:** `relocate.md`, `reforge.md`, `cast.md`
55
+
56
+ ### Secciones obligatorias
57
+
58
+ - [ ] **Definición de modular monolith**: módulos con boundaries fuertes, mismo proceso, despliegue único
59
+ - [ ] **Espectro Monolith → Modular → Microservices**: continuo, no binario
60
+ - [ ] **Marco de decisión**: cohesión, acoplamiento, topología de equipo (Conway's Law), escalabilidad, deployment autonomy
61
+ - [ ] **Cuándo mantener monolith**: equipo pequeño, dominio cohesionado, latencia crítica, inicio incierto
62
+ - [ ] **Señales para partir**: equipo scaling, deployment bottleneck, boundaries maduros, different failure characteristics
63
+ - [ ] **Criterio de corte por capa**: qué puede vivir como servicio vs qué debe compartirse (shared, platform)
64
+ - [ ] **Integración con Forge**: cómo modelar módulos usando features existentes, qué reglas (R8) se relajan dentro del monolith
65
+ - [ ] **Transición controlada**: split de features sin reescritura (strangler fig aplicado a features)
66
+ - [ ] **Anti-patrones**: distributed monolith, premature splitting, shared database entre servicios, nano-services
67
+ - [ ] **Ejemplo**: monolith modular de SaaS facturación que eventualmente parte billing en servicio separado
68
+
69
+ ### Criterio de completitud
70
+
71
+ Un equipo puede evaluar su arquitectura actual contra el marco de decisión y obtener una recomendación accionable sobre si partir o consolidar y por dónde empezar.
72
+
73
+ ---
74
+
75
+ ## 03 — `adr.md`
76
+
77
+ **Propósito:** Capturar y preservar decisiones arquitectónicas como parte del workflow de Forge.
78
+
79
+ **Depende de:** nada
80
+ **Es usado por:** `reforge.md`, `cast.md`, `inscribe.md`, todos
81
+
82
+ ### Secciones obligatorias
83
+
84
+ - [ ] **Formato ADR estándar**: Title, Status, Context, Decision, Consequences (plantilla)
85
+ - [ ] **Variantes**: ADR simple (5 secciones), ADR extendido (con alternatives, compliance), ADR ligero (1 párrafo)
86
+ - [ ] **Estados**: Proposed → Accepted / Deprecated / Superseded / Amended
87
+ - [ ] **Integración con `inscribe`**: anexar ADRs activos en ARCHITECTURE.md
88
+ - [ ] **Integración con `assay`**: las decisiones como insumo para el ensayo multi-persona
89
+ - [ ] **Cuándo escribir un ADR**: scoping, tecnología, patrón, estándar, cambio de regla, excepción
90
+ - [ ] **ADRs como fuente de verdad**: enlazar ADRs desde reglas de detect.mjs, desde inline ignores
91
+ - [ ] **Ejemplos**: ADRs reales del modelo Forge (ej. "usar capa Shared en vez de cross-feather imports", "adoptar Prisma como ORM")
92
+ - [ ] **Tooling**: script para crear, listar, cambiar estado de ADRs
93
+ - [ ] **Anti-patrones**: ADRs que nunca se leen, ADRs sin contexto, ADRs sin consecuencia, ADRs de frameworks
94
+
95
+ ### Criterio de completitud
96
+
97
+ Un `forge inscribe` genera ARCHITECTURE.md con enlaces a ADRs activos. Un nuevo miembro del equipo puede entender las decisiones clave en 10 minutos.
98
+
99
+ ---
100
+
101
+ ## 04 — `anti-corruption-layer.md`
102
+
103
+ **Propósito:** Aislar sistemas legacy o externos sin contaminar el modelo de dominio de Forge.
104
+
105
+ **Depende de:** `bounded-contexts.md`
106
+ **Es usado por:** `relocate.md`, `reforge.md`, `cast.md`
107
+
108
+ ### Secciones obligatorias
109
+
110
+ - [ ] **Definición y propósito ACL**: traducir entre modelos, evitar corrupción del modelo de dominio
111
+ - [ ] **Estructura de una ACL**: adapters de entrada (traducen del externo al dominio), adapters de salida (traducen del dominio al externo)
112
+ - [ ] **Strangler Fig pattern**: migración incremental con ACL como fachada
113
+ - [ ] **Implementación en el modelo de Forge**: la ACL vive en `adapter/out/` de una feature y traduce entre `infra/` y el modelo de dominio
114
+ - [ ] **Mapeo de integraciones legacy**: cómo modelar sistemas externos como bounded contexts con ACL
115
+ - [ ] **Estrategias de traducción**: event-based (publicar/suscribir), service-based (llamadas sincrónicas), repository-based (datos compartidos)
116
+ - [ ] **Conexión con reglas R1 y R7**: cómo ACL permite feature → infra sin violar la regla (porque el adapter traduce)
117
+ - [ ] **Detección automática**: qué patrones en detect.mjs indican necesidad de ACL
118
+ - [ ] **Ejemplo**: feature de "Orders" migrando de legacy SQL a nuevo schema con ACL + Strangler Fig
119
+ - [ ] **Anti-patrones**: ACL que filtra sin traducir (leaky abstraction), ACL que muta el origen, ACL como pasamanos
120
+
121
+ ### Criterio de completitud
122
+
123
+ Un desarrollador puede identificar cuándo necesita una ACL, modelarla dentro del feature correspondiente, y ejecutar la migración con `relocate` sin romper el sistema existente.
124
+
125
+ ---
126
+
127
+ ## 05 — `evolutionary-architecture.md`
128
+
129
+ **Propósito:** Guiar la evolución continua de la arquitectura sin reescrituras, usando fitness functions.
130
+
131
+ **Depende de:** nada (fundacional)
132
+ **Es usado por:** `inspect.md`, `reforge.md`, `graph.md`, `quench.md`
133
+
134
+ ### Secciones obligatorias
135
+
136
+ - [ ] **Definición**: arquitectura que evoluciona incrementalmente guiada por fitness functions
137
+ - [ ] **Fitness functions**: tests automatizados que validan características arquitectónicas (acoplamiento, modularidad, performance, seguridad)
138
+ - [ ] **Tipos de fitness functions**: estáticas (lint-level), dinámicas (runtime), periódicas (benchmark), trigger-based (CI)
139
+ - [ ] **Implementación en Forge**: las reglas R1-R9 como fitness functions gobernadas por detect.mjs
140
+ - [ ] **Fitness functions custom**: cómo el usuario define sus propias funciones y se registran en registry/rules.mjs
141
+ - [ ] **Guía de cambio incremental**: smallest viable change, refactor patterns, scaffolding antes de feature completo
142
+ - [ ] **Evolución de boundaries**: cómo partir, fusionar o mover features sin reescribir
143
+ - [ ] **Integración con CI/CD**: fitness functions en pipeline, gate de deployment, alertas de regresión
144
+ - [ ] **Ejemplo**: fitness function "no feature importa infra" evolucionando a "ningún módulo importa otro módulo sin interfaz"
145
+ - [ ] **Anti-patrones**: big-bang rewrite, frozen architecture, analysis paralysis, chasing tech debt sin métrica
146
+
147
+ ### Criterio de completitud
148
+
149
+ Un equipo puede añadir fitness functions personalizadas, integrarlas en su pipeline, y medir la salud arquitectónica en cada PR sin intervención manual.
150
+
151
+ ---
152
+
153
+ ## 06 — `cqrs.md`
154
+
155
+ **Propósito:** Modelar separación de commands y queries dentro de features con demandas asimétricas.
156
+
157
+ **Depende de:** `bounded-contexts.md`
158
+ **Es usado por:** `data-patterns.md`, `events.md`, `sagas.md`, `cast.md`
159
+
160
+ ### Secciones obligatorias
161
+
162
+ - [ ] **Fundamentos**: Command (escritura, efecto secundario, validación) vs Query (lectura, proyección, sin efectos)
163
+ - [ ] **Cuándo aplicar CQRS**: modelos de lectura/escritura divergentes, escalabilidad asimétrica, equipos separados, event sourcing
164
+ - [ ] **Implementación en el modelo de Forge**: command → use-case, query → repository o query service separado
165
+ - [ ] **Read models**: proyecciones desnormalizadas, tablas de lectura, caché de queries
166
+ - [ ] **CQRS parcial (sin event sourcing)**: commands y queries separados en la aplicación, misma BD
167
+ - [ ] **CQRS completo**: read model separado (tabla, BD, cache), eventual consistency
168
+ - [ ] **Materialized views / Projections**: cómo mantener read models actualizados
169
+ - [ ] **Separación de interfaces**: ICommandBus, IQueryBus como contratos en shared/
170
+ - [ ] **Conexión con reglas**: CQRS no viola ninguna regla de Forge porque commands y queries residen dentro del mismo feature
171
+ - [ ] **Anti-patrones**: CQRS everywhere (para CRUD simple), query leak (lógica de negocio en read model), eventual consistency ignorada
172
+ - [ ] **Ejemplo**: feature "Analytics" con CQRS parcial + read model en Redis via infra/redis
173
+
174
+ ### Criterio de completitud
175
+
176
+ Un feature complejo con demands asimétricas puede modelarse con CQRS dentro de la estructura de Forge sin violar reglas y sin over-engineering.
177
+
178
+ ---
179
+
180
+ ## 07 — `sagas.md`
181
+
182
+ **Propósito:** Coordinar transacciones multi-feature sin violar la regla R8 (no acoplamiento directo entre features).
183
+
184
+ **Depende de:** `bounded-contexts.md`, `events.md`
185
+ **Es usado por:** `cast.md`, `reforge.md`, `events.md`
186
+
187
+ ### Secciones obligatorias
188
+
189
+ - [ ] **Problema**: transacciones que cruzan bounded contexts sin ACID distribuido
190
+ - [ ] **Definición de Saga**: secuencia de transacciones locales con compensación
191
+ - [ ] **Coreografía (Choreography)**: cada participante publica/escucha eventos, decisión descentralizada
192
+ - [ ] **Orquestación (Orchestration)**: coordinador central que instruye a los participantes
193
+ - [ ] **Compensación**: transacciones reversibles, acciones compensatorias, consistencia eventual
194
+ - [ ] **Manejo de fallos**: retry con backoff, dead letter queue, fallback humano, saga log
195
+ - [ ] **Implementación en el modelo de Forge**: saga orchestrator como feature independiente (ej. "checkout-saga"), saga participantes usan eventos de dominio + adapters
196
+ - [ ] **Conexión con reglas R8 y R5**: cómo las sagas coordinan features sin importarlas directamente, eventos como contrato en shared/
197
+ - [ ] **Testing de sagas**: unit (participante individual), integration (flujo completo con mock), resilience (fallos y compensaciones)
198
+ - [ ] **Anti-patrones**: saga sin compensación (irreversible), orchestrator con lógica de negocio, coreografía sin trazabilidad, timeout único
199
+ - [ ] **Ejemplo**: saga de checkout (Inventory → Payment → Shipping) con orquestación y compensación por fallo de pago
200
+
201
+ ### Criterio de completitud
202
+
203
+ Un desarrollador puede modelar un flujo multi-feature usando sagas sin violar R8, con compensaciones claras, testing cubierto y trazabilidad.
204
+
205
+ ---
206
+
207
+ ## 08 — `transactional-outbox.md`
208
+
209
+ **Propósito:** Garantizar entrega confiable de eventos sin exponer inconsistencias transaccionales.
210
+
211
+ **Depende de:** `events.md`
212
+ **Es usado por:** `sagas.md`, `cqrs.md`, `events.md`
213
+
214
+ ### Secciones obligatorias
215
+
216
+ - [ ] **Problema**: dual-write (escribir en BD + publicar evento) atómico sin 2PC
217
+ - [ ] **Patrón Outbox**: escribir evento en tabla outbox dentro de la misma transacción que la operación de negocio
218
+ - [ ] **Outbox processor (relayer)**: proceso que lee la tabla outbox y publica eventos al message broker
219
+ - [ ] **Garantías**: at-least-once delivery, exactly-once processing con idempotencia
220
+ - [ ] **Implementaciones**: poll-based (relayer periódico), log-based (CDC con Debezium), hybrid
221
+ - [ ] **Integración con el modelo de Forge**: outbox en infra/prisma o infra/mongodb, processor como script/scheduler en platform/scheduler
222
+ - [ ] **Manejo de fallos**: retry con exponential backoff, poison messages, dead letter queue, alertas de outbox stuck
223
+ - [ ] **Idempotencia en consumidores**: deduplicación por event ID, idempotency key
224
+ - [ ] **Conexión con reglas**: outbox es infra que feature escribe mediante su adapter, sin violar R1
225
+ - [ ] **Anti-patrones**: outbox sin cleanup (tabla infinita), processor sin límite de reintentos, eventos sin idempotencia
226
+ - [ ] **Ejemplo**: feature "Orders" escribe pedido + outbox event en misma transacción Prisma → processor publica a RabbitMQ → "Payments" consume
227
+
228
+ ### Criterio de completitud
229
+
230
+ Un feature que publica eventos puede implementar outbox pattern siguiendo el template de Forge, garantizando at-least-once delivery sin riesgo de inconsistencias.
231
+
232
+ ---
233
+
234
+ ## 09 — `idempotency.md`
235
+
236
+ **Propósito:** Garantizar operaciones seguras para retry en APIs, eventos y procesos asíncronos.
237
+
238
+ **Depende de:** `api-design.md`
239
+ **Es usado por:** `api-design.md`, `events.md`, `sagas.md`, `cast.md`
240
+
241
+ ### Secciones obligatorias
242
+
243
+ - [ ] **Definición**: propiedad de una operación que puede aplicarse múltiples veces sin efecto secundario adicional
244
+ - [ ] **Tipos de idempotencia**: natural (GET), por clave (idempotency key), por semántica (last-write-wins)
245
+ - [ ] **Idempotency keys en APIs**: cabecera Idempotency-Key, almacenamiento en BD/cache, deduplicación, respuesta en caché
246
+ - [ ] **Idempotencia en eventos**: event ID como clave, deduplicación en consumidor, exactly-once semantics
247
+ - [ ] **Implementación en el modelo de Forge**: middleware en platform/http, repository en infra/redis, contract en shared/contracts
248
+ - [ ] **Idempotencia en sagas**: cómo cada paso de saga debe ser idempotente para retry seguro
249
+ - [ ] **Manejo de expiración**: TTL de claves de idempotencia, cleanup, conflictos de clave
250
+ - [ ] **Testing de idempotencia**: enviar misma request N veces, verificar resultado único
251
+ - [ ] **Conexión con reglas**: idempotency middleware es platform, repositorio de claves es infra, contracts son shared
252
+ - [ ] **Anti-patrones**: idempotency sin expiración, claves generadas por el servidor, idempotencia en GET, mutex como idempotencia
253
+ - [ ] **Ejemplo**: POST /payments con Idempotency-Key, key almacenada en Redis, respuesta en caché, consumidor de eventos con deduplicación
254
+
255
+ ### Criterio de completitud
256
+
257
+ Cada API de mutación en el feature expone idempotency keys. Cada consumidor de eventos maneja deduplicación. Retry es seguro en toda la cadena.
258
+
259
+ ---
260
+
261
+ ## 10 — `api-versioning.md`
262
+
263
+ **Propósito:** Evolucionar contratos de API sin romper clientes existentes.
264
+
265
+ **Depende de:** `api-design.md`
266
+ **Es usado por:** `api-design.md`, `reforge.md`, `cast.md`
267
+
268
+ ### Secciones obligatorias
269
+
270
+ - [ ] **Estrategias de versionado**: URL path (/v1/), header (Accept: application/vnd.api+json;version=1), content negotiation, query param
271
+ - [ ] **Compatibilidad**: backward compatible (additive changes), breaking changes (removal, rename, type change, required→optional)
272
+ - [ ] **Evolución de OpenAPI**: spec versionada, changelog automático, diff entre versiones
273
+ - [ ] **Versionado en el modelo de Forge**: controllers versionados por feature (features/users/adapters/in/http/v1/, v2/), routes con prefijo
274
+ - [ ] **Deprecación**: cabeceras Sunset, Deprecation, Retirement policy, migración de clientes
275
+ - [ ] **Internal vs Public API**: versionado estricto para pública, semver para interna
276
+ - [ ] **Integration testing multi-versión**: tests que corren contra v1 y v2 simultáneamente
277
+ - [ ] **Conexión con reglas**: ningún controller versionado debe violar R0 (cero lógica de negocio). La lógica vive en use cases
278
+ - [ ] **Anti-patrones**: versionado por "fecha" sin estabilidad, mantener N versiones sin política de muerte, versionado de toda la API en vez de por endpoint
279
+ - [ ] **Ejemplo**: feature "Users" migrando de v1 a v2 con cambio de modelo, controllers separados, deprecación gradual
280
+
281
+ ### Criterio de completitud
282
+
283
+ Un feature puede evolucionar su API de forma segura, con versionado explícito, deprecación controlada, tests multi-versión, y sin tocar la lógica de negocio.
284
+
285
+ ---
286
+
287
+ ## Integración en SKILL.md
288
+
289
+ - [x] Agregar las 10 referencias a la tabla **Module Index** en SKILL.md
290
+ - [x] Agregar entradas de **Command Routing** para los nuevos temas (lenguaje natural → referencia)
291
+ - [ ] Verificar que las referencias existentes que mencionan temas ahora cubiertos (events.md → sagas + outbox, data-patterns.md → CQRS) hagan `Ver: reference/<nueva>.md`
292
+
293
+ ## Templates
294
+
295
+ - [x] Evaluar si `templates/feature/` necesita nuevos templates (saga orchestrator, outbox processor, ACL adapter, CQRS query service)
296
+ - [x] saga-orchestrator.ts.md — orchestrator con compensaciones
297
+ - [x] cqrs-query.ts.md — query service para lecturas separadas
298
+ - [x] acl-repository.ts.md — ACL que implementa repositorio de dominio
299
+ - [x] acl-translator.ts.md — traducción entre DTO externo y entidad de dominio
300
+ - [x] acl-gateway.ts.md — comunicación con sistema externo (HTTP)
301
+ - [x] outbox-repository.ts.md — repositorio de outbox integrado en UnitOfWork
302
+ - [x] outbox-relayer.ts.md — relayer en platform/scheduler
303
+ - [x] domain-event.ts.md — clase base DomainEvent con eventId para deduplicación
304
+ - [ ] Evaluar si `templates/shared/` necesita contratos de idempotencia o de eventos de integración
305
+
306
+ ## Testing
307
+
308
+ - [x] transactional-outbox pattern — 5 tests (entry lifecycle, retry policy, DLQ, required fields, pending detection)
309
+ - [x] idempotency pattern — 5 tests (UUID validation, cached response, separate keys, TTL expiry, method filtering)
310
+ - [x] anti-corruption-layer pattern — 5 tests (external→domain mapping, domain→external mapping, null handling, 404 handling, delegation order)
311
+ - [ ] Prioridad futura: ADR workflow, evolutionary architecture fitness functions