@saulwade/swl-ses 1.5.0 → 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.
- package/CLAUDE.md +19 -2
- package/README.md +561 -561
- package/agentes/arquitecto-swl.md +33 -1
- package/agentes/nemesis-auditor-swl.md +59 -19
- package/bin/swl-mcp-server.js +214 -214
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/nemesis.md +230 -56
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/SKILL.md +171 -171
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +299 -299
- package/habilidades/infra-github-actions/SKILL.md +166 -166
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/manejo-errores/.evolved.json +8 -8
- package/habilidades/meta-skills-estandar/SKILL.md +225 -1
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/nemesis-evaluacion-json/SKILL.md +266 -0
- package/habilidades/nemesis-redistribuir/SKILL.md +341 -0
- package/habilidades/node-experto/SKILL.md +105 -4
- package/habilidades/patrones-python/SKILL.md +229 -229
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/planear-fase/SKILL.md +319 -319
- package/habilidades/protocolo-revision-swl/SKILL.md +350 -276
- package/habilidades/release-semver/.evolved.json +8 -8
- package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/tdd-workflow/SKILL.md +150 -4
- package/habilidades/testing-python/SKILL.md +340 -340
- package/habilidades/verificar-trabajo/SKILL.md +8 -3
- package/habilidades/web-fetcher-routing/SKILL.md +75 -75
- package/hooks/check-update.js +31 -3
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/registro-turnos.js +209 -209
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-memoria-hook.js +218 -218
- package/instintos/prompt-appendices.yaml +57 -57
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/modulos.json +1324 -1321
- package/manifiestos/skills-lock.json +1114 -1114
- package/package.json +2 -2
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +353 -351
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/arreglar-al-detectar.md +147 -147
- package/reglas/fragmentos-compartidos.md +152 -152
- package/reglas/harness-claude-code.md +213 -213
- package/reglas/registro-componentes-nuevos.md +192 -0
- package/reglas/usar-context7.md +226 -226
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/actualizar.js +110 -1
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/derivar-feature-list.js +489 -489
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +58 -4
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-inventario.js +420 -420
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/mcp_config.py +127 -0
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/transformadores/codex.js +375 -375
- package/scripts/lib/transformadores/cursor.js +359 -359
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-orchestrator.py +8 -18
- package/scripts/mcp-pool-manager.py +12 -23
- package/scripts/mcp-server/README.md +170 -170
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/mcp-server/telemetry.js +78 -78
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +201 -201
- package/scripts/publicar.js +511 -511
- package/scripts/run-eval.js +141 -141
- 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.
|
|
4
|
+
version: "1.0.2"
|
|
5
5
|
evolved: true
|
|
6
|
-
evolved-from: "1.0.
|
|
7
|
-
evolved-at: "2026-05-
|
|
6
|
+
evolved-from: "1.0.1"
|
|
7
|
+
evolved-at: "2026-05-16"
|
|
8
8
|
evolved-by: "aprender"
|
|
9
|
-
evolved-note: "
|
|
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
|
|
@@ -272,6 +362,17 @@ if (require.main === module) {
|
|
|
272
362
|
|
|
273
363
|
**`req.destroy()` en un handler `http` cierra el socket antes de poder enviar la respuesta**: cuando se aborta la lectura del body (por ejemplo al exceder `maxPayloadBytes`), llamar `req.destroy()` en el listener `data` cierra el socket inmediatamente. El subsiguiente `res.writeHead(413); res.end()` del handler falla porque ya no hay socket — el cliente recibe `ECONNRESET` en lugar de 413. Causa: `destroy()` no espera al flush de la respuesta pendiente; equivale a un `RST` TCP. Fix: usar `req.pause()` + un flag `abortado = true` + rechazar la promesa de lectura; dejar que el handler envíe `res.writeHead(413); res.end(...)` normalmente. Node cierra el socket por sí solo tras `res.end()`. Aplica a servidores HTTP nativos sin Express/Fastify, donde el control del body es manual.
|
|
274
364
|
|
|
365
|
+
**Regex multi-line con `\n` falla con archivos CRLF de Windows**: un parser que usa `/^---\n([\s\S]*?)\n---\n/` para detectar frontmatter YAML rompe con archivos commiteados en Windows (donde git aplica `core.autocrlf=true` y convierte LF↔CRLF al checkout). Caso real (Sub-fase 11.5 v1.5.1, swl-ses): `parsearFrontmatter` en `scripts/lib/transformadores/base.js` devolvía `frontmatter:{}` y `cuerpo:<archivo entero>` para los 60 agentes `agentes/*.md` cuando se ejecutaba en Windows. Resultado downstream: `transformarAgente` de Codex generaba TOML con `description = ""` y `developer_instructions` con el frontmatter duplicado dentro del body. Bug latente que solo aparece al ejecutar contra archivos reales con CRLF. Fix: normalizar antes del match — `const norm = String(contenido).replace(/\r\n/g, '\n').replace(/\r/g, '\n');` y aplicar regex sobre `norm`. Siempre asumir que cualquier archivo leído de filesystem en Windows puede tener CRLF, incluso si git lo "almacena" como LF. Aplica a TODA función con regex multi-line que reciba contenido de filesystem.
|
|
366
|
+
|
|
367
|
+
**Serializar TOML zero-deps: preferir `'''literal'''` sobre `"""basic"""` para multi-line con backslashes**: TOML soporta dos sintaxis multi-line — basic `"""..."""` que procesa escapes (`\n`, `\t`, `\\`) y literal `'''...'''` que preserva el contenido tal cual. Caso real (Sub-fase 11 v1.5.1, swl-ses): al serializar el cuerpo Markdown de agentes SWL (con regex `\n`, paths Windows con `\`, etc.) a `developer_instructions` de `~/.codex/agents/*.toml`, usar basic `"""..."""` exigía escapar cada `\` a `\\` (laborioso y propenso a bugs). Solución: usar literal `'''...'''` por default (preserva Markdown crudo) y caer a basic `"""..."""` con escape SOLO si el cuerpo contiene `'''` literal (raro). Helper en `scripts/lib/transformadores/codex.js`:
|
|
368
|
+
```js
|
|
369
|
+
_tomlMultiline(s) {
|
|
370
|
+
if (!s.includes("'''")) return `'''\n${s}\n'''`;
|
|
371
|
+
return `"""\n${s.replace(/\\/g, '\\\\').replace(/"""/g, '\\"""')}\n"""`;
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
Aplica a cualquier serialización TOML manual sin librería (`@iarna/toml`, etc.) donde el contenido es texto rico con caracteres especiales.
|
|
375
|
+
|
|
275
376
|
```js
|
|
276
377
|
// MAL — ECONNRESET en cliente, nunca recibe 413
|
|
277
378
|
req.on('data', chunk => {
|