@saulwade/swl-ses 1.1.4 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +13 -2
- package/README.md +3 -3
- package/agentes/revisor-codigo-swl.md +88 -36
- package/bin/swl-mcp-server.js +187 -0
- package/habilidades/benchmark-memoria/SKILL.md +186 -0
- package/habilidades/contenedores-docker/SKILL.md +8 -1
- package/habilidades/datos-etl/SKILL.md +18 -1
- package/habilidades/doubt-driven-review/SKILL.md +171 -0
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -0
- package/habilidades/eval-framework/SKILL.md +212 -0
- package/habilidades/memoria-busqueda/SKILL.md +24 -1
- package/habilidades/meta-skills-estandar/SKILL.md +4 -0
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -0
- package/habilidades/planear-fase/SKILL.md +299 -269
- package/habilidades/postgresql-experto/SKILL.md +24 -1
- package/habilidades/verificar-trabajo/SKILL.md +7 -1
- package/hooks/lib/evolution-tracker.js +65 -11
- package/hooks/lib/memory-search.js +44 -13
- package/hooks/sugerir-contribuir.js +226 -0
- package/manifiestos/hooks-config.json +9 -0
- package/manifiestos/modulos.json +35 -2
- package/manifiestos/perfiles.json +2 -1
- package/package.json +6 -3
- package/plugin.json +343 -343
- package/reglas/skills-estandar.md +3 -0
- package/scripts/benchmark-memoria.js +167 -0
- package/scripts/detectar-aprendizajes-duplicados.js +151 -0
- package/scripts/generar-checklists-consolidados.js +273 -0
- package/scripts/lib/benchmark-metrics.js +160 -0
- package/scripts/lib/eval-metrics-store.js +218 -0
- package/scripts/lib/eval-quality.js +171 -0
- package/scripts/lib/eval-schemas.js +144 -0
- package/scripts/lib/eval-self-correct.js +106 -0
- package/scripts/lib/eval-validator.js +185 -0
- package/scripts/lib/jaccard-similarity.js +98 -0
- package/scripts/lib/longmemeval-runner.js +125 -0
- package/scripts/lib/rrf-fusion.js +175 -0
- package/scripts/lib/scoring-instintos.js +40 -3
- package/scripts/mcp-server/README.md +128 -0
- package/scripts/mcp-server/handlers.js +206 -0
- package/scripts/run-eval.js +141 -0
package/CLAUDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# CLAUDE.md — @saulwade/swl-ses v1.1
|
|
1
|
+
# CLAUDE.md — @saulwade/swl-ses v1.2.1
|
|
2
2
|
|
|
3
3
|
## Reglas de máxima prioridad (aplican SIEMPRE, sin excepción)
|
|
4
4
|
|
|
@@ -23,7 +23,7 @@ El Read tool sigue siendo correcto para `.pdf` (≤20 páginas), `.md`, `.txt` y
|
|
|
23
23
|
## Qué es este repositorio
|
|
24
24
|
|
|
25
25
|
Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo).
|
|
26
|
-
11 lenguajes, 5 runtimes, 59 agentes,
|
|
26
|
+
11 lenguajes, 5 runtimes, 59 agentes, 153 skills, 42 comandos, 64 reglas, 40 hooks.
|
|
27
27
|
**Idioma**: 100% español (México) para componentes SWL y skills Anthropic en inglés.
|
|
28
28
|
|
|
29
29
|
## Estructura del repositorio
|
|
@@ -218,6 +218,17 @@ node scripts/generar-inventario.js
|
|
|
218
218
|
|
|
219
219
|
Motivo: Estimé "28 hooks" visualmente y propagué el error a 5 archivos; el conteo real (regenerado) era 30. Contar manualmente no es fuente de verdad — el script que recorre los directorios sí lo es. Aplica a cualquier proyecto SWL, no solo al sistema.
|
|
220
220
|
|
|
221
|
+
### Regla: checklists consolidados se regeneran, no se editan a mano
|
|
222
|
+
|
|
223
|
+
Los archivos en `docs/checklists-consolidados/` son derivados de las secciones `## Checklist` de las reglas en `reglas/`. Para modificar el contenido, editar la regla origen y ejecutar:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
npm run gen-checklists # regenera todos los archivos
|
|
227
|
+
npm run gen-checklists:check # falla si hay drift (uso CI)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
NO editar manualmente los archivos generados. Cada uno lleva header `<!-- GENERADO desde reglas/X.md -->`. Origen: opción B del análisis de repos externos (2026-05-09) — patrón "fuente única, presentación múltiple" sin duplicar contenido.
|
|
231
|
+
|
|
221
232
|
### Regla: modelo por defecto para auto-ejecución headless
|
|
222
233
|
|
|
223
234
|
Cuando un script o bot externo invoca `claude -p` sin intervención humana, usar por defecto:
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# swl-ses v1.
|
|
1
|
+
# swl-ses v1.2.0
|
|
2
2
|
|
|
3
3
|
> El paquete anterior `@saulwadeleon/swl-software-engineering-system` está deprecado. Migrar a `@saulwade/swl-ses` (npmjs.org canónico) o `@saul-wade/swl-ses` (mirror en GitHub Packages) — el CLI `swl-ses` no cambia.
|
|
4
4
|
|
|
@@ -177,7 +177,7 @@ claude
|
|
|
177
177
|
| `mobile` | Android + iOS + React Native/Flutter + UX |
|
|
178
178
|
| `devops` | CI/CD + cloud + observabilidad + releases + seguridad |
|
|
179
179
|
| `polyglot` | Todos los lenguajes: 11 lenguajes + revisores + build resolvers |
|
|
180
|
-
| `completo` | Todo: 59 agentes +
|
|
180
|
+
| `completo` | Todo: 59 agentes + 153 habilidades + 42 comandos + 64 reglas + 40 hooks |
|
|
181
181
|
|
|
182
182
|
### Targets soportados
|
|
183
183
|
|
|
@@ -478,7 +478,7 @@ swl-ses/
|
|
|
478
478
|
seguridad.js # Validaciones de seguridad
|
|
479
479
|
manifiestos/ # Perfiles y módulos de instalación
|
|
480
480
|
agentes/ # 59 agentes especializados
|
|
481
|
-
habilidades/ #
|
|
481
|
+
habilidades/ # 153 habilidades modulares
|
|
482
482
|
comandos/swl/ # 42 comandos slash
|
|
483
483
|
reglas/ # 20 reglas base + 40 por lenguaje
|
|
484
484
|
hooks/ # 39 hooks + 62 librerías en hooks/lib/
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: revisor-codigo-swl
|
|
3
3
|
description: >
|
|
4
|
-
Revisa la calidad del código producido con criterios de senior implacable
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Revisa la calidad del código producido con criterios de senior implacable
|
|
5
|
+
organizados en el modelo 5-axis (Correctness, Readability, Architecture,
|
|
6
|
+
Security, Performance). Cubre directamente legibilidad, SOLID, DRY,
|
|
7
|
+
complejidad ciclomática y code smells; documenta handoffs explícitos a
|
|
8
|
+
revisor-seguridad-swl (Security) y rendimiento-swl (Performance) sin
|
|
9
|
+
duplicar análisis. Emite un reporte con métricas numéricas y calificación
|
|
10
|
+
por dimensión. Invocar después de que el implementador termina un slice o
|
|
11
|
+
feature, antes de pasar a revisión de seguridad. También invocar para
|
|
12
|
+
auditar calidad de código heredado.
|
|
9
13
|
tools: [Read, Grep, Glob, Bash]
|
|
10
14
|
model: claude-sonnet-4-6
|
|
11
15
|
modeloAlterno: claude-haiku-4-5-20251001
|
|
12
16
|
ventanaContexto: 200k
|
|
13
17
|
color: orange
|
|
14
|
-
version: 1.
|
|
18
|
+
version: 1.2.0
|
|
15
19
|
evolved: true
|
|
16
|
-
evolved-from: "1.1.
|
|
17
|
-
evolved-at: "2026-05-
|
|
20
|
+
evolved-from: "1.1.2"
|
|
21
|
+
evolved-at: "2026-05-09"
|
|
18
22
|
evolved-by: "aprender"
|
|
19
|
-
evolved-note: "
|
|
23
|
+
evolved-note: "Reorganización de scoring con vocabulario 5-axis Addy Osmani sin duplicar capacidades existentes. Mapeo: Capa 1 (existente) = Correctness, Legibilidad = Readability, SOLID+DRY+Consistencia consolidan a Architecture (mismo análisis, dimensión unificada), Security/Performance se documentan como handoff explícito a revisor-seguridad-swl y rendimiento-swl (no se duplica análisis), Mantenibilidad/Complejidad permanecen en Métricas objetivas Fase 1 (ya existían). Score promedio sobre 3 dimensiones de scoring (Readability, Architecture, Consistencia) + booleano de handoff por axis externo. Veto items y formato de reporte preservados, retrocompatible. Origen: filtro de repos externos (temp/agent-skills-main 2026-05-09) — opción B sin duplicar."
|
|
20
24
|
nivelRiesgo: BAJO
|
|
21
25
|
skillsInvocables: [checklist-calidad, patrones-python, api-rest-diseno, tdd-workflow, verificar-trabajo, verificacion-evidencia, swl-revisar-impacto, prevencion-sobreingenieria]
|
|
22
26
|
skillsRestringidos: []
|
|
@@ -75,9 +79,12 @@ Antes de revisar cualquier código:
|
|
|
75
79
|
|
|
76
80
|
## Revision en dos capas (obligatorio)
|
|
77
81
|
|
|
78
|
-
Toda revision se ejecuta en dos capas en orden estricto
|
|
82
|
+
Toda revision se ejecuta en dos capas en orden estricto. El vocabulario
|
|
83
|
+
sigue el modelo 5-axis Addy Osmani (Correctness, Readability, Architecture,
|
|
84
|
+
Security, Performance) reorganizado contra las capacidades existentes del
|
|
85
|
+
sistema swl-ses — sin duplicar análisis que otros agentes ya realizan.
|
|
79
86
|
|
|
80
|
-
**Capa 1 — Spec Compliance
|
|
87
|
+
**Capa 1 — Correctness** (Spec Compliance): el codigo hace lo que se pidio?
|
|
81
88
|
- Leer PLAN.md o requisitos originales
|
|
82
89
|
- Verificar cada requisito tiene implementacion
|
|
83
90
|
- Verificar NO hay scope creep (cosas no pedidas)
|
|
@@ -85,11 +92,21 @@ Toda revision se ejecuta en dos capas en orden estricto:
|
|
|
85
92
|
- Si NO CUMPLE: devolver sin ejecutar Capa 2
|
|
86
93
|
|
|
87
94
|
**Capa 2 — Code Quality** (solo si Capa 1 = CUMPLE):
|
|
88
|
-
-
|
|
89
|
-
-
|
|
95
|
+
- 3 dimensiones de scoring directo: Readability, Architecture, Consistencia
|
|
96
|
+
- 2 axis con handoff explícito a revisores especializados:
|
|
97
|
+
- **Security** → `revisor-seguridad-swl` (NO duplicar aquí; reportar
|
|
98
|
+
como handoff y referenciar el reporte del agente especializado)
|
|
99
|
+
- **Performance** → `rendimiento-swl` o consultar `Skill("performance-baseline")`
|
|
100
|
+
cuando hay sospecha; reportar como handoff con justificación numérica
|
|
101
|
+
- Métricas objetivas (Fase 1) cubren Mantenibilidad y Complejidad como datos
|
|
102
|
+
cuantitativos, no como dimensiones de scoring redundantes
|
|
103
|
+
- Categorizar problemas: Critico (bloquea merge), Importante (fix antes de
|
|
104
|
+
merge), Menor (ticket)
|
|
90
105
|
- Veredicto: APROBADO | CON OBSERVACIONES | RECHAZADO
|
|
91
106
|
|
|
92
|
-
El reporte incluye ambas capas con veredicto explicito por capa
|
|
107
|
+
El reporte incluye ambas capas con veredicto explicito por capa Y registra
|
|
108
|
+
el estado de los 2 handoffs (Security/Performance) como booleano EJECUTADO
|
|
109
|
+
o NO_REQUERIDO con justificación.
|
|
93
110
|
|
|
94
111
|
## Flujo de trabajo paso a paso
|
|
95
112
|
|
|
@@ -285,23 +302,55 @@ Verifica que el código nuevo sigue los mismos patrones del código existente:
|
|
|
285
302
|
|
|
286
303
|
La inconsistencia es deuda técnica — hace el código más difícil de navegar.
|
|
287
304
|
|
|
288
|
-
### Fase 7 — Calcular score por dimensión
|
|
305
|
+
### Fase 7 — Calcular score por dimensión (5-axis consolidado)
|
|
289
306
|
|
|
290
|
-
|
|
307
|
+
El scoring se organiza en el vocabulario 5-axis estándar de la industria
|
|
308
|
+
(Addy Osmani), mapeado a las capacidades reales del sistema swl-ses:
|
|
291
309
|
|
|
292
|
-
|
|
293
|
-
|-----------|-------|-------------|
|
|
294
|
-
| Legibilidad | N/10 | Nombres claros + comentarios apropiados + tamaño de unidades |
|
|
295
|
-
| Mantenibilidad | N/10 | Índice radon MI normalizado + ausencia de code smells graves |
|
|
296
|
-
| SOLID | N/10 | 1 punto por cada principio respetado completamente |
|
|
297
|
-
| DRY | N/10 | Descuento por cada duplicación detectada |
|
|
298
|
-
| Complejidad | N/10 | Basado en complejidad ciclomática máxima y promedio |
|
|
299
|
-
| Consistencia | N/10 | Alineación con patrones del proyecto |
|
|
300
|
-
| **PROMEDIO** | **N/10** | Promedio simple de las 6 dimensiones |
|
|
310
|
+
**Dimensiones de scoring directo** (calificar 1-10):
|
|
301
311
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
312
|
+
| Dimensión 5-axis | Cubre | Metodología | Insumo |
|
|
313
|
+
|---|---|---|---|
|
|
314
|
+
| **Correctness** | Capa 1 — Spec Compliance | CUMPLE / PARCIAL / NO CUMPLE → 10 / 6 / 0 | Veredicto Capa 1 |
|
|
315
|
+
| **Readability** | Legibilidad | Nombres claros + comentarios apropiados + tamaño de unidades + nivel de abstracción consistente | Fase 2 |
|
|
316
|
+
| **Architecture** | SOLID + DRY + Consistencia | Promedio ponderado: SOLID 40% (1 punto por principio respetado), DRY 30% (descuento por duplicación), Consistencia 30% (alineación con patrones del proyecto) | Fases 3, 5, 6 |
|
|
317
|
+
|
|
318
|
+
**Métricas objetivas** (NO scoring duplicado, ya en Fase 1):
|
|
319
|
+
|
|
320
|
+
| Métrica | Datos | Acción si falla umbral |
|
|
321
|
+
|---|---|---|
|
|
322
|
+
| Mantenibilidad (radon MI) | Índice >= 65 | Reportar en métricas + flagging si < 50 |
|
|
323
|
+
| Complejidad ciclomática | Máx <= 10 por función, prom <= 5 | Veto item VI-2 si > 15 (ya cubierto) |
|
|
324
|
+
|
|
325
|
+
**Handoffs externos** (NO duplicar análisis aquí — reportar booleano + ref):
|
|
326
|
+
|
|
327
|
+
| Axis 5-axis | Agente especializado | Cuándo invocar | Cómo reportar |
|
|
328
|
+
|---|---|---|---|
|
|
329
|
+
| **Security** | `revisor-seguridad-swl` | Toda PR que toque endpoints, auth, manejo de archivos, datos de usuario o queries SQL | `Security: HANDOFF a revisor-seguridad-swl — [ref de reporte]` o `Security: NO_REQUERIDO — cambio sin superficie de seguridad` con justificación |
|
|
330
|
+
| **Performance** | `rendimiento-swl` | Loops anidados, queries N+1 sospechadas, paths críticos | `Performance: HANDOFF a rendimiento-swl — [ref]` o `Performance: NO_REQUERIDO — cambio fuera de path crítico` con justificación |
|
|
331
|
+
|
|
332
|
+
**Cálculo del PROMEDIO** (3 dimensiones de scoring):
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
PROMEDIO = (Correctness + Readability + Architecture) / 3
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Los 2 handoffs (Security, Performance) NO entran al promedio numérico —
|
|
339
|
+
afectan el veredicto:
|
|
340
|
+
|
|
341
|
+
- Si Security HANDOFF está pendiente y el cambio toca endpoints/auth/datos:
|
|
342
|
+
veredicto NO puede ser APROBADO hasta recibir el reporte del especialista.
|
|
343
|
+
- Si Performance HANDOFF está pendiente y el cambio toca path crítico:
|
|
344
|
+
igual.
|
|
345
|
+
- Si ambos NO_REQUERIDO con justificación: el veredicto se decide solo por
|
|
346
|
+
PROMEDIO + veto items.
|
|
347
|
+
|
|
348
|
+
Score >= 8.5 sin handoffs pendientes: Aprobar
|
|
349
|
+
Score 7.0-8.4 sin handoffs pendientes: Aprobar con correcciones menores documentadas
|
|
350
|
+
Score < 7.0 o handoff pendiente: Rechazar / esperar reporte del especialista
|
|
351
|
+
|
|
352
|
+
Los veto items (cap a 6.0) y la regla de NO aprobar con CRÍTICO no resuelto
|
|
353
|
+
se preservan sin cambios.
|
|
305
354
|
|
|
306
355
|
## Clasificación de problemas
|
|
307
356
|
|
|
@@ -396,16 +445,19 @@ Si no se detecta ninguno: `### VETO ITEMS DETECTADOS\n- Ninguno`.
|
|
|
396
445
|
| Líneas por función (máx) | X | <= 30 | OK/ALERTA |
|
|
397
446
|
| Violaciones linter | X | 0 | OK/ALERTA |
|
|
398
447
|
|
|
399
|
-
### Score por dimensión
|
|
448
|
+
### Score por dimensión (5-axis)
|
|
400
449
|
| Dimensión | Score | Justificación breve |
|
|
401
450
|
|-----------|-------|---------------------|
|
|
402
|
-
|
|
|
403
|
-
|
|
|
404
|
-
|
|
|
405
|
-
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
|
451
|
+
| Correctness (Capa 1 — Spec) | N/10 | CUMPLE/PARCIAL/NO CUMPLE → 10/6/0 |
|
|
452
|
+
| Readability | N/10 | [Legibilidad: nombres + comentarios + tamaño] |
|
|
453
|
+
| Architecture | N/10 | [SOLID 40% + DRY 30% + Consistencia 30%] |
|
|
454
|
+
| **PROMEDIO** | **N/10** | (Correctness + Readability + Architecture) / 3 |
|
|
455
|
+
|
|
456
|
+
### Handoffs externos (no duplicar análisis)
|
|
457
|
+
| Axis | Estado | Ref/Justificación |
|
|
458
|
+
|------|--------|-------------------|
|
|
459
|
+
| Security | EJECUTADO / NO_REQUERIDO / PENDIENTE | [ref reporte revisor-seguridad-swl o "cambio sin superficie de seguridad"] |
|
|
460
|
+
| Performance | EJECUTADO / NO_REQUERIDO / PENDIENTE | [ref reporte rendimiento-swl o "cambio fuera de path crítico"] |
|
|
409
461
|
|
|
410
462
|
### Problemas encontrados
|
|
411
463
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* swl-mcp-server — Servidor MCP **EXPERIMENTAL** para exponer la memoria
|
|
6
|
+
* de swl-ses a clientes MCP externos (Cursor, Gemini CLI, OpenCode, etc.).
|
|
7
|
+
*
|
|
8
|
+
* **NO PRODUCCIÓN — STUB EXPERIMENTAL**.
|
|
9
|
+
* Ver `scripts/mcp-server/README.md` para limitaciones detalladas.
|
|
10
|
+
*
|
|
11
|
+
* Modo de transporte: stdio (JSON-RPC sobre stdin/stdout).
|
|
12
|
+
* No HTTP, no auth, no rate limiting.
|
|
13
|
+
*
|
|
14
|
+
* Uso (cliente MCP):
|
|
15
|
+
* - Configurar el cliente para ejecutar `node /path/to/swl-ses/bin/swl-mcp-server.js`
|
|
16
|
+
* con stdio.
|
|
17
|
+
* - Los handlers leen el cwd del proceso para localizar `.planning/`,
|
|
18
|
+
* `instintos/`, `APRENDIZAJES.md`. Por defecto usa `process.cwd()`.
|
|
19
|
+
* - Override con env var `SWL_MCP_BASE_DIR` si el cliente arranca el server
|
|
20
|
+
* desde otro directorio.
|
|
21
|
+
*
|
|
22
|
+
* Protocolo MCP soportado (subset):
|
|
23
|
+
* - initialize / initialized
|
|
24
|
+
* - tools/list
|
|
25
|
+
* - tools/call
|
|
26
|
+
*
|
|
27
|
+
* NO soporta:
|
|
28
|
+
* - resources/list, prompts/list
|
|
29
|
+
* - logging, sampling
|
|
30
|
+
* - cancellation, progress
|
|
31
|
+
* - HTTP transport
|
|
32
|
+
*
|
|
33
|
+
* Trigger documentado para implementación completa: "uso ≥2 runtimes
|
|
34
|
+
* diferentes (Cursor + Claude Code o similar) consistentemente por
|
|
35
|
+
* ≥1 mes". Hoy: 0 instalaciones reportadas.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const path = require('path');
|
|
39
|
+
|
|
40
|
+
const { HANDLERS } = require('../scripts/mcp-server/handlers');
|
|
41
|
+
|
|
42
|
+
const SERVER_NAME = 'swl-mcp-server';
|
|
43
|
+
const SERVER_VERSION = '0.1.0-experimental';
|
|
44
|
+
const PROTOCOL_VERSION = '2024-11-05';
|
|
45
|
+
|
|
46
|
+
const baseDir = process.env.SWL_MCP_BASE_DIR || process.cwd();
|
|
47
|
+
|
|
48
|
+
// ── logging ───────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
// Stderr para evitar contaminar stdout (que es JSON-RPC).
|
|
51
|
+
function log(level, msg, data) {
|
|
52
|
+
const linea = JSON.stringify({
|
|
53
|
+
timestamp: new Date().toISOString(),
|
|
54
|
+
level,
|
|
55
|
+
msg,
|
|
56
|
+
...(data ? { data } : {}),
|
|
57
|
+
});
|
|
58
|
+
process.stderr.write(linea + '\n');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── JSON-RPC helpers ──────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
function respuesta(id, result) {
|
|
64
|
+
return JSON.stringify({ jsonrpc: '2.0', id, result });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function errorResp(id, code, message) {
|
|
68
|
+
return JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── routing ───────────────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
function manejarInitialize(request) {
|
|
74
|
+
return respuesta(request.id, {
|
|
75
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
76
|
+
capabilities: {
|
|
77
|
+
tools: { listChanged: false },
|
|
78
|
+
},
|
|
79
|
+
serverInfo: {
|
|
80
|
+
name: SERVER_NAME,
|
|
81
|
+
version: SERVER_VERSION,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function manejarToolsList(request) {
|
|
87
|
+
const tools = Object.entries(HANDLERS).map(([name, def]) => ({
|
|
88
|
+
name,
|
|
89
|
+
description: def.description,
|
|
90
|
+
inputSchema: def.inputSchema,
|
|
91
|
+
}));
|
|
92
|
+
return respuesta(request.id, { tools });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function manejarToolsCall(request) {
|
|
96
|
+
const { name, arguments: args } = request.params || {};
|
|
97
|
+
const def = HANDLERS[name];
|
|
98
|
+
if (!def) {
|
|
99
|
+
return errorResp(request.id, -32601, `Tool no encontrado: ${name}`);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const result = def.handler(baseDir, args || {});
|
|
103
|
+
return respuesta(request.id, {
|
|
104
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
105
|
+
});
|
|
106
|
+
} catch (err) {
|
|
107
|
+
log('error', `Excepción en handler ${name}`, { error: err.message });
|
|
108
|
+
return errorResp(request.id, -32603, `Error interno: ${err.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function rutear(request) {
|
|
113
|
+
switch (request.method) {
|
|
114
|
+
case 'initialize':
|
|
115
|
+
return manejarInitialize(request);
|
|
116
|
+
case 'initialized':
|
|
117
|
+
case 'notifications/initialized':
|
|
118
|
+
return null; // notification — sin respuesta
|
|
119
|
+
case 'tools/list':
|
|
120
|
+
return manejarToolsList(request);
|
|
121
|
+
case 'tools/call':
|
|
122
|
+
return manejarToolsCall(request);
|
|
123
|
+
case 'ping':
|
|
124
|
+
return respuesta(request.id, {});
|
|
125
|
+
default:
|
|
126
|
+
return errorResp(request.id, -32601, `Método no soportado: ${request.method}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── loop principal ────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
function arrancar() {
|
|
133
|
+
log('warn', '⚠ swl-mcp-server stub experimental — NO usar en producción');
|
|
134
|
+
log('info', `Server iniciando`, { name: SERVER_NAME, version: SERVER_VERSION, baseDir });
|
|
135
|
+
|
|
136
|
+
let buffer = '';
|
|
137
|
+
|
|
138
|
+
process.stdin.setEncoding('utf8');
|
|
139
|
+
process.stdin.on('data', (chunk) => {
|
|
140
|
+
buffer += chunk;
|
|
141
|
+
|
|
142
|
+
// Cada mensaje JSON-RPC termina con \n
|
|
143
|
+
let nlIndex;
|
|
144
|
+
while ((nlIndex = buffer.indexOf('\n')) >= 0) {
|
|
145
|
+
const linea = buffer.slice(0, nlIndex).trim();
|
|
146
|
+
buffer = buffer.slice(nlIndex + 1);
|
|
147
|
+
|
|
148
|
+
if (!linea) continue;
|
|
149
|
+
|
|
150
|
+
let request;
|
|
151
|
+
try {
|
|
152
|
+
request = JSON.parse(linea);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
log('error', 'JSON inválido recibido', { error: err.message, linea: linea.slice(0, 100) });
|
|
155
|
+
process.stdout.write(errorResp(null, -32700, 'Parse error') + '\n');
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const respuestaStr = rutear(request);
|
|
160
|
+
if (respuestaStr) {
|
|
161
|
+
process.stdout.write(respuestaStr + '\n');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
process.stdin.on('end', () => {
|
|
167
|
+
log('info', 'stdin cerrado, server termina');
|
|
168
|
+
process.exit(0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Manejo de errores no capturados — nunca crashear silenciosamente
|
|
172
|
+
process.on('uncaughtException', (err) => {
|
|
173
|
+
log('error', 'uncaughtException', { error: err.message, stack: err.stack });
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (require.main === module) {
|
|
178
|
+
arrancar();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
rutear,
|
|
183
|
+
arrancar,
|
|
184
|
+
SERVER_NAME,
|
|
185
|
+
SERVER_VERSION,
|
|
186
|
+
PROTOCOL_VERSION,
|
|
187
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: benchmark-memoria
|
|
3
|
+
description: >
|
|
4
|
+
Benchmark de retrieval para `hooks/lib/memory-search` que mide R@5, R@10,
|
|
5
|
+
MRR y nDCG@10 contra un dataset de queries con respuestas conocidas
|
|
6
|
+
(gold_ids). Útil para detectar regresión de calidad de búsqueda al cambiar
|
|
7
|
+
RRF weights, scoring o fuentes. Cargar cuando se modifique
|
|
8
|
+
`memory-search.js`, `rrf-fusion.js` o `session-fts.js` para verificar que
|
|
9
|
+
no degrada retrieval. NO cargar para uso operacional ni para evaluar
|
|
10
|
+
outputs de agentes (eso es `eval-framework`).
|
|
11
|
+
version: "1.0.0"
|
|
12
|
+
herramientasPermitidas: [Read, Bash]
|
|
13
|
+
exclusiones:
|
|
14
|
+
- "No cargar para evaluar outputs de agentes (aprendizajes, observaciones, resúmenes); eso es `eval-framework`."
|
|
15
|
+
- "No cargar como gate de release sin un dataset ≥30 entries con status='real' — las métricas con dataset placeholder no son estadísticamente significativas."
|
|
16
|
+
- "No cargar para benchmark de performance (latencia, throughput); eso es `performance-baseline`."
|
|
17
|
+
- "No cargar si el repo no tiene `.planning/APRENDIZAJES.md` ni sesiones — sin contenido no hay nada que recuperar."
|
|
18
|
+
evolvable: true # default para skill estandar
|
|
19
|
+
---
|
|
20
|
+
# Benchmark de Memoria — LongMemEval-S adaptado a SWL
|
|
21
|
+
|
|
22
|
+
## Cuándo cargar esta skill
|
|
23
|
+
|
|
24
|
+
- Tras modificar `hooks/lib/memory-search.js`, `scripts/lib/rrf-fusion.js`,
|
|
25
|
+
pesos de fusión o algoritmo de scoring de relevancia.
|
|
26
|
+
- Tras agregar fuentes de búsqueda nuevas (ej. evals, instintos globales).
|
|
27
|
+
- Antes de un release que toque la capa de memoria, para confirmar que no
|
|
28
|
+
hay regresión en R@5.
|
|
29
|
+
- Como parte opcional de `/swl:salud` cuando `SWL_BENCHMARK_MEMORIA=1`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Componentes del benchmark
|
|
34
|
+
|
|
35
|
+
| Módulo | Propósito |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `scripts/lib/benchmark-metrics.js` | Funciones puras: `recallAt`, `precisionAt`, `mrr`, `ndcgAt`, `dcg`, `calcularMetricas`, `promediar`. |
|
|
38
|
+
| `scripts/lib/longmemeval-runner.js` | Adapter que ejecuta queries contra `memory-search` y compara con gold. |
|
|
39
|
+
| `scripts/benchmark-memoria.js` | CLI runner principal. |
|
|
40
|
+
| `.planning/benchmark/dataset.jsonl` | Dataset (placeholder por defecto, debe expandirse). |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Estado del dataset (CRÍTICO leer antes de usar)
|
|
45
|
+
|
|
46
|
+
El dataset por defecto en `.planning/benchmark/dataset.jsonl` es **placeholder**
|
|
47
|
+
con 10 entries marcadas explícitamente como `"status": "placeholder"`. Las
|
|
48
|
+
métricas calculadas con este dataset son **indicativas, no estadísticamente
|
|
49
|
+
significativas**.
|
|
50
|
+
|
|
51
|
+
### Limitaciones del dataset placeholder
|
|
52
|
+
|
|
53
|
+
1. **IDs volátiles**: los `gold_ids` referencian `apr-N` (índice de entrada en
|
|
54
|
+
`APRENDIZAJES.md`). Si se agregan/borran entradas, los índices cambian y
|
|
55
|
+
el dataset queda desincronizado. Para dataset real considerar IDs estables
|
|
56
|
+
(sesiones tienen timestamp; instintos tienen `id` propio).
|
|
57
|
+
2. **N=10 es ruido estadístico**: para que R@5=80% sea significativo
|
|
58
|
+
estadísticamente (vs 70% baseline), se requieren al menos 30 queries
|
|
59
|
+
con N=10 random. Por debajo, las métricas reflejan suerte.
|
|
60
|
+
3. **Cobertura limitada**: el placeholder cubre solo categorías técnicas
|
|
61
|
+
(patrones, gotchas, decisiones). Falta cobertura de:
|
|
62
|
+
- Bug fixes históricos
|
|
63
|
+
- Workflow questions ("qué hicimos antes de X")
|
|
64
|
+
- Cross-session ("cuándo se decidió Y")
|
|
65
|
+
- Negative queries (preguntas cuya respuesta es "no aplica")
|
|
66
|
+
|
|
67
|
+
### Cómo expandir a dataset real
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 1. Identifica una pregunta real de uso
|
|
71
|
+
QUERY="qué hicimos sobre force push a main protegida"
|
|
72
|
+
|
|
73
|
+
# 2. Ejecuta búsqueda y anota top-5 IDs
|
|
74
|
+
node -e "console.log(require('./hooks/lib/memory-search').search('.', '$QUERY').slice(0, 5).map(r => r.id + ' / ' + r.titulo).join('\n'))"
|
|
75
|
+
|
|
76
|
+
# 3. Verifica manualmente qué IDs son CORRECTOS (revisión humana,
|
|
77
|
+
# no se inventa). Solo esos van en gold_ids.
|
|
78
|
+
|
|
79
|
+
# 4. Agrega la entry al dataset:
|
|
80
|
+
cat >> .planning/benchmark/dataset.jsonl << 'EOF'
|
|
81
|
+
{"question_id": "q-real-001", "question": "qué hicimos sobre force push a main protegida", "gold_ids": ["apr-313"], "category": "decision", "status": "real"}
|
|
82
|
+
EOF
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Repetir hasta tener ≥30 entries con `status: "real"`. Solo entonces el
|
|
86
|
+
benchmark es gate de release.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Uso
|
|
91
|
+
|
|
92
|
+
### CLI básico
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Ejecutar benchmark con dataset por defecto
|
|
96
|
+
node scripts/benchmark-memoria.js
|
|
97
|
+
|
|
98
|
+
# Output esperado:
|
|
99
|
+
# Recall @ 5: 85.0%
|
|
100
|
+
# Recall @ 10: 92.0%
|
|
101
|
+
# MRR: 0.741
|
|
102
|
+
# nDCG @ 10: 0.812
|
|
103
|
+
# Precision @ 5: 41.3%
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Opciones
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Dataset alternativo
|
|
110
|
+
node scripts/benchmark-memoria.js --dataset .planning/benchmark/custom.jsonl
|
|
111
|
+
|
|
112
|
+
# Top-k personalizado (default 20)
|
|
113
|
+
node scripts/benchmark-memoria.js --limit 30
|
|
114
|
+
|
|
115
|
+
# Output JSON (para scripts)
|
|
116
|
+
node scripts/benchmark-memoria.js --json
|
|
117
|
+
|
|
118
|
+
# Detalle por query (útil para debugging)
|
|
119
|
+
node scripts/benchmark-memoria.js --verbose
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Tracking histórico opcional
|
|
123
|
+
|
|
124
|
+
Si se setea `SWL_BENCHMARK_PERSIST=1`, el benchmark escribe el resumen
|
|
125
|
+
agregado a `.planning/evolucion/benchmark-memoria.jsonl` (append-only)
|
|
126
|
+
para detectar regresión entre releases:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
SWL_BENCHMARK_PERSIST=1 node scripts/benchmark-memoria.js
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Comparar entre releases:
|
|
133
|
+
```bash
|
|
134
|
+
tail -5 .planning/evolucion/benchmark-memoria.jsonl | jq -c '{ts: .timestamp, r5: .promedio.recall_at_5}'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Métricas explicadas
|
|
140
|
+
|
|
141
|
+
| Métrica | Significado | Rango |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| **Recall @ k** | ¿El sistema recuperó al menos un gold ID en los primeros k? | 0 o 1 por query, promediado en [0, 1] |
|
|
144
|
+
| **Precision @ k** | ¿Qué porcentaje de los primeros k son gold? | [0, 1] |
|
|
145
|
+
| **MRR** | 1 / posición del primer gold encontrado | [0, 1] — alto = gold cerca del top |
|
|
146
|
+
| **nDCG @ k** | DCG normalizado: penaliza gold en posiciones bajas | [0, 1] — mide ranking quality |
|
|
147
|
+
|
|
148
|
+
**Interpretación**:
|
|
149
|
+
- R@5 alto + MRR bajo → encuentra gold pero no en posición 1.
|
|
150
|
+
- R@5 bajo + R@10 alto → necesita expandir top-k para alcanzar.
|
|
151
|
+
- nDCG@10 alto → ranking respeta orden de relevancia.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Anti-patrones (qué NO hacer)
|
|
156
|
+
|
|
157
|
+
- **Usar el dataset placeholder como gate de CI**: no significativo, da
|
|
158
|
+
falsa sensación de seguridad. Marcar el job como informativo solamente
|
|
159
|
+
hasta tener dataset real ≥30.
|
|
160
|
+
- **Inventar gold_ids "que se ven correctos"**: el dataset SOLO sirve si
|
|
161
|
+
los gold_ids son verificados manualmente como correctos por un humano
|
|
162
|
+
con conocimiento del proyecto.
|
|
163
|
+
- **Optimizar el algoritmo solo para que las métricas suban**: si el
|
|
164
|
+
dataset es placeholder, "mejorar" R@5 puede ser overfitting al
|
|
165
|
+
placeholder. Dataset real primero, optimización después.
|
|
166
|
+
- **Borrar entries que dan métricas bajas**: una query con R@5=0 puede
|
|
167
|
+
estar revelando un bug real del sistema de búsqueda, no un problema
|
|
168
|
+
del dataset. Debug antes de borrar.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Gotchas / Errores comunes no obvios
|
|
173
|
+
|
|
174
|
+
- **Dataset JSONL acepta comentarios `//`**: las líneas que empiezan con
|
|
175
|
+
`//` son ignoradas. Útil para documentar inline. NO es JSON estándar
|
|
176
|
+
pero el parser de `longmemeval-runner` lo respeta.
|
|
177
|
+
- **Si la query no matchea ningún token mínimo (<= 3 chars), `search()`
|
|
178
|
+
devuelve `[]`**: las stop words están filtradas en
|
|
179
|
+
`hooks/lib/memory-search.js`. Asegurar que cada query tenga ≥2
|
|
180
|
+
términos significativos.
|
|
181
|
+
- **Los `gold_ids` deben usar el formato `apr-N`/`ses-YYYYMMDD-HHmm`/etc.
|
|
182
|
+
EXACTO** que devuelve `memory-search`. Si se anota `apr-14` cuando el
|
|
183
|
+
search devuelve `apr-014`, el match falla silenciosamente.
|
|
184
|
+
- **El benchmark mide `memory-search` específicamente, no toda la memoria
|
|
185
|
+
SWL**: instintos globales no entran si solo se buscan locales; sesiones
|
|
186
|
+
archivadas no entran. Documentar el scope de cada query.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: contenedores-docker
|
|
3
3
|
description: Docker y containerización. Dockerfiles optimizados con multi-stage builds, docker-compose, volúmenes, networking, health checks, security scanning, build caching, distroless images. Anti-patrones comunes.
|
|
4
|
-
version: "1.0.
|
|
4
|
+
version: "1.0.1"
|
|
5
|
+
evolved: true
|
|
6
|
+
evolved-from: "1.0.0"
|
|
7
|
+
evolved-at: "2026-05-05"
|
|
8
|
+
evolved-by: "aprender"
|
|
9
|
+
evolved-note: "Gotcha de la sesión SIGM 2026-05-05 (L6): scripts en /docker-entrypoint-initdb.d con dependencias externas (mc, aws, etc.) abortan initdb"
|
|
5
10
|
herramientasPermitidas: [Read]
|
|
6
11
|
exclusiones:
|
|
7
12
|
- "No cargar para orquestación Kubernetes (Deployments, Services, Helm, HPA) — para Kubernetes cargar `kubernetes-orquestacion`."
|
|
@@ -135,3 +140,5 @@ Para ejemplos completos de multi-stage Node.js/Angular, .dockerignore, docker-co
|
|
|
135
140
|
**Secrets pasados como `ARG` en el Dockerfile son visibles en `docker history` aunque se eliminen en una capa posterior**: `ARG SECRET_KEY` seguido de `RUN rm -f /app/.env` no elimina el valor del ARG del historial de la imagen — `docker history --no-trunc imagen` muestra los valores de los ARG. Causa: `docker history` registra los comandos de cada capa, incluyendo los valores de ARG usados. Fix: NUNCA pasar secretos como `ARG` o `ENV` en el Dockerfile; para secrets en build-time usar `--secret` de Docker BuildKit: `RUN --mount=type=secret,id=api_key cat /run/secrets/api_key`.
|
|
136
141
|
|
|
137
142
|
**`.dockerignore` mal configurado incluye archivos `.env` o `node_modules` en el contexto de build, ralentizando el daemon y exponiendo secretos**: si `.dockerignore` no existe o no excluye `node_modules`, el contexto de build puede ser de cientos de MB — todo se transfiere al daemon antes del build. Causa: el contexto de build incluye todo el directorio por defecto. Fix: crear `.dockerignore` con al menos `.git`, `node_modules`, `__pycache__`, `.env*`, `*.pyc`, `dist`, `build`, y verificar el tamaño del contexto con `docker build` mirando la línea "Sending build context to Docker daemon X.XX MB".
|
|
143
|
+
|
|
144
|
+
**Scripts en `database/init/` (mounted a `/docker-entrypoint-initdb.d`) no pueden depender de binarios fuera de la imagen base**: un script con `set -e` que invoca `mc` (cliente MinIO), `aws`, `gh`, `kubectl` o cualquier binario no presente en la imagen `postgres`/`postgis` aborta el initdb con `command not found` (exit 127). El contenedor sale con error y los scripts SQL que iban después (incluido schemas/seeds completos) NUNCA se ejecutan. Caso real (SIGM 2026-05-04): `02-crear-buckets-minio.sh` con `mc alias set` aborta `postgis/postgis:17-3.5` antes de cargar el schema; ningún schema se aplicaba aunque el archivo init.sh estaba bien. Causa: el directorio `/docker-entrypoint-initdb.d` ejecuta todos los archivos `.sh` y `.sql` en orden alfabético; cualquier fallo aborta toda la cadena. Fix: scripts que invocan binarios externos van en `scripts/setup/` o `scripts/init-deps/` y se ejecutan manualmente o via `docker compose run --rm --entrypoint /scripts/setup/X.sh <servicio_que_tiene_el_binario>`. Solo SQL puro o bash que use `psql` pueden vivir en `database/init/`.
|
|
@@ -4,7 +4,12 @@ description: >
|
|
|
4
4
|
Ingeniería de datos y ETL: diseño de pipelines, calidad de datos, evolución de esquemas,
|
|
5
5
|
CDC (change data capture), validación de datos, linaje de datos, procesamiento batch vs
|
|
6
6
|
streaming, patrones dbt, patrones de data lake.
|
|
7
|
-
version: "1.0.
|
|
7
|
+
version: "1.0.1"
|
|
8
|
+
evolved: true
|
|
9
|
+
evolved-from: "1.0.0"
|
|
10
|
+
evolved-at: "2026-05-05"
|
|
11
|
+
evolved-by: "aprender"
|
|
12
|
+
evolved-note: "Gotcha de la sesión SIGM 2026-05-05 (L9): seeds con check constraint fecha_vencimiento >= fecha_emision violan al usar emision única para todas las tuplas"
|
|
8
13
|
herramientasPermitidas: [Read, Grep]
|
|
9
14
|
evolvable: true # default para skill estandar
|
|
10
15
|
exclusiones:
|
|
@@ -127,3 +132,15 @@ esquemas (Avro), patrones dbt (modelos incrementales), y linaje de datos, ver
|
|
|
127
132
|
**Validación con Great Expectations pasa en staging pero falla en producción con el mismo schema**: los expectations se definen sobre una muestra de datos de staging que puede no tener todos los valores posibles del dominio. Causa: un expectation de tipo `expect_column_values_to_be_in_set(["A","B","C"])` falla en producción cuando aparece un valor "D" válido que no existía en el sample de staging. Fix: para columnas con dominio abierto, usar `expect_column_values_to_not_be_null` en lugar de un set cerrado. Reservar sets cerrados solo para columnas con dominio verdaderamente finito y controlado (estados de máquina de estado, flags booleanos).
|
|
128
133
|
|
|
129
134
|
**El job de Spark/pandas falla en producción por OOM aunque funcionó bien en staging con datos de muestra**: el procesamiento en memoria de un DataFrame completo escala linealmente con el volumen de datos. En staging con 10K registros todo cabe en RAM; en producción con 10M registros el proceso muere. Causa: operaciones como `groupby + apply` con funciones Python puras no se pueden paralelizar ni distribuir automáticamente. Fix: para transformaciones sobre datasets grandes, usar procesamiento por chunks (`chunksize` en pandas) o migrar a Spark/DuckDB para operaciones distribuidas. Medir el uso de memoria en staging con el volumen máximo esperado en producción, no con la muestra de desarrollo.
|
|
135
|
+
|
|
136
|
+
**Seeds que simulan data histórica con check constraints de fechas relativas violan el constraint si se usa una sola fecha de emisión**: cuando un seed crea facturas/pagos demo con una mezcla de estatus (vencidas, vigentes, pagadas) y la tabla tiene check constraint del tipo `fecha_vencimiento >= fecha_emision`, generar `fecha_emision = '2025-12-10'` y luego asignar `fecha_vencimiento = '2025-10-31'` para vencidas viola el constraint. Caso real (SIGM 2026-05-04): seed `009-datos-operativos-demo.sql` con 500 facturas demo donde `n%10=0` representaba "vencidas" pero la fecha_emision común era posterior. Causa: la lógica genera dimensiones derivadas (vencimiento) sin re-derivar las fuente (emisión). Fix: separar fechas POR ESTATUS antes de generar tuplas:
|
|
137
|
+
```sql
|
|
138
|
+
-- Vencidas: emision en pasado profundo, vencimiento ya pasó
|
|
139
|
+
CASE WHEN n % 10 = 0 THEN DATE '2025-09-30'
|
|
140
|
+
ELSE DATE '2025-12-10' + (n % 3)
|
|
141
|
+
END AS fecha_emision,
|
|
142
|
+
CASE WHEN n % 10 = 0 THEN DATE '2025-10-31'
|
|
143
|
+
ELSE DATE '2026-01-31'
|
|
144
|
+
END AS fecha_vencimiento,
|
|
145
|
+
```
|
|
146
|
+
Generalización: cualquier check constraint que relacione columnas debe respetarse al SEEDEAR, no solo en INSERT productivo.
|