@saulwade/swl-ses 1.5.1 → 1.5.2

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 (133) hide show
  1. package/CLAUDE.md +225 -209
  2. package/README.md +561 -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/comandos/swl/.evolved.json +22 -22
  7. package/comandos/swl/contribuir.md +233 -233
  8. package/comandos/swl/nemesis.md +230 -56
  9. package/gateway/lib/event-channel.js +191 -191
  10. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  11. package/habilidades/benchmark-memoria/SKILL.md +186 -186
  12. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  13. package/habilidades/doubt-driven-review/SKILL.md +171 -171
  14. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  15. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
  16. package/habilidades/eval-framework/SKILL.md +212 -212
  17. package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
  18. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  19. package/habilidades/harness-claude-code/SKILL.md +299 -299
  20. package/habilidades/infra-github-actions/SKILL.md +166 -166
  21. package/habilidades/legacy-code-rescue/SKILL.md +267 -267
  22. package/habilidades/manejo-errores/.evolved.json +8 -8
  23. package/habilidades/meta-skills-estandar/SKILL.md +207 -4
  24. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  25. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
  26. package/habilidades/nemesis-evaluacion-json/SKILL.md +266 -0
  27. package/habilidades/nemesis-redistribuir/SKILL.md +341 -0
  28. package/habilidades/node-experto/SKILL.md +94 -4
  29. package/habilidades/patrones-python/SKILL.md +229 -229
  30. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  31. package/habilidades/planear-fase/SKILL.md +319 -319
  32. package/habilidades/protocolo-revision-swl/SKILL.md +350 -276
  33. package/habilidades/release-semver/.evolved.json +8 -8
  34. package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
  35. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  36. package/habilidades/tdd-workflow/SKILL.md +121 -4
  37. package/habilidades/testing-python/SKILL.md +340 -340
  38. package/habilidades/web-fetcher-routing/SKILL.md +75 -75
  39. package/hooks/check-update.js +31 -3
  40. package/hooks/claudemd-bloat-detector.js +161 -161
  41. package/hooks/lib/agent-routing.js +107 -107
  42. package/hooks/lib/auto-consolidator.js +335 -335
  43. package/hooks/lib/error-classifier.js +308 -308
  44. package/hooks/lib/merkle-audit.js +96 -96
  45. package/hooks/lib/provenance-tracker.js +191 -191
  46. package/hooks/lib/rate-limit-tracker.js +253 -253
  47. package/hooks/lib/resource-quota.js +122 -122
  48. package/hooks/lib/retry-jitter.js +165 -165
  49. package/hooks/lib/security-net.js +201 -201
  50. package/hooks/lib/skill-auditor.js +588 -588
  51. package/hooks/lib/sync-status.js +228 -228
  52. package/hooks/lib/taint-tracker.js +107 -107
  53. package/hooks/lib/text-similarity.js +241 -241
  54. package/hooks/lib/toon-compressor.js +245 -245
  55. package/hooks/registro-turnos.js +209 -209
  56. package/hooks/sugerir-regenerar-inventario.js +170 -170
  57. package/hooks/validar-formato-post-subagente.js +140 -140
  58. package/hooks/validar-memoria-hook.js +218 -218
  59. package/instintos/prompt-appendices.yaml +57 -57
  60. package/manifiestos/agent-output-schemas.json +57 -57
  61. package/manifiestos/modulos.json +1324 -1321
  62. package/manifiestos/skills-lock.json +1114 -1114
  63. package/package.json +2 -2
  64. package/plantillas/auditor-veto-template.md +105 -105
  65. package/plantillas/github-workflows/README.md +47 -47
  66. package/plantillas/github-workflows/release-please.yml +44 -44
  67. package/plantillas/github-workflows/swl-ci.yml +107 -107
  68. package/plantillas/github-workflows/swl-security.yml +51 -51
  69. package/plugin.json +353 -351
  70. package/reglas/analisis-previo-tareas-grandes.md +172 -172
  71. package/reglas/arreglar-al-detectar.md +147 -147
  72. package/reglas/fragmentos-compartidos.md +152 -152
  73. package/reglas/harness-claude-code.md +213 -213
  74. package/reglas/registro-componentes-nuevos.md +192 -0
  75. package/reglas/usar-context7.md +226 -226
  76. package/schemas/diary-entry.schema.json +80 -80
  77. package/scripts/actualizar.js +110 -1
  78. package/scripts/audit-tools/audit-history.js +330 -330
  79. package/scripts/audit-tools/bundle-tracker.js +290 -290
  80. package/scripts/audit-tools/canary-monitor.js +352 -352
  81. package/scripts/audit-tools/code-profiler.js +605 -605
  82. package/scripts/audit-tools/dep-doctor.js +320 -320
  83. package/scripts/audit-tools/env-validator.js +206 -206
  84. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  85. package/scripts/audit-tools/lib/output.js +23 -23
  86. package/scripts/audit-tools/migration-checker.js +392 -392
  87. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  88. package/scripts/benchmark-memoria.js +167 -167
  89. package/scripts/configurar-branch-protection.js +418 -418
  90. package/scripts/derivar-feature-list.js +489 -489
  91. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  92. package/scripts/doctor.js +27 -0
  93. package/scripts/field-report.js +199 -199
  94. package/scripts/generar-checklists-consolidados.js +273 -273
  95. package/scripts/generar-inventario.js +420 -420
  96. package/scripts/generar-matriz-lenguajes.js +271 -271
  97. package/scripts/lib/artefactos-python.js +43 -43
  98. package/scripts/lib/benchmark-metrics.js +160 -160
  99. package/scripts/lib/budget-enforcer.js +252 -252
  100. package/scripts/lib/configurar-ci.js +380 -380
  101. package/scripts/lib/contadores-inventario.js +217 -217
  102. package/scripts/lib/detectar-stack-detallado.js +307 -307
  103. package/scripts/lib/diary-entry.js +234 -234
  104. package/scripts/lib/eval-metrics-store.js +218 -218
  105. package/scripts/lib/eval-quality.js +171 -171
  106. package/scripts/lib/eval-schemas.js +144 -144
  107. package/scripts/lib/eval-self-correct.js +106 -106
  108. package/scripts/lib/eval-validator.js +185 -185
  109. package/scripts/lib/expandir-targets.js +71 -71
  110. package/scripts/lib/jaccard-similarity.js +98 -98
  111. package/scripts/lib/longmemeval-runner.js +125 -125
  112. package/scripts/lib/mcp_config.py +127 -0
  113. package/scripts/lib/npm-version.js +261 -261
  114. package/scripts/lib/paquetes-conocidos.js +50 -50
  115. package/scripts/lib/prompt-builder.js +264 -264
  116. package/scripts/lib/rrf-fusion.js +175 -175
  117. package/scripts/lib/scoring-instintos.js +277 -277
  118. package/scripts/lib/semantic-search.js +252 -252
  119. package/scripts/lib/toml-merge.js +204 -204
  120. package/scripts/lib/transformadores/codex.js +375 -375
  121. package/scripts/lib/transformadores/cursor.js +359 -359
  122. package/scripts/limpiar-artefactos-python.js +131 -131
  123. package/scripts/mcp-orchestrator.py +8 -18
  124. package/scripts/mcp-pool-manager.py +12 -23
  125. package/scripts/mcp-server/README.md +170 -170
  126. package/scripts/mcp-server/auth.js +105 -105
  127. package/scripts/mcp-server/cache.js +106 -106
  128. package/scripts/mcp-server/telemetry.js +78 -78
  129. package/scripts/migrar-csv-a-array.js +168 -168
  130. package/scripts/migrar-fase-dominio.js +201 -201
  131. package/scripts/publicar.js +511 -511
  132. package/scripts/run-eval.js +141 -141
  133. package/scripts/validar-userland-vacio.js +110 -110
@@ -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