@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.
Files changed (41) hide show
  1. package/CLAUDE.md +13 -2
  2. package/README.md +3 -3
  3. package/agentes/revisor-codigo-swl.md +88 -36
  4. package/bin/swl-mcp-server.js +187 -0
  5. package/habilidades/benchmark-memoria/SKILL.md +186 -0
  6. package/habilidades/contenedores-docker/SKILL.md +8 -1
  7. package/habilidades/datos-etl/SKILL.md +18 -1
  8. package/habilidades/doubt-driven-review/SKILL.md +171 -0
  9. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -0
  10. package/habilidades/eval-framework/SKILL.md +212 -0
  11. package/habilidades/memoria-busqueda/SKILL.md +24 -1
  12. package/habilidades/meta-skills-estandar/SKILL.md +4 -0
  13. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -0
  14. package/habilidades/planear-fase/SKILL.md +299 -269
  15. package/habilidades/postgresql-experto/SKILL.md +24 -1
  16. package/habilidades/verificar-trabajo/SKILL.md +7 -1
  17. package/hooks/lib/evolution-tracker.js +65 -11
  18. package/hooks/lib/memory-search.js +44 -13
  19. package/hooks/sugerir-contribuir.js +226 -0
  20. package/manifiestos/hooks-config.json +9 -0
  21. package/manifiestos/modulos.json +35 -2
  22. package/manifiestos/perfiles.json +2 -1
  23. package/package.json +6 -3
  24. package/plugin.json +343 -343
  25. package/reglas/skills-estandar.md +3 -0
  26. package/scripts/benchmark-memoria.js +167 -0
  27. package/scripts/detectar-aprendizajes-duplicados.js +151 -0
  28. package/scripts/generar-checklists-consolidados.js +273 -0
  29. package/scripts/lib/benchmark-metrics.js +160 -0
  30. package/scripts/lib/eval-metrics-store.js +218 -0
  31. package/scripts/lib/eval-quality.js +171 -0
  32. package/scripts/lib/eval-schemas.js +144 -0
  33. package/scripts/lib/eval-self-correct.js +106 -0
  34. package/scripts/lib/eval-validator.js +185 -0
  35. package/scripts/lib/jaccard-similarity.js +98 -0
  36. package/scripts/lib/longmemeval-runner.js +125 -0
  37. package/scripts/lib/rrf-fusion.js +175 -0
  38. package/scripts/lib/scoring-instintos.js +40 -3
  39. package/scripts/mcp-server/README.md +128 -0
  40. package/scripts/mcp-server/handlers.js +206 -0
  41. package/scripts/run-eval.js +141 -0
package/CLAUDE.md CHANGED
@@ -1,4 +1,4 @@
1
- # CLAUDE.md — @saulwade/swl-ses v1.1.4
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, 151 skills, 42 comandos, 64 reglas, 39 hooks.
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.4
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 + 151 habilidades + 42 comandos + 64 reglas + 39 hooks |
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/ # 151 habilidades modulares
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
- legibilidad, mantenibilidad, DRY, SOLID, complejidad ciclomática y code smells.
6
- Emite un reporte con métricas numéricas y calificación por dimensión. Invocar
7
- después de que el implementador termina un slice o feature, antes de pasar a
8
- revisión de seguridad. También invocar para auditar calidad de código heredado.
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.1.2
18
+ version: 1.2.0
15
19
  evolved: true
16
- evolved-from: "1.1.1"
17
- evolved-at: "2026-05-04"
20
+ evolved-from: "1.1.2"
21
+ evolved-at: "2026-05-09"
18
22
  evolved-by: "aprender"
19
- evolved-note: "Fix Fase 5b la guía de pasada 2 listaba patrones de naming específicos (self._repo, self.repo, uow) presentándolos como cobertura exhaustiva. Reescrita como principio semántico de dos condiciones (verbo de mutación + receptor de capa de persistencia), explícitamente independiente del naming concreto. Cubre repositorios con cualquier nombre de variable, dependencias inyectadas y CRUD modules."
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**: el codigo hace lo que se pidio?
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
- - Legibilidad, SOLID, DRY, complejidad, code smells
89
- - Categorizar: Critico (bloquea merge), Importante (fix antes de merge), Menor (ticket)
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
- Califica de 1 a 10 cada dimensión con justificación numérica:
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
- | Dimensión | Score | Metodología |
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
- Score >= 8.5: Aprobar
303
- Score 7.0-8.4: Aprobar con correcciones menores documentadas
304
- Score < 7.0: Rechazarcorrecciones requeridas antes de continuar
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
- | Legibilidad | N/10 | [razón] |
403
- | Mantenibilidad | N/10 | [razón] |
404
- | SOLID | N/10 | [razón] |
405
- | DRY | N/10 | [razón] |
406
- | Complejidad | N/10 | [razón] |
407
- | Consistencia | N/10 | [razón] |
408
- | **PROMEDIO** | **N/10** | |
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.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.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.