@saulwade/swl-ses 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CLAUDE.md +225 -209
  2. package/README.md +578 -561
  3. package/agentes/arquitecto-swl.md +33 -1
  4. package/agentes/nemesis-auditor-swl.md +59 -19
  5. package/bin/swl-mcp-server.js +214 -214
  6. package/bin/swl-ses.js +49 -7
  7. package/comandos/swl/.evolved.json +22 -22
  8. package/comandos/swl/contribuir.md +233 -233
  9. package/comandos/swl/nemesis.md +230 -56
  10. package/gateway/lib/event-channel.js +191 -191
  11. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  12. package/habilidades/benchmark-memoria/SKILL.md +186 -186
  13. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  14. package/habilidades/doubt-driven-review/SKILL.md +171 -171
  15. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  16. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
  17. package/habilidades/eval-framework/SKILL.md +212 -212
  18. package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
  19. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  20. package/habilidades/harness-claude-code/SKILL.md +299 -299
  21. package/habilidades/infra-github-actions/SKILL.md +166 -166
  22. package/habilidades/legacy-code-rescue/SKILL.md +267 -267
  23. package/habilidades/manejo-errores/.evolved.json +8 -8
  24. package/habilidades/meta-skills-estandar/SKILL.md +207 -4
  25. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  26. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
  27. package/habilidades/nemesis-evaluacion-json/SKILL.md +266 -0
  28. package/habilidades/nemesis-redistribuir/SKILL.md +341 -0
  29. package/habilidades/node-experto/SKILL.md +94 -4
  30. package/habilidades/patrones-python/SKILL.md +229 -229
  31. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  32. package/habilidades/planear-fase/SKILL.md +319 -319
  33. package/habilidades/protocolo-revision-swl/SKILL.md +350 -276
  34. package/habilidades/release-semver/.evolved.json +8 -8
  35. package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
  36. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  37. package/habilidades/tdd-workflow/SKILL.md +121 -4
  38. package/habilidades/testing-python/SKILL.md +340 -340
  39. package/habilidades/web-fetcher-routing/SKILL.md +75 -75
  40. package/hooks/check-update.js +31 -3
  41. package/hooks/claudemd-bloat-detector.js +161 -161
  42. package/hooks/extraccion-aprendizajes.js +11 -0
  43. package/hooks/lib/agent-routing.js +107 -107
  44. package/hooks/lib/auto-consolidator.js +335 -335
  45. package/hooks/lib/error-classifier.js +308 -308
  46. package/hooks/lib/merkle-audit.js +96 -96
  47. package/hooks/lib/provenance-tracker.js +191 -191
  48. package/hooks/lib/rate-limit-tracker.js +253 -253
  49. package/hooks/lib/resource-quota.js +122 -122
  50. package/hooks/lib/retry-jitter.js +165 -165
  51. package/hooks/lib/security-net.js +201 -201
  52. package/hooks/lib/skill-auditor.js +588 -588
  53. package/hooks/lib/sync-status.js +228 -228
  54. package/hooks/lib/taint-tracker.js +107 -107
  55. package/hooks/lib/text-similarity.js +241 -241
  56. package/hooks/lib/toon-compressor.js +245 -245
  57. package/hooks/registro-turnos.js +209 -209
  58. package/hooks/sugerir-regenerar-inventario.js +170 -170
  59. package/hooks/validar-formato-post-subagente.js +140 -140
  60. package/hooks/validar-memoria-hook.js +218 -218
  61. package/instintos/prompt-appendices.yaml +57 -57
  62. package/manifiestos/agent-output-schemas.json +57 -57
  63. package/manifiestos/modulos.json +1324 -1321
  64. package/manifiestos/skills-lock.json +1142 -1114
  65. package/package.json +5 -4
  66. package/plantillas/auditor-veto-template.md +105 -105
  67. package/plantillas/github-workflows/README.md +47 -47
  68. package/plantillas/github-workflows/release-please.yml +44 -44
  69. package/plantillas/github-workflows/swl-ci.yml +107 -107
  70. package/plantillas/github-workflows/swl-security.yml +51 -51
  71. package/plugin.json +355 -351
  72. package/reglas/analisis-previo-tareas-grandes.md +172 -172
  73. package/reglas/arreglar-al-detectar.md +147 -147
  74. package/reglas/fragmentos-compartidos.md +152 -152
  75. package/reglas/harness-claude-code.md +213 -213
  76. package/reglas/registro-componentes-nuevos.md +192 -0
  77. package/reglas/usar-context7.md +226 -226
  78. package/schemas/diary-entry.schema.json +80 -80
  79. package/scripts/actualizar.js +110 -1
  80. package/scripts/audit-tools/audit-history.js +330 -330
  81. package/scripts/audit-tools/bundle-tracker.js +290 -290
  82. package/scripts/audit-tools/canary-monitor.js +352 -352
  83. package/scripts/audit-tools/code-profiler.js +605 -605
  84. package/scripts/audit-tools/dep-doctor.js +320 -320
  85. package/scripts/audit-tools/env-validator.js +206 -206
  86. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  87. package/scripts/audit-tools/lib/output.js +23 -23
  88. package/scripts/audit-tools/migration-checker.js +392 -392
  89. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  90. package/scripts/benchmark-memoria.js +167 -167
  91. package/scripts/configurar-branch-protection.js +418 -418
  92. package/scripts/derivar-feature-list.js +489 -489
  93. package/scripts/desinstalar.js +105 -24
  94. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  95. package/scripts/doctor.js +27 -0
  96. package/scripts/field-report.js +199 -199
  97. package/scripts/generar-checklists-consolidados.js +273 -273
  98. package/scripts/generar-inventario.js +420 -420
  99. package/scripts/generar-matriz-lenguajes.js +271 -271
  100. package/scripts/instalador.js +55 -4
  101. package/scripts/lib/artefactos-python.js +43 -43
  102. package/scripts/lib/benchmark-metrics.js +160 -160
  103. package/scripts/lib/budget-enforcer.js +252 -252
  104. package/scripts/lib/configurar-ci.js +380 -380
  105. package/scripts/lib/contadores-inventario.js +217 -217
  106. package/scripts/lib/detectar-stack-detallado.js +307 -307
  107. package/scripts/lib/diary-entry.js +234 -234
  108. package/scripts/lib/eval-metrics-store.js +218 -218
  109. package/scripts/lib/eval-quality.js +171 -171
  110. package/scripts/lib/eval-schemas.js +144 -144
  111. package/scripts/lib/eval-self-correct.js +106 -106
  112. package/scripts/lib/eval-validator.js +185 -185
  113. package/scripts/lib/expandir-targets.js +71 -71
  114. package/scripts/lib/jaccard-similarity.js +98 -98
  115. package/scripts/lib/longmemeval-runner.js +125 -125
  116. package/scripts/lib/mcp_config.py +127 -0
  117. package/scripts/lib/npm-version.js +261 -261
  118. package/scripts/lib/paquetes-conocidos.js +50 -50
  119. package/scripts/lib/parsear-opciones.js +3 -0
  120. package/scripts/lib/prompt-builder.js +264 -264
  121. package/scripts/lib/rrf-fusion.js +175 -175
  122. package/scripts/lib/scoring-instintos.js +277 -277
  123. package/scripts/lib/semantic-search.js +252 -252
  124. package/scripts/lib/toml-merge.js +204 -204
  125. package/scripts/lib/transformadores/codex.js +375 -375
  126. package/scripts/lib/transformadores/cursor.js +359 -359
  127. package/scripts/lib/ui.js +148 -22
  128. package/scripts/limpiar-artefactos-python.js +131 -131
  129. package/scripts/mcp-orchestrator.py +8 -18
  130. package/scripts/mcp-pool-manager.py +12 -23
  131. package/scripts/mcp-server/README.md +170 -170
  132. package/scripts/mcp-server/auth.js +105 -105
  133. package/scripts/mcp-server/cache.js +106 -106
  134. package/scripts/mcp-server/telemetry.js +78 -78
  135. package/scripts/migrar-csv-a-array.js +168 -168
  136. package/scripts/migrar-fase-dominio.js +201 -201
  137. package/scripts/publicar.js +511 -511
  138. package/scripts/run-eval.js +141 -141
  139. package/scripts/tui/componentes/selector-multi.js +189 -0
  140. package/scripts/tui/componentes/selector-unico.js +158 -0
  141. package/scripts/tui/ejecutores.js +375 -0
  142. package/scripts/tui/index.js +162 -0
  143. package/scripts/tui/lib/colores.js +129 -0
  144. package/scripts/tui/lib/render.js +264 -0
  145. package/scripts/tui/lib/teclas.js +113 -0
  146. package/scripts/tui/pantallas/inspect.js +173 -0
  147. package/scripts/tui/pantallas/install-wizard.js +334 -0
  148. package/scripts/tui/pantallas/menu-principal.js +52 -0
  149. package/scripts/tui/pantallas/progreso.js +274 -0
  150. package/scripts/tui/pantallas/resumen.js +132 -0
  151. package/scripts/tui/pantallas/uninstall-wizard.js +208 -0
  152. package/scripts/tui/pantallas/update-wizard.js +232 -0
  153. package/scripts/tui/pantallas/welcome.js +187 -0
  154. package/scripts/validar-userland-vacio.js +110 -110
  155. package/scripts/verificar-docs-vs-codigo.js +425 -0
@@ -0,0 +1,341 @@
1
+ ---
2
+ name: nemesis-redistribuir
3
+ description: >
4
+ Protocolo de redistribución del scope cuando una auditoría nemesis supera
5
+ los umbrales que saturan la context window del agente evaluator (>1500 LOC
6
+ o >5 archivos en módulos distintos). Divide el scope en sub-scopes coherentes,
7
+ ejecuta el evaluator una vez por sub-scope con ventana fresca, y consolida
8
+ los reportes parciales en un veredicto unificado. Cargar desde el comando
9
+ /swl:nemesis cuando la fase 0 detecta scope grande, antes de invocar al
10
+ agente nemesis-auditor-swl. NO cargar para scope pequeño — el bucle iterativo
11
+ Feynman+State del agente es más profundo que la redistribución.
12
+ ---
13
+
14
+ # Redistribución de scope para Nemesis
15
+
16
+ Este skill implementa el patrón "divide & consolidate" cuando una auditoría
17
+ nemesis no cabe en una sola invocación del agente evaluator. El bug que
18
+ originó esta solución es el incidente SIGM 2026-05-16 documentado en
19
+ ADR-0021: el agente saturó Sonnet 4.6 (200K context) en la pasada 4-5 de 6
20
+ con scope de ~3170 LOC.
21
+
22
+ El skill **NO sustituye** al protocolo Feynman+State del agente. Lo aplica
23
+ en porciones coherentes del codebase con context fresca por porción.
24
+
25
+ ---
26
+
27
+ ## Cuándo cargar
28
+
29
+ - El comando `/swl:nemesis` en su Fase 0 detectó que el scope supera el
30
+ umbral de redistribución.
31
+ - El usuario invocó explícitamente `/swl:nemesis --redistribuir` (forzado).
32
+ - Un componente downstream (orquestador, hook de telemetría) necesita
33
+ conocer el formato del plan de redistribución para coordinar.
34
+
35
+ ## Cuándo NO cargar
36
+
37
+ - Scope ≤ 1500 LOC y ≤ 5 archivos: el bucle iterativo normal del agente
38
+ evaluator cabe sin problema. Redistribuir aquí degrada profundidad.
39
+ - Auditoría dirigida con `--modulo` ya acotado por el usuario: respetar la
40
+ decisión explícita. NO sobre-dividir lo que el usuario ya delimitó.
41
+ - Modo `--pass1` o `--pass2` sin loop: una sola pasada cabe en cualquier
42
+ contexto razonable. La redistribución agregaría overhead sin ganancia.
43
+
44
+ ---
45
+
46
+ ## Umbrales canónicos
47
+
48
+ | Métrica | Umbral | Justificación |
49
+ |---------|--------|---------------|
50
+ | LOC totales del scope | > 1500 | El bucle Feynman+State con feed-forward llega a ~3-4× del scope en tokens hacia la pasada 4. 1500 LOC × 4 ≈ 80K tokens cabe; >1500 LOC tiende a saturar 200K en pasadas finales. |
51
+ | Archivos en módulos distintos | > 5 | Cada módulo introduce vocabulario y contratos propios; >5 dominios distintos hacen que el agente pierda foco entre pasadas. |
52
+ | Profundidad de directorios | > 4 niveles desde root | Heurística secundaria — codebases profundos suelen tener acoplamiento implícito que el agente no captura en una sola invocación. |
53
+
54
+ **Activar redistribución si CUALQUIERA de los tres umbrales se supera.** No
55
+ requerir los tres simultáneamente. Es defensa conservadora.
56
+
57
+ Los umbrales son heurísticos iniciales basados en el incidente SIGM. La
58
+ auto-evolución del skill ajustará valores con telemetría real.
59
+
60
+ ---
61
+
62
+ ## Protocolo de redistribución
63
+
64
+ ### Fase 1 — Análisis del scope
65
+
66
+ ```bash
67
+ # Contar LOC reales (excluyendo blank lines y comentarios single-line)
68
+ find <scope> -type f \( -name "*.py" -o -name "*.ts" -o -name "*.go" -o -name "*.rs" -o -name "*.java" -o -name "*.cs" -o -name "*.sql" \) | \
69
+ xargs grep -cv "^[[:space:]]*$\|^[[:space:]]*#\|^[[:space:]]*//" | \
70
+ awk -F: '{sum += $2} END {print sum}'
71
+
72
+ # Listar archivos por módulo (primer subdirectorio bajo backend/, frontend/, database/, etc.)
73
+ find <scope> -type f -name "*.py" | awk -F/ '{print $1"/"$2"/"$3}' | sort -u
74
+ ```
75
+
76
+ Producir un análisis estructurado:
77
+
78
+ ```json
79
+ {
80
+ "loc_total": 3170,
81
+ "archivos": 14,
82
+ "modulos": [
83
+ { "ruta": "backend/app/auth/", "archivos": 5, "loc": 627 },
84
+ { "ruta": "backend/app/db/", "archivos": 1, "loc": 345 },
85
+ { "ruta": "database/schemas/", "archivos": 3, "loc": 1057 },
86
+ { "ruta": "database/policies/", "archivos": 4, "loc": 775 },
87
+ { "ruta": "database/functions/", "archivos": 1, "loc": 166 }
88
+ ],
89
+ "supera_umbrales": ["loc_total", "archivos_modulos_distintos"]
90
+ }
91
+ ```
92
+
93
+ ### Fase 2 — División en sub-scopes coherentes
94
+
95
+ Reglas de división, en orden de prioridad:
96
+
97
+ 1. **Por capa arquitectónica** cuando aplica: separar `backend/`, `frontend/`,
98
+ `database/`, `mobile/`. Estas capas tienen vocabularios diferentes y el
99
+ agente trabaja mejor con una a la vez.
100
+
101
+ 2. **Por dominio de negocio** dentro de una capa: si `backend/app/` tiene
102
+ `auth/`, `pagos/`, `usuarios/` y cada uno excede 500 LOC, dividir por
103
+ dominio. Los acoplamientos cross-dominio son input para una pasada de
104
+ consolidación posterior, no para la pasada inicial.
105
+
106
+ 3. **Por archivo de tamaño excepcional**: si un solo archivo supera 1000 LOC
107
+ (caso típico de `repository.py` o `schema.sql` monolíticos), tratarlo
108
+ como sub-scope independiente. El agente puede dedicarle una pasada
109
+ completa sin distracción.
110
+
111
+ 4. **Por relación contractual**: schema SQL + funciones PL/pgSQL + policies
112
+ RLS de un mismo dominio van juntas (no se divide schema de su policy
113
+ correspondiente — el agente perdería la correlación).
114
+
115
+ Plan resultante en `.planning/audit/nemesis-plan.json`:
116
+
117
+ ```json
118
+ {
119
+ "modo": "redistribuido",
120
+ "fecha_generacion": "2026-05-16T20:00:00Z",
121
+ "scope_original": ["backend/app/auth/", "database/schemas/002-schema-usuario.sql", "database/policies/"],
122
+ "sub_scopes": [
123
+ {
124
+ "id": "sub-1-auth-backend",
125
+ "archivos": ["backend/app/auth/router.py", "backend/app/auth/service.py", "backend/app/auth/dependencies.py", "backend/app/auth/schemas.py"],
126
+ "loc": 627,
127
+ "dominio": "Autenticación backend Python",
128
+ "orden_ejecucion": 1
129
+ },
130
+ {
131
+ "id": "sub-2-auth-schema",
132
+ "archivos": ["database/schemas/002-schema-usuario.sql", "database/schemas/010-schema-token-revocacion.sql"],
133
+ "loc": 955,
134
+ "dominio": "Schema SQL usuario + tokens",
135
+ "orden_ejecucion": 2
136
+ },
137
+ {
138
+ "id": "sub-3-rls-policies",
139
+ "archivos": ["database/policies/rls.sql", "database/policies/rls-fase5.sql"],
140
+ "loc": 499,
141
+ "dominio": "Row Level Security policies",
142
+ "orden_ejecucion": 3
143
+ }
144
+ ],
145
+ "consolidacion_requerida": true,
146
+ "razon_redistribuir": "loc_total=3170 (>1500) + archivos_modulos_distintos=5 (>5)"
147
+ }
148
+ ```
149
+
150
+ ### Fase 3 — Ejecución secuencial de sub-scopes
151
+
152
+ El comando `/swl:nemesis` invoca al agente `nemesis-auditor-swl` una vez por
153
+ sub-scope, en el orden declarado en `nemesis-plan.json`. Cada invocación:
154
+
155
+ - Recibe scope acotado al sub-scope correspondiente.
156
+ - Recibe **`sub_id`** (el campo `id` del sub-scope en el plan, ej: `"sub-1-auth-backend"`).
157
+ El agente USA `sub_id` para construir la ruta de output. **Sin `sub_id`, todos
158
+ los sub-scopes escribirían al mismo `iter-N/` y se sobreescribirían entre sí.**
159
+ - Recibe `iter` (1, 2 o 3 según iteración del loop).
160
+ - Produce `.planning/audit/findings/iter-N/<sub_id>/evaluacion.json` + reporte markdown.
161
+ - Es invocación independiente con context window fresca (no acumula del
162
+ sub-scope anterior).
163
+
164
+ **Contrato del comando ↔ agente**:
165
+
166
+ ```
167
+ invocar(nemesis-auditor-swl,
168
+ scope=sub_scope.archivos,
169
+ iter=<1..3>,
170
+ sub_id=sub_scope.id)
171
+ ```
172
+
173
+ El agente DEBE respetar `sub_id` al escribir. Si recibe `null` o `undefined`,
174
+ opera en modo monolítico (escribe a `iter-N/` sin subdirectorio).
175
+
176
+ **Importante**: ejecutar **secuencialmente**, no en paralelo. La razón:
177
+
178
+ - Permite que el agente del sub-scope N+1 cargue `Skill("memoria-busqueda")`
179
+ y consulte los hallazgos del sub-scope N para detectar correlaciones.
180
+ - Evita race conditions sobre `.planning/audit/findings/`.
181
+ - Suaviza el costo de tokens (no se acumulan N invocaciones simultáneas).
182
+
183
+ Si el costo de secuencial es prohibitivo, considerar paralelo solo cuando
184
+ los sub-scopes son **arquitectónicamente independientes** (capas separadas
185
+ sin acoplamiento esperado).
186
+
187
+ ### Fase 4 — Consolidación
188
+
189
+ Tras completar todos los sub-scopes, el comando genera un `evaluacion.json`
190
+ consolidado en `.planning/audit/findings/iter-N/evaluacion.json`:
191
+
192
+ ```json
193
+ {
194
+ "schema_version": "1.0.0",
195
+ "metadata": {
196
+ "iteracion": 1,
197
+ "modo": "redistribuido",
198
+ "sub_scopes_consolidados": ["sub-1-auth-backend", "sub-2-auth-schema", "sub-3-rls-policies"]
199
+ },
200
+ "veredicto": {
201
+ "status": "NEEDS_IMPROVEMENT",
202
+ "puede_remediar_automaticamente": true
203
+ },
204
+ "hallazgos": [
205
+ /* concatenación de hallazgos de todos los sub-scopes, con prefijo de origen */
206
+ /* ej: hallazgos del sub-1 tienen id "S1-F-04", del sub-2 tienen "S2-F-04" */
207
+ ],
208
+ "correlaciones_cross_subscope": [
209
+ {
210
+ "descripcion": "F-04 (sub-1, backend no incrementa intentos_fallidos) correlaciona con S2-X (schema declara la columna). Ambos hallazgos comparten causa raíz.",
211
+ "hallazgos_ligados": ["S1-F-04", "S2-X"],
212
+ "accion_unificada": "Una sola implementación cierra ambos."
213
+ }
214
+ ]
215
+ }
216
+ ```
217
+
218
+ La consolidación es responsabilidad del comando, **no** del agente. El skill
219
+ provee el formato; el comando lo materializa con datos reales de los sub-scopes.
220
+
221
+ ### Detección de correlaciones cross-subscope
222
+
223
+ Tras consolidar, el comando puede invocar al agente `nemesis-auditor-swl` con
224
+ **scope mínimo** (~50 LOC seleccionados estratégicamente) y skills
225
+ `memoria-busqueda` + el reporte consolidado como input, para que detecte
226
+ correlaciones cross-subscope que ninguna pasada individual encontró.
227
+
228
+ Esta "pasada de consolidación" es opcional. Se activa cuando:
229
+
230
+ - Hay 2+ sub-scopes con hallazgos.
231
+ - Los sub-scopes comparten al menos un identificador (tabla, función, módulo
232
+ importado) — detectable con grep simple.
233
+ - El presupuesto de tokens lo permite.
234
+
235
+ ---
236
+
237
+ ## Formato del plan persistido
238
+
239
+ `.planning/audit/nemesis-plan.json` — schema:
240
+
241
+ ```typescript
242
+ interface NemesisPlan {
243
+ schema_version: "1.0.0";
244
+ modo: "redistribuido";
245
+ fecha_generacion: string; // ISO 8601
246
+ scope_original: string[]; // paths
247
+ sub_scopes: SubScope[];
248
+ consolidacion_requerida: boolean;
249
+ razon_redistribuir: string; // formato "metrica1=valor1 (>umbral1) + ..."
250
+ }
251
+
252
+ interface SubScope {
253
+ id: string; // "sub-N-<dominio-kebab>"
254
+ archivos: string[]; // paths relativos
255
+ loc: number;
256
+ dominio: string; // descripción humana
257
+ orden_ejecucion: number; // 1-indexed
258
+ consolidacion_inputs?: string[]; // ids de sub-scopes cuyos hallazgos
259
+ // este sub-scope debe considerar como contexto
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Reglas estrictas
266
+
267
+ ### Trazabilidad
268
+
269
+ Cada hallazgo del reporte consolidado DEBE incluir prefijo del sub-scope de
270
+ origen (`S1-F-04`, `S2-F-12`). Permite al implementador:
271
+
272
+ - Localizar el reporte parcial original.
273
+ - Reproducir el sub-scope con `/swl:nemesis --pass1 --modulo <archivos del sub-scope>`.
274
+ - Auditar qué porción del codebase produjo qué hallazgos.
275
+
276
+ ### Registro en nudges.jsonl
277
+
278
+ El comando DEBE registrar la activación del modo redistribuido en
279
+ `.planning/nudges.jsonl`:
280
+
281
+ ```json
282
+ {
283
+ "timestamp": "2026-05-16T20:00:00Z",
284
+ "tipo": "nemesis-redistribuido",
285
+ "mutation_category": "scope-adaptation",
286
+ "risk_level": "low",
287
+ "razon": "loc_total=3170 (>1500)",
288
+ "sub_scopes": 3,
289
+ "archivo_plan": ".planning/audit/nemesis-plan.json"
290
+ }
291
+ ```
292
+
293
+ Esto alimenta la telemetría de auto-evolución (regla `memoria-consolidada.md
294
+ § Estado del sistema y observabilidad`) y permite ajustar umbrales con datos
295
+ reales.
296
+
297
+ ### Idempotencia
298
+
299
+ Si `.planning/audit/nemesis-plan.json` ya existe y `--continue` fue pasado al
300
+ comando, reutilizar el plan existente. NO regenerar — la división puede
301
+ diferir si el filesystem cambió entre invocaciones y eso rompe la trazabilidad.
302
+
303
+ Para forzar regeneración del plan: `/swl:nemesis --redistribuir --reset-plan`.
304
+
305
+ ---
306
+
307
+ ## Anti-patrones
308
+
309
+ - **Dividir scope ya pequeño**: aplicar redistribución a < 1500 LOC introduce
310
+ overhead sin ganancia. Verificar umbrales antes de cargar este skill.
311
+
312
+ - **Dividir por número de archivos sin coherencia**: 10 archivos del mismo
313
+ módulo NO requieren división — el agente puede manejarlos juntos. El criterio
314
+ es **dominios diferentes**, no archivos totales.
315
+
316
+ - **Paralelizar sub-scopes con acoplamiento implícito**: ejecutar `auth/` y
317
+ `db/tenant.py` en paralelo pierde la correlación que el agente vería en
318
+ ejecución secuencial. Solo paralelo cuando los sub-scopes son
319
+ arquitectónicamente independientes.
320
+
321
+ - **Olvidar la pasada de consolidación**: si la auditoría es de un sistema
322
+ con alto acoplamiento entre módulos (auth + RLS + tokens), omitir la
323
+ consolidación deja hallazgos cross-módulo sin detectar. Tablar la decisión
324
+ en el plan.
325
+
326
+ - **Trazabilidad perdida**: emitir hallazgos consolidados sin prefijo `SN-`.
327
+ Cuando el implementador quiera reproducir un hallazgo específico, no sabrá
328
+ qué sub-scope re-auditar.
329
+
330
+ ---
331
+
332
+ ## Referencias
333
+
334
+ - ADR-0021: `nemesis-evaluator-optimizer` — define el modo redistribuido como
335
+ parte del patrón completo.
336
+ - `reglas/analisis-previo-tareas-grandes.md`: aplica el mismo principio
337
+ "dividir antes de implementar" pero a auditoría en lugar de implementación.
338
+ - `reglas/seguridad-agentes.md § Anti-fallback silencioso`: justifica registrar
339
+ la activación de redistribución como nudge explícito, no como decisión
340
+ silenciosa.
341
+ - Incidente SIGM 2026-05-16: caso real que motivó los umbrales canónicos.
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: node-experto
3
3
  description: Node.js y TypeScript backend moderno. Cubre patterns de Express/Fastify/NestJS, error handling middleware, streams y buffers, worker threads y clustering, Prisma/Drizzle ORM, validación con Zod, graceful shutdown y anti-patrones críticos como callback hell y event loop blocking.
4
- version: "1.0.1"
4
+ version: "1.0.2"
5
5
  evolved: true
6
- evolved-from: "1.0.0"
7
- evolved-at: "2026-05-15"
6
+ evolved-from: "1.0.1"
7
+ evolved-at: "2026-05-16"
8
8
  evolved-by: "aprender"
9
- evolved-note: "Gotcha req.destroy() vs req.pause() en HTTP nativos (PR #13). + CRLF regex breakage en Windows + serializar TOML con literal '''...''' (Sub-fase 11/11.5 v1.5.1)."
9
+ evolved-note: "v1.0.1: gotcha req.destroy() + CRLF + TOML literal. v1.0.2: gotchas `undefined<N` (NaN comparison), first-wins vs deep merge en config (PR #27 #30 v1.5.2), `assert.notMatch` no existe."
10
10
  herramientasPermitidas: [Read, Write, Glob]
11
11
  exclusiones:
12
12
  - "No cargar para aplicaciones Next.js con App Router — Next.js tiene patrones de Server Components, SSR y caché que difieren del backend Node puro; cargar `nextjs-experto`."
@@ -121,6 +121,9 @@ export class NotFoundError extends AppError {
121
121
  | `promise.then()` sin `.catch()` | asyncHandler o try/catch |
122
122
  | `require()` dinámico en hot path | Importar al inicio |
123
123
  | Operaciones síncronas de fs en requests | `fs.promises.*` |
124
+ | **Comparación `undefined < N`** evalúa `false` silenciosamente (NaN) | `(x \|\| 0) < N` con fallback explícito |
125
+ | **First-wins en config merging** cuando el consumidor hace deep merge | Replicar deep merge del consumidor (override por key, env mergeado por sub-clave) |
126
+ | **`assert.notMatch`** no existe en `node:assert` | Usar `assert.doesNotMatch` (con minúscula en "Match") |
124
127
 
125
128
  ```typescript
126
129
  // MAL: bloquea el event loop
@@ -130,6 +133,93 @@ const hash = crypto.pbkdf2Sync(pass, salt, 100000, 64, 'sha512');
130
133
  const hash = await pool.run({ pass, salt });
131
134
  ```
132
135
 
136
+ ### Gotcha: `undefined < N` evalúa a false (NaN comparison)
137
+
138
+ ```javascript
139
+ // MAL — el branch nunca dispara porque `notificado` puede ser undefined
140
+ const data = JSON.parse(readFileSync(flagPath, 'utf8'));
141
+ if (data.hayNueva && data.notificado < 2) {
142
+ emitirAviso();
143
+ data.notificado = (data.notificado || 0) + 1; // ← inconsistente: defensive aquí pero no arriba
144
+ writeFileSync(flagPath, JSON.stringify(data));
145
+ }
146
+
147
+ // BIEN — defense-in-depth con fallback explícito en ambos sitios
148
+ const data = JSON.parse(readFileSync(flagPath, 'utf8'));
149
+ const actual = data.notificado || 0; // fallback explícito al leer
150
+ if (data.hayNueva && actual < 2) {
151
+ emitirAviso();
152
+ data.notificado = actual + 1;
153
+ writeFileSync(flagPath, JSON.stringify(data));
154
+ }
155
+
156
+ // MEJOR aún — inicializar el campo al escribir para que nunca sea undefined
157
+ function registrarCheck(local, remota, hayNueva) {
158
+ writeFileSync(flagPath, JSON.stringify({
159
+ timestamp: Date.now(),
160
+ local,
161
+ remota,
162
+ hayNueva,
163
+ notificado: 0, // ← inicialización explícita
164
+ }));
165
+ }
166
+ ```
167
+
168
+ **Por qué importa**: bugs de comparación con `undefined` son **silenciosos** (no
169
+ tiran error). El código se ve sintácticamente correcto y los tests sin
170
+ escenario adversarial pasan. Detectados típicamente solo cuando un usuario
171
+ reporta que "el aviso nunca aparece" — investigación cuesta horas.
172
+
173
+ ### Gotcha: first-wins vs deep merge en config
174
+
175
+ Cuando un sistema carga config desde múltiples archivos (`base.json` +
176
+ `overrides.json` + `local.json`) y el **consumidor** del config hace deep
177
+ merge (ejemplo: Claude Code con `settings.json` + `settings.local.json`), el
178
+ loader interno **DEBE** replicar la misma semántica:
179
+
180
+ ```javascript
181
+ // MAL — first-wins descarta info silenciosamente
182
+ function cargarConfig(paths) {
183
+ for (const p of paths) {
184
+ if (fs.existsSync(p)) {
185
+ const data = JSON.parse(fs.readFileSync(p, 'utf8'));
186
+ if (data.servers && Object.keys(data.servers).length) {
187
+ return data.servers; // ← primer archivo con servers !== {} gana, resto invisible
188
+ }
189
+ }
190
+ }
191
+ return {};
192
+ }
193
+
194
+ // BIEN — deep merge replicando el consumidor canónico
195
+ function cargarConfig(paths) {
196
+ // Orden: base → override por key. Último gana en escalares;
197
+ // merge profundo en sub-objects (env, etc.).
198
+ const merged = {};
199
+ for (const p of paths) {
200
+ if (!fs.existsSync(p)) continue;
201
+ const data = JSON.parse(fs.readFileSync(p, 'utf8'));
202
+ for (const [nombre, cfg] of Object.entries(data.servers || {})) {
203
+ if (merged[nombre]) {
204
+ merged[nombre] = {
205
+ ...merged[nombre],
206
+ ...cfg,
207
+ env: { ...(merged[nombre].env || {}), ...(cfg.env || {}) },
208
+ };
209
+ } else {
210
+ merged[nombre] = cfg;
211
+ }
212
+ }
213
+ }
214
+ return merged;
215
+ }
216
+ ```
217
+
218
+ **Regla**: si el archivo de config tiene múltiples capas que el consumidor
219
+ mergea, el loader interno NUNCA usa first-wins. Replicar la semántica del
220
+ consumidor evita confusiones donde override parcial (solo `env` en local)
221
+ "rompe" la config completa.
222
+
133
223
  ---
134
224
 
135
225
  ## Checklist Node.js