@saulwade/swl-ses 1.7.4 → 1.9.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.
- package/CLAUDE.md +196 -196
- package/README.md +579 -579
- package/agentes/auto-evolucion-swl.md +7 -7
- package/agentes/disenador-ui-swl.md +12 -0
- package/agentes/investigador-ux-swl.md +9 -0
- package/agentes/orquestador-swl.md +89 -1
- package/agentes/perfilador-usuario-swl.md +2 -2
- package/agentes/revisor-codigo-swl.md +34 -10
- package/agentes/revisor-seguridad-swl.md +7 -0
- package/agentes/tdd-qa-swl.md +23 -2
- package/agentes/ux-disenador-swl.md +6 -0
- package/comandos/swl/autoresearch.md +102 -6
- package/comandos/swl/evaluar-skill.md +1 -1
- package/comandos/swl/evolucion-estado.md +5 -5
- package/comandos/swl/evolucionar.md +2 -2
- package/comandos/swl/inbox.md +1 -1
- package/comandos/swl/metricas.md +34 -0
- package/comandos/swl/nemesis.md +42 -1
- package/comandos/swl/planear-fase.md +8 -0
- package/comandos/swl/predecir.md +139 -0
- package/comandos/swl/reflect-skills.md +2 -2
- package/comandos/swl/salud.md +1 -1
- package/comandos/swl/verificar.md +50 -7
- package/habilidades/ai-runtime-security/SKILL.md +2 -2
- package/habilidades/angular-moderno/SKILL.md +44 -1
- package/habilidades/auto-evolucion-protocolo/SKILL.md +2 -2
- package/habilidades/autoresearch/SKILL.md +15 -1
- package/habilidades/benchmark-memoria/SKILL.md +2 -2
- package/habilidades/calidad-mutation-testing/SKILL.md +170 -0
- package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
- package/habilidades/checklist-seguridad/SKILL.md +29 -1
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -0
- package/habilidades/css-moderno/SKILL.md +3 -1
- package/habilidades/drift-detection/SKILL.md +3 -3
- package/habilidades/eval-framework/SKILL.md +1 -1
- package/habilidades/fastapi-experto/SKILL.md +56 -5
- package/habilidades/guardrail-semantico/SKILL.md +4 -4
- package/habilidades/patrones-python/SKILL.md +8 -5
- package/habilidades/proceso-ddia-streaming/SKILL.md +4 -4
- package/habilidades/proceso-debate-adversarial/SKILL.md +164 -0
- package/habilidades/proceso-debate-adversarial/recursos/personas.md +105 -0
- package/habilidades/proceso-dynamic-workflows/SKILL.md +138 -0
- package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -0
- package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -0
- package/habilidades/swl-claudemd/SKILL.md +2 -2
- package/habilidades/tdd-workflow/SKILL.md +14 -1
- package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -0
- package/habilidades/testing-python/SKILL.md +1 -1
- package/habilidades/tracing-processor/SKILL.md +1 -1
- package/hooks/actualizar-perfil-usuario.js +2 -2
- package/hooks/aiisms-detector.js +2 -2
- package/hooks/auto-evolucion.js +1 -1
- package/hooks/captura-feedback-usuario.js +2 -2
- package/hooks/claudemd-bloat-detector.js +2 -2
- package/hooks/claudemd-duplicacion-detector.js +1 -1
- package/hooks/contexto-iteracion.js +144 -0
- package/hooks/guardrail-modelo.js +2 -2
- package/hooks/lib/loop-telemetry.js +321 -0
- package/hooks/lib/memory-search.js +1 -1
- package/hooks/lib/nudge-tracker.js +1 -1
- package/hooks/metricas-evolucion.js +3 -3
- package/hooks/notificacion-telegram.js +11 -3
- package/hooks/rotar-audit-auto.js +2 -2
- package/hooks/validar-formato-post-subagente.js +2 -2
- package/hooks/validar-intent-spec.js +1 -1
- package/hooks/validar-planning-paths.js +134 -0
- package/llms.txt +29 -0
- package/manifiestos/hooks-config.json +30 -12
- package/manifiestos/modulos.json +1358 -1351
- package/manifiestos/planning-paths.json +44 -0
- package/manifiestos/skills-lock.json +1275 -1254
- package/package.json +93 -92
- package/plugin.json +375 -372
- package/reglas/arquitectura.evolved.json +7 -0
- package/reglas/arquitectura.md +65 -0
- package/reglas/gobernanza.md +1 -1
- package/reglas/memoria-consolidada.md +7 -7
- package/reglas/seguridad.evolved.json +7 -0
- package/reglas/seguridad.md +144 -0
- package/reglas/sin-duplicacion-reglas-globales.md +1 -1
- package/scripts/auditar-agentes-gaps.js +1 -1
- package/scripts/auditar-cobertura-frameworks.js +2 -2
- package/scripts/auditar-skills-gaps.js +2 -2
- package/scripts/benchmark-memoria.js +3 -3
- package/scripts/generar-inventario.js +64 -1
- package/scripts/inferir-herramientas-permitidas.js +1 -1
- package/scripts/instalador.js +80 -2
- package/scripts/lib/dashboard-widgets.js +3 -3
- package/scripts/lib/drift-detector.js +3 -3
- package/scripts/lib/eval-metrics-store.js +3 -3
- package/scripts/lib/gitignore-manifest.js +3 -3
- package/scripts/mcp-server/README.md +1 -1
- package/scripts/mcp-server/telemetry.js +2 -2
- package/scripts/reflect-skills.js +4 -4
- package/scripts/rotar-audit-logs.js +2 -2
- package/scripts/run-skill-evals.js +2 -2
- package/scripts/smoke-test.js +24 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"evolved": true,
|
|
3
|
+
"evolved-from": "1.8.0",
|
|
4
|
+
"evolved-at": "2026-06-04",
|
|
5
|
+
"evolved-by": "evolucionar",
|
|
6
|
+
"evolved-note": "PE-010 sección nueva 'Tablas append-only — excepción documentada a audit columns nullable=False'. Patrón portable a logs estructurados, audit trails, event sourcing, telemetría. Origen: OIC v1.5 Slice 1 2026-06-04 (BitacoraError)."
|
|
7
|
+
}
|
package/reglas/arquitectura.md
CHANGED
|
@@ -274,6 +274,71 @@ Documentar en un ADR qué patrón se usa y por qué.
|
|
|
274
274
|
|
|
275
275
|
---
|
|
276
276
|
|
|
277
|
+
## Tablas append-only — excepción documentada a "audit columns nullable=False"
|
|
278
|
+
|
|
279
|
+
Algunas tablas son **append-only por diseño**: solo se crean filas (INSERT) y
|
|
280
|
+
eventualmente se eliminan por purga (DELETE). Nunca se actualizan. Ejemplos
|
|
281
|
+
típicos: logs de errores (`bitacora_error`, `error_log`), audit trails inmutables,
|
|
282
|
+
telemetría, métricas históricas, event sourcing event store, outbox pattern.
|
|
283
|
+
|
|
284
|
+
Para estas tablas la regla general del proyecto "`created_by`/`updated_by`
|
|
285
|
+
`nullable=False` en tablas transaccionales" **NO aplica**. Documentar la
|
|
286
|
+
excepción explícitamente:
|
|
287
|
+
|
|
288
|
+
1. En el **docstring de la clase modelo** (Python/Java/C#):
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
class BitacoraError(Base):
|
|
292
|
+
"""
|
|
293
|
+
Registro inmutable de un error backend o frontend.
|
|
294
|
+
|
|
295
|
+
Tabla append-only: solo se crean filas (INSERT) y se purgan por antigüedad
|
|
296
|
+
(DELETE). Nunca se actualizan. Esta invariante es fundamental para que el
|
|
297
|
+
log de errores sea un audit trail confiable.
|
|
298
|
+
|
|
299
|
+
Excepción a regla "audit columns nullable=False":
|
|
300
|
+
La convención del proyecto exige `created_by` y `updated_by` NOT NULL
|
|
301
|
+
en tablas transaccionales. BitacoraError queda exenta porque:
|
|
302
|
+
(a) Es un log del sistema, no una entidad de negocio modificable.
|
|
303
|
+
(b) No tiene `updated_at` ni `updated_by` — no hay concepto de
|
|
304
|
+
"última modificación" en una tabla append-only.
|
|
305
|
+
(c) `usuario_id` es nullable de forma intencional: los errores de
|
|
306
|
+
sesiones no autenticadas (401 antes del login) no tienen usuario.
|
|
307
|
+
"""
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
2. En el **comentario del DDL SQL**:
|
|
311
|
+
|
|
312
|
+
```sql
|
|
313
|
+
COMMENT ON TABLE bitacora_error IS
|
|
314
|
+
'Errores backend y frontend persistidos para diagnóstico. '
|
|
315
|
+
'Append-only: solo INSERT y DELETE (purga). '
|
|
316
|
+
'Sin updated_at/updated_by por diseño (excepción documentada en CONTEXTO).';
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
3. En el **GRANT** del rol de aplicación, omitir explícitamente `UPDATE`:
|
|
320
|
+
|
|
321
|
+
```sql
|
|
322
|
+
-- INSERT: para que el sistema cree entradas.
|
|
323
|
+
-- SELECT: para que el admin las consulte.
|
|
324
|
+
-- DELETE: para que el job de purga elimine antiguas.
|
|
325
|
+
-- NO UPDATE: la tabla es append-only por diseño.
|
|
326
|
+
GRANT SELECT, INSERT, DELETE ON bitacora_error TO ine_user;
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Criterios para considerar una tabla append-only legítima**:
|
|
330
|
+
|
|
331
|
+
- Toda fila representa un evento ocurrido en un momento específico (immutable in time).
|
|
332
|
+
- No hay caso de negocio que requiera modificar la fila después de creada.
|
|
333
|
+
- La única forma de "corregir" un dato erróneo es insertar una fila correctora
|
|
334
|
+
posterior (event sourcing) o purgar y re-insertar (telemetría).
|
|
335
|
+
- El borrado se hace por política de retención (antigüedad), no por revocación.
|
|
336
|
+
|
|
337
|
+
**Origen del patrón**: OIC v1.5 Slice 1 (2026-06-04). Patrón portable a cualquier
|
|
338
|
+
proyecto con logs estructurados, audit trails o event sourcing.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
277
342
|
## Split modular con compositor por herencia múltiple
|
|
278
343
|
|
|
279
344
|
Cuando un módulo backend (router, service, repository) supera ~1500 LOC en un
|
package/reglas/gobernanza.md
CHANGED
|
@@ -40,7 +40,7 @@ Los skills generados por `auto-evolucion-swl` o `/swl:evolucionar`:
|
|
|
40
40
|
`agentes/auto-evolucion-swl.md` sección "Gate G8". Origen ADR 0013
|
|
41
41
|
sección 3C.
|
|
42
42
|
- La promoción se registra en `.planning/AUDITORIA.md` con justificación
|
|
43
|
-
Y en `.planning/
|
|
43
|
+
Y en `.planning/evolution/evoluciones.jsonl` con evento
|
|
44
44
|
`tipo: "promocion-skill"` y `score`.
|
|
45
45
|
|
|
46
46
|
### Reglas nuevas obligatorias
|
|
@@ -15,7 +15,7 @@ regla clara, un mismo dato puede terminar en el lugar incorrecto o duplicado.
|
|
|
15
15
|
| 2 | **`.planning/APRENDIZAJES.md`** | Markdown con entradas `### [YYYY-MM-DD] Título` agrupadas por sección | Conocimiento del dominio (anti-patrones, patrones, decisiones, gotchas) |
|
|
16
16
|
| 3 | **`instintos/proyecto.yaml` / `global.yaml` / `perfil-usuario.yaml`** | YAML estructurado con confidence, scope, evidence_count | Patrones aprendidos inductivamente, con promoción/degradación |
|
|
17
17
|
| 4 | **`.planning/sessions/search-index.json`** | Índice FTS de sesiones pasadas | Búsqueda histórica de trabajo (qué se hizo, cuándo) |
|
|
18
|
-
| 5 | **`.planning/
|
|
18
|
+
| 5 | **`.planning/evolution/*`** | JSONL (nudges, agentes) + JSON (métricas, alertas, evoluciones) | Estado del ciclo de auto-evolución |
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -44,11 +44,11 @@ regla clara, un mismo dato puede terminar en el lugar incorrecto o duplicado.
|
|
|
44
44
|
|
|
45
45
|
| Tipo de dato | Canal correcto |
|
|
46
46
|
|---|---|
|
|
47
|
-
| Nudge emitido por un hook | 5 (`.planning/
|
|
48
|
-
| Evento de subagente terminado | 5 (`.planning/auto-
|
|
49
|
-
| Evolución aplicada/revertida a skill | 5 (`.planning/
|
|
50
|
-
| Alerta persistente (nudges ignorados) | 5 (`.planning/
|
|
51
|
-
| Métricas agregadas (health score) | 5 (`.planning/
|
|
47
|
+
| Nudge emitido por un hook | 5 (`.planning/evolution/nudges.jsonl`) |
|
|
48
|
+
| Evento de subagente terminado | 5 (`.planning/auto-evolution/agentes.jsonl`) |
|
|
49
|
+
| Evolución aplicada/revertida a skill | 5 (`.planning/evolution/evoluciones.jsonl`) |
|
|
50
|
+
| Alerta persistente (nudges ignorados) | 5 (`.planning/evolution/alertas-persistentes.json`) |
|
|
51
|
+
| Métricas agregadas (health score) | 5 (`.planning/evolution/metricas.json`) |
|
|
52
52
|
| Traza de ejecución de agente | `.planning/traces/` (OTLP-lite) |
|
|
53
53
|
| Audit trail inmutable | `.planning/audit.jsonl` + Merkle |
|
|
54
54
|
|
|
@@ -125,7 +125,7 @@ conocido, datos en canales deprecados.
|
|
|
125
125
|
|
|
126
126
|
- **Corrección del usuario escrita a APRENDIZAJES.md** → debe ir al perfil
|
|
127
127
|
- **Anti-patrón técnico escrito al perfil del usuario** → debe ir a APRENDIZAJES
|
|
128
|
-
- **Métrica persistida en APRENDIZAJES.md** → debe ir a `.planning/
|
|
128
|
+
- **Métrica persistida en APRENDIZAJES.md** → debe ir a `.planning/evolution/`
|
|
129
129
|
- **Secreto/credencial en cualquier canal** → bloqueo inmediato (`privacy-memoria`)
|
|
130
130
|
- **Dato duplicado en proyecto.yaml y global.yaml** → uno debe borrarse
|
|
131
131
|
(promover a global SOLO cuando confidence > 0.8 Y validado cross-proyecto)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"evolved": true,
|
|
3
|
+
"evolved-from": "1.8.0",
|
|
4
|
+
"evolved-at": "2026-06-04",
|
|
5
|
+
"evolved-by": "evolucionar",
|
|
6
|
+
"evolved-note": "PE-003 (verificación de flags antes de asumir servicios externos LDAP/Redis/S3) + PE-004 (sanitización PII centralizada en handlers) + PE-007 (generación de passwords legibles sin chars ambiguos). Origen: OIC v1.5 2026-06-04, bug histórico manuel.monteagudo + Slice 2 v1.5 Observabilidad."
|
|
7
|
+
}
|
package/reglas/seguridad.md
CHANGED
|
@@ -141,6 +141,147 @@ vulnerabilidad del OWASP Top 10. Las más frecuentes en este stack:
|
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
|
144
|
+
## Verificación obligatoria de flags de configuración antes de asumir servicios externos
|
|
145
|
+
|
|
146
|
+
Toda rama de código que asuma un servicio externo opcional (LDAP, Redis, S3, SMTP,
|
|
147
|
+
OIDC, Auth0, etc.) DEBE verificar el flag de configuración del proyecto ANTES de
|
|
148
|
+
tomar la decisión. Sin esta verificación, una configuración no esperada deja a los
|
|
149
|
+
usuarios atrapados en flujos rotos sin error visible.
|
|
150
|
+
|
|
151
|
+
**Anti-patrón** (caso real OIC 2026-06-04, dos bugs históricos en el mismo flujo):
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
# MAL — asume LDAP siempre habilitado
|
|
155
|
+
if rol == "ADMIN":
|
|
156
|
+
hashed = hash_password(body.password)
|
|
157
|
+
else:
|
|
158
|
+
# "Para usuarios LDAP, no se almacena contraseña funcional local."
|
|
159
|
+
hashed = hash_password(uuid.uuid4().hex)
|
|
160
|
+
# ↑ Si AUTH_LDAP_ENABLED=false (caso real), el usuario queda con un hash
|
|
161
|
+
# UUID dummy que ninguna password real puede igualar → 401 perpetuo.
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
# MAL — rechaza no-ADMIN sin verificar si LDAP existe siquiera
|
|
166
|
+
if rol != "ADMIN":
|
|
167
|
+
raise HTTPException(400, "Los usuarios no ADMIN gestionan su contraseña en LDAP")
|
|
168
|
+
# ↑ Sin LDAP habilitado los usuarios no tienen forma de cambiar password.
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Patrón correcto**:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# BIEN — siempre verifica el flag antes de asumir el servicio
|
|
175
|
+
if rol == "ADMIN":
|
|
176
|
+
hashed = hash_password(body.password)
|
|
177
|
+
else:
|
|
178
|
+
if body.password:
|
|
179
|
+
hashed = hash_password(body.password)
|
|
180
|
+
elif not settings.AUTH_LDAP_ENABLED:
|
|
181
|
+
# Sin LDAP: generar password funcional para que el usuario pueda entrar
|
|
182
|
+
temp = _generar_temp_password()
|
|
183
|
+
hashed = hash_password(temp)
|
|
184
|
+
# devolver `temp` en la respuesta del POST UNA SOLA VEZ
|
|
185
|
+
else:
|
|
186
|
+
# LDAP habilitado: dummy OK porque el usuario se autentica fuera
|
|
187
|
+
hashed = hash_password(uuid.uuid4().hex)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Detección temprana en code review**: cualquier comentario `"para LDAP"`,
|
|
191
|
+
`"para Redis"`, `"para S3"` debe acompañarse del check del flag correspondiente.
|
|
192
|
+
Si el comentario está solo, es bug latente — el código asume un mundo que el
|
|
193
|
+
deployment puede no tener.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Sanitización PII centralizada en handlers vs refactorear N sitios
|
|
198
|
+
|
|
199
|
+
Cuando un sistema persiste logs estructurados (audit trails, error logs,
|
|
200
|
+
telemetría) y existen N sitios donde el código emite `logger.error(..., extra={...})`
|
|
201
|
+
con potencial PII en los valores, **siempre preferir sanitización centralizada en
|
|
202
|
+
el handler** sobre refactorear cada sitio individualmente.
|
|
203
|
+
|
|
204
|
+
**Decisión arquitectónica** (caso OIC v1.5 Slice 2, 2026-06-04):
|
|
205
|
+
|
|
206
|
+
| Opción | Esfuerzo | Cobertura futura | Mantenimiento |
|
|
207
|
+
|---|---|---|---|
|
|
208
|
+
| Refactorear N sitios uno por uno | O(N) turnos | Solo lo refactorizado | Cada `logger.error` nuevo requiere auditoría |
|
|
209
|
+
| Sanitización centralizada en handler | O(1) turno | Todos los sitios + futuros automáticamente | Cero auditoría manual al agregar nuevos `logger.error` |
|
|
210
|
+
|
|
211
|
+
**Patrón portable**:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
import re
|
|
215
|
+
|
|
216
|
+
_PATRON_SENSIBLE = re.compile(
|
|
217
|
+
r'(password|token|secret|jwt|authorization|cookie|api[_-]?key)([=:])([^&\s"\']+)',
|
|
218
|
+
re.IGNORECASE,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def sanitizar_url(url: str | None) -> str | None:
|
|
222
|
+
"""Redacta query-params sensibles preservando key y separador."""
|
|
223
|
+
if not url:
|
|
224
|
+
return url
|
|
225
|
+
return _PATRON_SENSIBLE.sub(r"\1\2<redacted>", url)
|
|
226
|
+
|
|
227
|
+
def sanitizar_valor(valor: Any) -> Any:
|
|
228
|
+
"""Sanitiza recursivamente strings dentro de dict/list/tuple."""
|
|
229
|
+
if isinstance(valor, str):
|
|
230
|
+
return _PATRON_SENSIBLE.sub(r"\1\2<redacted>", valor)
|
|
231
|
+
if isinstance(valor, dict):
|
|
232
|
+
return {k: sanitizar_valor(v) for k, v in valor.items()}
|
|
233
|
+
if isinstance(valor, (list, tuple)):
|
|
234
|
+
return [sanitizar_valor(item) for item in valor]
|
|
235
|
+
return valor
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Regla del regex**:
|
|
239
|
+
- Grupo 1 (key) y grupo 2 (`=` o `:`) se preservan → mantiene legibilidad
|
|
240
|
+
(`password=<redacted>` en vez de `<redacted>`).
|
|
241
|
+
- Grupo 3 (valor) se reemplaza por `<redacted>`.
|
|
242
|
+
- Boundary del valor: `&`, whitespace, comilla, paréntesis.
|
|
243
|
+
- `api_key` y `api-key` ambos matchean con `api[_-]?key`.
|
|
244
|
+
|
|
245
|
+
**Aplicabilidad**: handlers de logging custom, middleware HTTP que redactan headers,
|
|
246
|
+
sanitización de cookies en logs, redactor de stack traces, error reporters
|
|
247
|
+
(Sentry-style). Defensa en profundidad sin tocar el código del caller.
|
|
248
|
+
|
|
249
|
+
**Limitación documentada**: el regex opera sobre strings; NO sanitiza claves cuyo
|
|
250
|
+
nombre no esté en la lista (no detecta `contraseña` en español, `pwd`, `mot_de_passe`).
|
|
251
|
+
Mantener el regex extensible vía configuración si el proyecto opera en múltiples idiomas.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Generación de tokens y passwords legibles (sin caracteres ambiguos)
|
|
256
|
+
|
|
257
|
+
Para passwords temporales (reset por admin, primer login, recuperación) que el
|
|
258
|
+
usuario debe transcribir verbalmente o desde papel, usar alfabeto SIN caracteres
|
|
259
|
+
ambiguos: `0` (cero) vs `O` (o mayúscula), `1` (uno) vs `l` (L minúscula) vs `I`
|
|
260
|
+
(i mayúscula).
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
import secrets
|
|
264
|
+
|
|
265
|
+
# 54 chars del alfabeto seguro (sin 0, O, 1, l, I)
|
|
266
|
+
_ALFABETO_LEGIBLE = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
|
267
|
+
|
|
268
|
+
def generar_password_temporal(longitud: int = 12) -> str:
|
|
269
|
+
"""12 chars → ~71 bits de entropía. Suficiente para temporal que debe
|
|
270
|
+
cambiarse en primer login. NUNCA persistir en texto plano; el caller
|
|
271
|
+
la hashea con bcrypt y la devuelve UNA SOLA VEZ en la respuesta del POST."""
|
|
272
|
+
return "".join(secrets.choice(_ALFABETO_LEGIBLE) for _ in range(longitud))
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**NO usar** `secrets.token_urlsafe()` para passwords visibles al humano: incluye
|
|
276
|
+
`-` y `_` que pueden confundir al transcribir. Reservar `token_urlsafe()` para
|
|
277
|
+
tokens de sesión, CSRF, magic links — strings que no se leen ni transcriben.
|
|
278
|
+
|
|
279
|
+
**Para tokens de uso programático** (API keys, session IDs, refresh tokens):
|
|
280
|
+
`secrets.token_urlsafe(32)` (32 bytes → 256 bits) o `secrets.token_hex(32)`. Sin
|
|
281
|
+
restricción de alfabeto porque el usuario nunca los teclea manualmente.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
144
285
|
## Checklist antes de merge a main
|
|
145
286
|
|
|
146
287
|
- [ ] No hay secrets hardcodeados (revisar con `git grep -i "password\|secret\|token\|key"`)
|
|
@@ -149,3 +290,6 @@ vulnerabilidad del OWASP Top 10. Las más frecuentes en este stack:
|
|
|
149
290
|
- [ ] Sin uso de `eval`, `exec`, `shell=True`
|
|
150
291
|
- [ ] Logs sin datos sensibles
|
|
151
292
|
- [ ] Dependencias nuevas auditadas
|
|
293
|
+
- [ ] Cualquier rama que asuma servicio externo (LDAP/Redis/S3) verifica su flag de config
|
|
294
|
+
- [ ] Sanitización PII centralizada en handlers (no diseminada por N sitios)
|
|
295
|
+
- [ ] Passwords temporales generadas con alfabeto sin chars ambiguos
|
|
@@ -96,7 +96,7 @@ node scripts/auditar-claudemd.js
|
|
|
96
96
|
- **Auditor síncrono**: `scripts/auditar-claudemd.js` (dimensión 7,
|
|
97
97
|
severidad WARN, no bloquea).
|
|
98
98
|
- **Hook async**: `hooks/claudemd-duplicacion-detector.js` (PostToolUse,
|
|
99
|
-
emite nudge a `.planning/
|
|
99
|
+
emite nudge a `.planning/evolution/nudges.jsonl`).
|
|
100
100
|
- **Comando**: `/swl:claudemd audit` reporta el conteo; `/swl:claudemd
|
|
101
101
|
refactor` propone el reemplazo concreto.
|
|
102
102
|
|
|
@@ -34,7 +34,7 @@ try {
|
|
|
34
34
|
|
|
35
35
|
var RAIZ_AGENTES = path.resolve(__dirname, '..', 'agentes');
|
|
36
36
|
var DESTINO_PLANNING = path.resolve(
|
|
37
|
-
__dirname, '..', '.planning', '
|
|
37
|
+
__dirname, '..', '.planning', 'evolution', 'audit-agentes-gaps.json'
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
// Patrones - soportan LF y CRLF (Windows).
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Uso:
|
|
12
12
|
* node scripts/auditar-cobertura-frameworks.js # JSON a stdout
|
|
13
13
|
* node scripts/auditar-cobertura-frameworks.js --resumen # tabla a stderr
|
|
14
|
-
* node scripts/auditar-cobertura-frameworks.js --save # persiste en .planning/
|
|
14
|
+
* node scripts/auditar-cobertura-frameworks.js --save # persiste en .planning/evolution/
|
|
15
15
|
* node scripts/auditar-cobertura-frameworks.js --framework=nist_csf # solo un framework
|
|
16
16
|
*
|
|
17
17
|
* No falla si un skill no tiene mapeos — solo los skills del dominio de
|
|
@@ -35,7 +35,7 @@ try {
|
|
|
35
35
|
|
|
36
36
|
const RAIZ_SKILLS = path.resolve(__dirname, '..', 'habilidades');
|
|
37
37
|
const DESTINO = path.resolve(
|
|
38
|
-
__dirname, '..', '.planning', '
|
|
38
|
+
__dirname, '..', '.planning', 'evolution', 'cobertura-frameworks.json'
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
const CAMPOS_FRAMEWORK = Object.freeze([
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Uso:
|
|
11
11
|
* node scripts/auditar-skills-gaps.js # JSON a stdout
|
|
12
12
|
* node scripts/auditar-skills-gaps.js --resumen # solo tabla a stderr
|
|
13
|
-
* node scripts/auditar-skills-gaps.js --save # persiste en .planning/
|
|
13
|
+
* node scripts/auditar-skills-gaps.js --save # persiste en .planning/evolution/
|
|
14
14
|
* node scripts/auditar-skills-gaps.js --incluir-excepciones # incluye gaps excusados en el total
|
|
15
15
|
*
|
|
16
16
|
* Excepciones documentadas: lista inline de skills con gaps justificados por ADR.
|
|
@@ -30,7 +30,7 @@ try {
|
|
|
30
30
|
|
|
31
31
|
const RAIZ_SKILLS = path.resolve(__dirname, '..', 'habilidades');
|
|
32
32
|
const DESTINO_PLANNING = path.resolve(
|
|
33
|
-
__dirname, '..', '.planning', '
|
|
33
|
+
__dirname, '..', '.planning', 'evolution', 'audit-skills-gaps.json'
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
// Patrones de detección
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* 2 - Argumentos inválidos
|
|
27
27
|
*
|
|
28
28
|
* Persistencia opcional: si se setea SWL_BENCHMARK_PERSIST=1, escribe
|
|
29
|
-
* el resultado agregado a `.planning/
|
|
29
|
+
* el resultado agregado a `.planning/evolution/benchmark-memoria.jsonl`
|
|
30
30
|
* para tracking histórico.
|
|
31
31
|
*/
|
|
32
32
|
|
|
@@ -36,7 +36,7 @@ const path = require('path');
|
|
|
36
36
|
const { ejecutarDataset } = require('./lib/longmemeval-runner');
|
|
37
37
|
|
|
38
38
|
const DATASET_DEFAULT = '.planning/benchmark/dataset.jsonl';
|
|
39
|
-
const HISTORICO_PATH = '.planning/
|
|
39
|
+
const HISTORICO_PATH = '.planning/evolution/benchmark-memoria.jsonl';
|
|
40
40
|
|
|
41
41
|
function uso() {
|
|
42
42
|
console.error('Uso: node scripts/benchmark-memoria.js [--dataset <ruta>] [--limit <n>] [--json] [--verbose]');
|
|
@@ -64,7 +64,7 @@ function parseArgs(argv) {
|
|
|
64
64
|
function persistirHistorico(baseDir, resumen) {
|
|
65
65
|
if (process.env.SWL_BENCHMARK_PERSIST !== '1') return;
|
|
66
66
|
try {
|
|
67
|
-
const dirEvolucion = path.join(baseDir, '.planning', '
|
|
67
|
+
const dirEvolucion = path.join(baseDir, '.planning', 'evolution');
|
|
68
68
|
if (!fs.existsSync(dirEvolucion)) fs.mkdirSync(dirEvolucion, { recursive: true });
|
|
69
69
|
const linea = JSON.stringify({
|
|
70
70
|
timestamp: new Date().toISOString(),
|
|
@@ -33,7 +33,8 @@ const args = new Set(process.argv.slice(2));
|
|
|
33
33
|
const DRY = args.has('--dry-run');
|
|
34
34
|
const SOLO_INV = args.has('--inventario');
|
|
35
35
|
const SOLO_SAL = args.has('--salud');
|
|
36
|
-
const
|
|
36
|
+
const SOLO_LLMS = args.has('--llms');
|
|
37
|
+
const AMBOS = !SOLO_INV && !SOLO_SAL && !SOLO_LLMS;
|
|
37
38
|
|
|
38
39
|
// ── Helpers ────────────────────────────────────────────────────────────
|
|
39
40
|
|
|
@@ -427,6 +428,57 @@ function generarSalud() {
|
|
|
427
428
|
return lines.join('\n');
|
|
428
429
|
}
|
|
429
430
|
|
|
431
|
+
// ── llms.txt (convención llmstxt.org) ─────────────────────────────────
|
|
432
|
+
// Índice raíz LLM-legible para descubrimiento externo por herramientas/agentes
|
|
433
|
+
// que esperan el estándar. Cifras sincronizadas con INVENTARIO (mismo cómputo)
|
|
434
|
+
// para que el gate de release no detecte drift. Adoptado de obsidian-second-brain
|
|
435
|
+
// (análisis temp/ 2026-06-05); el resto de ese repo era redundante con swl-ses.
|
|
436
|
+
function generarLlmsTxt() {
|
|
437
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(RAIZ, 'package.json'), 'utf8'));
|
|
438
|
+
const agentes = recolectarAgentes().length;
|
|
439
|
+
const skills = recolectarSkills().length;
|
|
440
|
+
const hooks = recolectarHooks().length;
|
|
441
|
+
const comandos = contarArchivos(path.join(RAIZ, 'comandos', 'swl'), '.md');
|
|
442
|
+
const reglasBase = contarArchivos(path.join(RAIZ, 'reglas'), '.md');
|
|
443
|
+
const reglasLang = fs.readdirSync(path.join(RAIZ, 'reglas', 'lenguajes'))
|
|
444
|
+
.filter(d => fs.statSync(path.join(RAIZ, 'reglas', 'lenguajes', d)).isDirectory())
|
|
445
|
+
.reduce((acc, d) => acc + contarArchivos(path.join(RAIZ, 'reglas', 'lenguajes', d), '.md'), 0);
|
|
446
|
+
|
|
447
|
+
const L = [];
|
|
448
|
+
L.push('# swl-ses (@saulwade/swl-ses)');
|
|
449
|
+
L.push('');
|
|
450
|
+
L.push(`> Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo), distribuido como paquete npm y plugin de Claude Code. ${agentes} agentes, ${skills} habilidades, ${comandos} comandos, ${reglasBase} reglas base y ${hooks} hooks. Soporta 11 lenguajes y 7 runtimes (Claude Code, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot). Versión ${pkg.version}.`);
|
|
451
|
+
L.push('');
|
|
452
|
+
L.push('Archivo generado por `node scripts/generar-inventario.js` — no editar a mano. Las cifras se sincronizan con INVENTARIO.md en cada regeneración.');
|
|
453
|
+
L.push('');
|
|
454
|
+
L.push('## Documentación');
|
|
455
|
+
L.push('');
|
|
456
|
+
L.push('- [README](README.md): overview público y quickstart');
|
|
457
|
+
L.push('- [Manual de uso](MANUAL_USO.md): manual operacional completo');
|
|
458
|
+
L.push('- [Comandos](COMANDOS.md): referencia detallada de cada comando `/swl:*`');
|
|
459
|
+
L.push('- [Agentes](AGENTS.md): catálogo de agentes con capacidades');
|
|
460
|
+
L.push('- [Inventario](INVENTARIO.md): conteos oficiales de todos los componentes');
|
|
461
|
+
L.push('- [Instalación](INSTALACION.md): instalación, perfiles y configuración');
|
|
462
|
+
L.push('- [Instrucciones del proyecto](CLAUDE.md): convenciones, stack y reglas');
|
|
463
|
+
L.push('');
|
|
464
|
+
L.push('## Componentes');
|
|
465
|
+
L.push('');
|
|
466
|
+
L.push(`- ${agentes} agentes especializados en \`agentes/\` (orquestación, implementación por stack, revisión, calidad, diseño)`);
|
|
467
|
+
L.push(`- ${skills} habilidades cargables bajo demanda en \`habilidades/\` (conocimiento operacional con divulgación progresiva)`);
|
|
468
|
+
L.push(`- ${comandos} comandos \`/swl:*\` en \`comandos/swl/\` (ciclo GSD, calidad, release, diagnóstico)`);
|
|
469
|
+
L.push(`- ${reglasBase} reglas base + ${reglasLang} reglas por lenguaje en \`reglas/\` (políticas obligatorias por matcher)`);
|
|
470
|
+
L.push(`- ${hooks} hooks en \`hooks/\` (telemetría, validación, seguridad; zero-deps, escrituras atómicas)`);
|
|
471
|
+
L.push('');
|
|
472
|
+
L.push('## Opcional');
|
|
473
|
+
L.push('');
|
|
474
|
+
L.push('- [CHANGELOG](CHANGELOG.md): historial de versiones');
|
|
475
|
+
L.push('- [Índice de ADRs](.planning/adrs/README.md): decisiones de arquitectura');
|
|
476
|
+
L.push('- [Variables de entorno](docs/variables-entorno.md): configuración opt-in');
|
|
477
|
+
L.push('');
|
|
478
|
+
|
|
479
|
+
return L.join('\n');
|
|
480
|
+
}
|
|
481
|
+
|
|
430
482
|
// ── Ejecutar ──────────────────────────────────────────────────────────
|
|
431
483
|
|
|
432
484
|
if (AMBOS || SOLO_INV) {
|
|
@@ -450,3 +502,14 @@ if (AMBOS || SOLO_SAL) {
|
|
|
450
502
|
console.log('SALUD.md generado correctamente.');
|
|
451
503
|
}
|
|
452
504
|
}
|
|
505
|
+
|
|
506
|
+
if (AMBOS || SOLO_LLMS) {
|
|
507
|
+
const llms = generarLlmsTxt();
|
|
508
|
+
if (DRY) {
|
|
509
|
+
console.log('\n=== llms.txt (dry-run) ===\n');
|
|
510
|
+
console.log(llms);
|
|
511
|
+
} else {
|
|
512
|
+
atomicWriteSync(path.join(RAIZ, 'llms.txt'), llms, 'utf8');
|
|
513
|
+
console.log('llms.txt generado correctamente.');
|
|
514
|
+
}
|
|
515
|
+
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* node scripts/inferir-herramientas-permitidas.js --dry-run --skill=fastapi-experto
|
|
15
15
|
* node scripts/inferir-herramientas-permitidas.js --aplicar
|
|
16
16
|
* node scripts/inferir-herramientas-permitidas.js --aplicar --solo-faltantes
|
|
17
|
-
* node scripts/inferir-herramientas-permitidas.js --aplicar --log=.planning/
|
|
17
|
+
* node scripts/inferir-herramientas-permitidas.js --aplicar --log=.planning/evolution/infer-herramientas.log
|
|
18
18
|
*
|
|
19
19
|
* @module scripts/inferir-herramientas-permitidas
|
|
20
20
|
*/
|
package/scripts/instalador.js
CHANGED
|
@@ -210,10 +210,30 @@ async function install(opciones) {
|
|
|
210
210
|
// 2b. Filtrar reglas de lenguaje por stack detectado
|
|
211
211
|
// El stack se detecta siempre que: hay reglas de lenguaje en el perfil, O
|
|
212
212
|
// se ejecuta con --force (actualización) para poder limpiar las ya instaladas.
|
|
213
|
+
// FIX (thrashing de contexto en subagentes): en scope GLOBAL las reglas
|
|
214
|
+
// por-lenguaje NO se instalan en ~/.claude/rules/. Global no tiene un proyecto
|
|
215
|
+
// que detectar, así que se instalarían los 8 lenguajes (~69K tokens) y
|
|
216
|
+
// saturarían la ventana de todo subagente que herede ~/.claude/rules/. Las
|
|
217
|
+
// reglas por-lenguaje viven project-scoped, donde el stack-filter sí aplica.
|
|
218
|
+
if (esGlobal) {
|
|
219
|
+
const antesGlobal = resolucion.archivos.length;
|
|
220
|
+
resolucion.archivos = resolucion.archivos.filter(
|
|
221
|
+
a => !(a.rutaRelativa && a.rutaRelativa.startsWith('reglas/lenguajes/'))
|
|
222
|
+
);
|
|
223
|
+
const excluidasGlobal = antesGlobal - resolucion.archivos.length;
|
|
224
|
+
if (excluidasGlobal > 0) {
|
|
225
|
+
console.log(
|
|
226
|
+
`\n[stack] Scope global: ${excluidasGlobal} regla(s) por-lenguaje excluidas ` +
|
|
227
|
+
'(se instalan project-scoped, filtradas por stack; no en ~/.claude/rules/ ' +
|
|
228
|
+
'para no saturar el contexto de subagentes).'
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
213
233
|
const tieneReglasLenguaje = resolucion.archivos.some(
|
|
214
234
|
a => a.rutaRelativa && a.rutaRelativa.startsWith('reglas/lenguajes/')
|
|
215
235
|
);
|
|
216
|
-
const necesitaStack = (tieneReglasLenguaje || force) && !allLangs;
|
|
236
|
+
const necesitaStack = (tieneReglasLenguaje || force) && !allLangs && !esGlobal;
|
|
217
237
|
|
|
218
238
|
let stackDetectado = null;
|
|
219
239
|
|
|
@@ -224,7 +244,7 @@ async function install(opciones) {
|
|
|
224
244
|
// FIX v1.6.6: si el usuario pasa --all-langs pero el perfil actual no incluye
|
|
225
245
|
// reglas/lenguajes/, el flag se ignora silenciosamente. Antes la única pista
|
|
226
246
|
// era el conteo final de archivos. Ahora emitimos warning explícito.
|
|
227
|
-
if (allLangs && !tieneReglasLenguaje) {
|
|
247
|
+
if (allLangs && !tieneReglasLenguaje && !esGlobal) {
|
|
228
248
|
console.log(
|
|
229
249
|
'\n[stack] Aviso: --all-langs ignorado — el perfil actual (' +
|
|
230
250
|
(resolucion.perfil || 'core') +
|
|
@@ -269,6 +289,16 @@ async function install(opciones) {
|
|
|
269
289
|
}
|
|
270
290
|
}
|
|
271
291
|
|
|
292
|
+
// Scope global: purgar TODAS las reglas por-lenguaje preexistentes de
|
|
293
|
+
// ~/.claude/rules/ (instalaciones previas dejaban los 8 lenguajes always-loaded,
|
|
294
|
+
// saturando el contexto de subagentes). Corre en cada install global, no solo --force.
|
|
295
|
+
if (esGlobal && rutas.reglas) {
|
|
296
|
+
const purgaGlobal = limpiarReglasSinStack(rutas.reglas, new Set());
|
|
297
|
+
if (purgaGlobal.eliminados > 0) {
|
|
298
|
+
console.log(`\n[stack] Scope global: ${purgaGlobal.eliminados} subdir(s) de reglas por-lenguaje purgadas de ~/.claude/rules/ (${purgaGlobal.lenguajes.join(', ')}) — viven project-scoped.`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
272
302
|
// 4. Detectar _userland/
|
|
273
303
|
const userlandAgentes = path.join(process.cwd(), '_userland', 'agentes');
|
|
274
304
|
const userlandHabilidades = path.join(process.cwd(), '_userland', 'habilidades');
|
|
@@ -416,6 +446,54 @@ async function install(opciones) {
|
|
|
416
446
|
console.log(` [migración] ${migracionesAplicadas} archivo(s) de .planning/ migrado(s) de inglés a español`);
|
|
417
447
|
}
|
|
418
448
|
|
|
449
|
+
// 6c-bis. Migración de DIRECTORIOS runtime de .planning/ español → inglés.
|
|
450
|
+
// Unificación: los paths runtime/técnicos van en inglés (telemetría, perfil,
|
|
451
|
+
// logs archivados). NO incluye fases/ (vocabulario GSD de cara al usuario, se
|
|
452
|
+
// mantiene español por coherencia con los comandos *-fase). Ver ADR-0031.
|
|
453
|
+
const MIGRACIONES_DIRS_PLANNING = [
|
|
454
|
+
{ viejo: 'evolucion', nuevo: 'evolution' },
|
|
455
|
+
{ viejo: 'auto-evolucion', nuevo: 'auto-evolution' },
|
|
456
|
+
{ viejo: 'perfil-usuario', nuevo: 'user-profile' },
|
|
457
|
+
{ viejo: 'archivo', nuevo: 'archive' },
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
let dirsMigrados = 0;
|
|
461
|
+
for (const m of MIGRACIONES_DIRS_PLANNING) {
|
|
462
|
+
const dirViejo = path.join(process.cwd(), '.planning', m.viejo);
|
|
463
|
+
const dirNuevo = path.join(process.cwd(), '.planning', m.nuevo);
|
|
464
|
+
|
|
465
|
+
if (!fs.existsSync(dirViejo)) continue;
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
if (!fs.existsSync(dirNuevo)) {
|
|
469
|
+
// Caso 1: solo existe el viejo → renombrar el directorio completo
|
|
470
|
+
fs.renameSync(dirViejo, dirNuevo);
|
|
471
|
+
console.log(` ✓ Migración dir: .planning/${m.viejo}/ → .planning/${m.nuevo}/`);
|
|
472
|
+
} else {
|
|
473
|
+
// Caso 2: ambos existen → mover entradas del viejo al nuevo (sin sobreescribir)
|
|
474
|
+
let saltadas = 0;
|
|
475
|
+
for (const entry of fs.readdirSync(dirViejo)) {
|
|
476
|
+
const src = path.join(dirViejo, entry);
|
|
477
|
+
const dst = path.join(dirNuevo, entry);
|
|
478
|
+
if (!fs.existsSync(dst)) fs.renameSync(src, dst);
|
|
479
|
+
else saltadas++; // conflicto de nombre: se deja en origen (no destructivo)
|
|
480
|
+
}
|
|
481
|
+
try { fs.rmdirSync(dirViejo); } catch { /* no vacío: se deja, no destructivo */ }
|
|
482
|
+
const sufijo = saltadas > 0
|
|
483
|
+
? ` (${saltadas} entrada(s) con conflicto dejadas en .planning/${m.viejo}/)`
|
|
484
|
+
: '';
|
|
485
|
+
console.log(` ⊕ Migración dir: .planning/${m.viejo}/ fusionado en .planning/${m.nuevo}/${sufijo}`);
|
|
486
|
+
}
|
|
487
|
+
dirsMigrados++;
|
|
488
|
+
} catch (err) {
|
|
489
|
+
console.log(` ! Migración dir ${m.viejo}: ${err.message}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (dirsMigrados > 0) {
|
|
494
|
+
console.log(` [migración] ${dirsMigrados} directorio(s) runtime de .planning/ migrado(s) español → inglés`);
|
|
495
|
+
}
|
|
496
|
+
|
|
419
497
|
// 6d. Filtrar skills por targets declarados en SKILL.md frontmatter (patrón skillshare)
|
|
420
498
|
// Si un skill declara targets: [claude, copilot], solo se instala en esos runtimes.
|
|
421
499
|
// Si no declara targets, se instala en todos (comportamiento por defecto).
|
|
@@ -122,7 +122,7 @@ const _WIDGET_CATALOG = [
|
|
|
122
122
|
{
|
|
123
123
|
id: 'nudges-recientes',
|
|
124
124
|
label: 'Nudges Recientes',
|
|
125
|
-
descripcion: 'Últimos nudges emitidos por hooks de observabilidad (.planning/
|
|
125
|
+
descripcion: 'Últimos nudges emitidos por hooks de observabilidad (.planning/evolution/nudges.jsonl)',
|
|
126
126
|
categoria: 'evolucion',
|
|
127
127
|
modos: ['offline'],
|
|
128
128
|
tamanoDefault: 'md',
|
|
@@ -130,7 +130,7 @@ const _WIDGET_CATALOG = [
|
|
|
130
130
|
{
|
|
131
131
|
id: 'evoluciones-aplicadas',
|
|
132
132
|
label: 'Evoluciones Aplicadas',
|
|
133
|
-
descripcion: 'Historial de evoluciones de skills y agentes (.planning/
|
|
133
|
+
descripcion: 'Historial de evoluciones de skills y agentes (.planning/evolution/evoluciones.jsonl)',
|
|
134
134
|
categoria: 'evolucion',
|
|
135
135
|
modos: ['offline'],
|
|
136
136
|
tamanoDefault: 'md',
|
|
@@ -138,7 +138,7 @@ const _WIDGET_CATALOG = [
|
|
|
138
138
|
{
|
|
139
139
|
id: 'alertas-persistentes',
|
|
140
140
|
label: 'Alertas Persistentes',
|
|
141
|
-
descripcion: 'Alertas activas sin resolver del ciclo AGP (.planning/
|
|
141
|
+
descripcion: 'Alertas activas sin resolver del ciclo AGP (.planning/evolution/alertas-persistentes.json)',
|
|
142
142
|
categoria: 'evolucion',
|
|
143
143
|
modos: ['offline'],
|
|
144
144
|
tamanoDefault: 'md',
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - Fuente de datos: JSONL en disco, no SQLite
|
|
18
18
|
* - Sin dependencias externas (solo Node stdlib: fs, path)
|
|
19
19
|
* - Métricas configurables via parámetro, no hardcodeadas
|
|
20
|
-
* - Emite nudge a .planning/
|
|
20
|
+
* - Emite nudge a .planning/evolution/nudges.jsonl en drift crítico
|
|
21
21
|
*
|
|
22
22
|
* @module scripts/lib/drift-detector
|
|
23
23
|
*/
|
|
@@ -50,7 +50,7 @@ const METRICAS_DEFAULT = [
|
|
|
50
50
|
|
|
51
51
|
// Ruta raíz del proyecto (dos niveles arriba de scripts/lib/)
|
|
52
52
|
const RAIZ_PROYECTO = path.resolve(__dirname, '..', '..');
|
|
53
|
-
const RUTA_NUDGES = path.join(RAIZ_PROYECTO, '.planning', '
|
|
53
|
+
const RUTA_NUDGES = path.join(RAIZ_PROYECTO, '.planning', 'evolution', 'nudges.jsonl');
|
|
54
54
|
|
|
55
55
|
// ── helpers internos ──────────────────────────────────────────────────────────
|
|
56
56
|
|
|
@@ -202,7 +202,7 @@ function contarNudgesPrevios(metrica, agente) {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
/**
|
|
205
|
-
* Emite un nudge a .planning/
|
|
205
|
+
* Emite un nudge a .planning/evolution/nudges.jsonl cuando se detecta
|
|
206
206
|
* drift crítico. Usa fs.appendFileSync (JSONL, no sobreescribe).
|
|
207
207
|
*
|
|
208
208
|
* Aplica repair-loop detection: si el mismo (metrica, agente) ya fue
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Patrón adoptado de `temp/agentmemory-main/src/eval/metrics-store.ts`.
|
|
7
7
|
* Adaptado a swl-ses: file-based en lugar de SQLite. Persiste:
|
|
8
|
-
* - JSONL append-only: `.planning/
|
|
9
|
-
* - JSON agregado: `.planning/
|
|
8
|
+
* - JSONL append-only: `.planning/evolution/eval-results.jsonl` (cada eval individual)
|
|
9
|
+
* - JSON agregado: `.planning/evolution/eval-metrics.json` (totales por functionId)
|
|
10
10
|
*
|
|
11
11
|
* El JSONL preserva el detalle histórico (un evento por evaluación). El JSON
|
|
12
12
|
* agregado se recalcula incrementalmente: avg latency, avg quality, success
|
|
@@ -31,7 +31,7 @@ try {
|
|
|
31
31
|
|
|
32
32
|
// ── constantes ────────────────────────────────────────────────────────────────
|
|
33
33
|
|
|
34
|
-
const DIR_EVOLUCION = path.join('.planning', '
|
|
34
|
+
const DIR_EVOLUCION = path.join('.planning', 'evolution');
|
|
35
35
|
const RUTA_JSONL = path.join(DIR_EVOLUCION, 'eval-results.jsonl');
|
|
36
36
|
const RUTA_AGREGADO = path.join(DIR_EVOLUCION, 'eval-metrics.json');
|
|
37
37
|
|