@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
package/comandos/swl/nemesis.md
CHANGED
|
@@ -8,7 +8,7 @@ description: >
|
|
|
8
8
|
automático para scope > 1500 LOC o > 5 archivos en módulos distintos.
|
|
9
9
|
Persiste hallazgos en .planning/audit/findings/iter-N/.
|
|
10
10
|
allowed-tools: [Read, Grep, Glob, Bash, Write, Agent, Skill]
|
|
11
|
-
argument-hint: "[--remediar] [--pass1 | --pass2 | --continue] [--modulo <ruta>] [--redistribuir] [--reset-plan]"
|
|
11
|
+
argument-hint: "[--remediar] [--pass1 | --pass2 | --continue] [--modulo <ruta>] [--redistribuir] [--reset-plan] [--cross-model]"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# /swl:nemesis — Auditoría iterativa con remediación opt-in
|
|
@@ -54,6 +54,29 @@ y el ciclo continúa hasta convergencia o agotar el guardrail de 3 iteraciones.
|
|
|
54
54
|
| Acotar módulo | `--modulo <ruta>` | Limita el scope a un archivo o subdirectorio específico. Se combina con cualquier otro flag. |
|
|
55
55
|
| Forzar redistribución | `--redistribuir` | Activa `Skill("nemesis-redistribuir")` aunque scope sea menor al umbral. |
|
|
56
56
|
| Reset del plan | `--reset-plan` | Regenera `nemesis-plan.json` desde cero (descarta plan previo). |
|
|
57
|
+
| **Revisión cross-modelo** | `--cross-model` | Opt-in: rutea la evaluación a un reviewer en OTRO modelo (vía MCP, p.ej. `gemini-review`/`codex-review`) para combatir *self-preferential bias*. Degrada al reviewer same-model si no hay MCP configurado (anuncia la degradación). Combina con `--remediar`. Ver `Skill("proceso-dynamic-workflows") § Revisión cross-modelo`. |
|
|
58
|
+
|
|
59
|
+
## Revisión cross-modelo (`--cross-model`)
|
|
60
|
+
|
|
61
|
+
El reviewer adversarial en el MISMO modelo aún arrastra *self-preferential bias*
|
|
62
|
+
(uno de los 3 modos de falla nombrados en el blog oficial de dynamic workflows).
|
|
63
|
+
`--cross-model` hace que la evaluación la emita un modelo DISTINTO al ejecutor:
|
|
64
|
+
|
|
65
|
+
- **Wiring (opt-in, ligero)**: si hay un MCP reviewer configurado (patrón ARIS
|
|
66
|
+
`gemini-review`/`codex-review`: expone `review`/`review_reply`, devuelve JSON con
|
|
67
|
+
`threadId` + `response`), el paso de evaluación se rutea a ese MCP. swl-ses NO
|
|
68
|
+
embebe el servidor — lo provee el usuario con su API key del otro modelo.
|
|
69
|
+
- **Degradación explícita** (regla `arreglar-al-detectar.md` / no-fallback-silencioso):
|
|
70
|
+
si el MCP no está disponible, NO falla — usa el reviewer same-model y **anuncia**
|
|
71
|
+
"cross-model solicitado pero MCP ausente → degradado a same-model".
|
|
72
|
+
- **Reviewer memory + debate** (de ARIS `auto-review-loop`): el reviewer externo
|
|
73
|
+
arrastra sospechas entre iteraciones (un `threadId`); el ejecutor puede rebatir,
|
|
74
|
+
el reviewer falla el veredicto final — *"it can drive, never acquit"*.
|
|
75
|
+
- **Trazabilidad**: el JSON del reviewer (`threadId`, `response`, score) se persiste
|
|
76
|
+
junto a `evaluacion.json` en `.planning/audit/findings/iter-N/` → veredicto
|
|
77
|
+
independiente auditable.
|
|
78
|
+
|
|
79
|
+
Detalle del patrón: `Skill("proceso-dynamic-workflows") § Revisión cross-modelo`.
|
|
57
80
|
|
|
58
81
|
## Flujo completo con `--remediar`
|
|
59
82
|
|
|
@@ -207,6 +230,24 @@ Estructura de salida bajo `.planning/audit/findings/`:
|
|
|
207
230
|
|
|
208
231
|
Cada `evaluacion.json` sigue el schema `nemesis-evaluacion-json` v1.0.0.
|
|
209
232
|
|
|
233
|
+
### Telemetría de loop (obligatoria con `--remediar`)
|
|
234
|
+
|
|
235
|
+
En modo `--remediar`, además de los `iter-N/` el comando registra la
|
|
236
|
+
trayectoria en el formato estándar de telemetría de loops
|
|
237
|
+
(`hooks/lib/loop-telemetry.js`). Esto habilita: inyección de estado por el
|
|
238
|
+
hook `contexto-iteracion.js` durante las ejecuciones largas (8-30 turnos),
|
|
239
|
+
detección de plateau, y lectura de la corrida por `/swl:metricas`.
|
|
240
|
+
|
|
241
|
+
- Al iniciar iter-1: `iniciarCorrida({tipo: 'nemesis', direccion: 'lower_is_better', config: {modulo, maxIter: 3}})`.
|
|
242
|
+
- Tras cada iteración: `registrarIteracion(dir, {iteracion: N, metrica: criticos+altos, delta, estado: 'keep', descripcion: 'iter N: status=<status>, X criticos, Y altos'})`.
|
|
243
|
+
- Al cerrar (PASS, FAIL o Recovery Catalog): `escribirHandoff(dir, {source: 'swl:nemesis', status, findings: <hallazgos residuales con archivo_linea>, config})`.
|
|
244
|
+
Mapeo de status: PASS → `COMPLETO`, max iteraciones → `ACOTADO`, Recovery
|
|
245
|
+
Catalog/escalada → `INTERRUMPIDO`.
|
|
246
|
+
|
|
247
|
+
El `handoff.json` resultante es consumible por una corrida posterior de
|
|
248
|
+
`/swl:verificar --until-converge` o por el orquestador (los `findings`
|
|
249
|
+
traen `archivo_linea` verificable según `verificar-citas-normativas.md § Familia 2`).
|
|
250
|
+
|
|
210
251
|
## Cómo interpretar el reporte
|
|
211
252
|
|
|
212
253
|
### Severidades
|
|
@@ -161,6 +161,14 @@ El agente planificador-swl debe generar el plan con esta estructura exacta:
|
|
|
161
161
|
## Tests requeridos
|
|
162
162
|
[lista de tests que deben existir al finalizar la fase]
|
|
163
163
|
|
|
164
|
+
## Escenarios Gherkin (opt-in — solo si la fase tiene criterios de aceptación de negocio)
|
|
165
|
+
[Si el CONTEXTO.md/PRD trae criterios de aceptación con reglas de negocio
|
|
166
|
+
distinguibles, convertirlos en escenarios Given–When–Then siguiendo
|
|
167
|
+
`habilidades/tdd-workflow/recursos/gherkin-bdd.md` y presentarlos al usuario
|
|
168
|
+
para validación ANTES de aprobar el plan. Cada escenario validado se referencia
|
|
169
|
+
desde la tarea que lo implementa (será su test RED). Omitir esta sección en
|
|
170
|
+
fases técnicas puras — Gherkin sin lector de negocio es ceremonia.]
|
|
171
|
+
|
|
164
172
|
## Riesgos y mitigaciones
|
|
165
173
|
| Riesgo | Impacto | Probabilidad | Mitigación |
|
|
166
174
|
|--------|---------|-------------|-----------|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swl:predecir
|
|
3
|
+
description: >
|
|
4
|
+
Análisis predictivo pre-implementación: 5 personas expertas (Arquitecto,
|
|
5
|
+
Seguridad, Rendimiento, Confiabilidad, Abogado del Diablo) analizan EN FRÍO
|
|
6
|
+
un cambio propuesto antes de implementarlo, un sintetizador deduplica con
|
|
7
|
+
anti-herd check y entrega hallazgos rankeados por severidad × confianza ×
|
|
8
|
+
acuerdo. Usar antes de /swl:planear-fase en fases de riesgo, antes de un
|
|
9
|
+
refactor grande, o cuando el costo de descubrir un problema DESPUÉS de
|
|
10
|
+
implementar es alto. Con --adversarial usa el set atacante (Rompedor,
|
|
11
|
+
Tramposo, Escalador, Novato, Insider).
|
|
12
|
+
argument-hint: "[descripción del cambio] [--scope <glob>] [--adversarial] [--presupuesto N] [--chain planear-fase]"
|
|
13
|
+
allowed-tools: [Read, Grep, Glob, Bash, Write, Agent, Skill]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# /swl:predecir — Análisis predictivo pre-implementación
|
|
17
|
+
|
|
18
|
+
Eres el coordinador de un panel de personas expertas que evalúan un cambio
|
|
19
|
+
propuesto ANTES de que se implemente. El valor del comando está en el
|
|
20
|
+
aislamiento: cada persona analiza en frío, sin ver a las demás — los sesgos
|
|
21
|
+
de manada y de confirmación se eliminan por construcción.
|
|
22
|
+
|
|
23
|
+
Complementa (no reemplaza) a `/swl:verificar` y `/swl:nemesis`: esos auditan
|
|
24
|
+
código que YA existe; `predecir` ataca el plan cuando corregirlo cuesta una
|
|
25
|
+
conversación, no un refactor.
|
|
26
|
+
|
|
27
|
+
## Cuándo usar
|
|
28
|
+
|
|
29
|
+
- Antes de `/swl:planear-fase` en fases que tocan auth, dinero, datos
|
|
30
|
+
productivos o contratos públicos de API.
|
|
31
|
+
- Antes de un refactor cross-módulo o una migración de schema.
|
|
32
|
+
- Cuando el usuario duda entre implementar o no un cambio con blast radius alto.
|
|
33
|
+
|
|
34
|
+
**Cuándo NO usar**: cambios triviales (1-2 archivos), código ya implementado
|
|
35
|
+
(usar `/swl:verificar`), o decisiones entre 2+ alternativas (usar el debate
|
|
36
|
+
adversarial de `Skill("proceso-debate-adversarial")` — predecir analiza UNA
|
|
37
|
+
propuesta, el debate compara varias).
|
|
38
|
+
|
|
39
|
+
## Flags
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
[descripción] La propuesta de cambio a analizar (obligatoria; si falta, preguntar)
|
|
43
|
+
--scope <glob> Archivos relevantes para grounding (default: derivar del texto)
|
|
44
|
+
--adversarial Set de personas atacantes en vez del set default
|
|
45
|
+
--presupuesto N Máximo de hallazgos totales (default: 40 → 8 por persona)
|
|
46
|
+
--chain planear-fase Al terminar, ofrecer arrancar /swl:planear-fase con los hallazgos como contexto
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Paso 0 — Carga y configuración
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Skill("proceso-debate-adversarial")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
El skill define el protocolo COLD START, el anti-herd check y el formato de
|
|
56
|
+
síntesis. Las definiciones de personas viven en
|
|
57
|
+
`habilidades/proceso-debate-adversarial/recursos/personas.md`.
|
|
58
|
+
|
|
59
|
+
Confirmar con el usuario: propuesta, scope, set de personas, presupuesto.
|
|
60
|
+
|
|
61
|
+
## Paso 1 — Reconocimiento del codebase
|
|
62
|
+
|
|
63
|
+
Construir el paquete de conocimiento que recibirá CADA persona (idéntico para
|
|
64
|
+
todas): descripción de la propuesta + inventario de archivos del scope +
|
|
65
|
+
dependencias relevantes + superficie de API afectada + cobertura de tests del
|
|
66
|
+
área. Usar `code-review-graph` si está disponible (blast radius, callers);
|
|
67
|
+
si no, Grep/Glob dirigidos. Máximo ~1,500 tokens — las personas analizan, no
|
|
68
|
+
exploran.
|
|
69
|
+
|
|
70
|
+
## Paso 2 — Análisis en frío (5 invocaciones Agent independientes)
|
|
71
|
+
|
|
72
|
+
Por cada persona, UNA invocación del Agent tool (general-purpose o el agente
|
|
73
|
+
afín al dominio) con prompt autocontenido:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
Eres [persona] — [enfoque de recursos/personas.md].
|
|
77
|
+
Propuesta: [descripción]
|
|
78
|
+
Contexto del codebase: [paquete del Paso 1]
|
|
79
|
+
Tu tarea: encuentra hasta [presupuesto/5] problemas que esta propuesta
|
|
80
|
+
causaría, desde tu perspectiva. Por cada uno: título, severidad
|
|
81
|
+
(crítico/alto/medio/bajo), confianza 0-100%, archivo:línea si aplica,
|
|
82
|
+
recomendación concreta. Preguntas guía: [de la persona]. Red flags: [de la
|
|
83
|
+
persona]. NO incluyas elogios ni evaluación general — solo hallazgos.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Las 5 invocaciones pueden correr en paralelo. NUNCA pasar el output de una
|
|
87
|
+
persona a otra — el aislamiento es el mecanismo del comando.
|
|
88
|
+
|
|
89
|
+
## Paso 3 — Síntesis con anti-herd check
|
|
90
|
+
|
|
91
|
+
Aplicar el protocolo de síntesis del skill:
|
|
92
|
+
|
|
93
|
+
1. Deduplicar (mismo archivo:línea + mismo problema → fusionar, severidad más alta gana).
|
|
94
|
+
2. Registrar disensos (dos personas en desacuerdo → ambas posturas visibles).
|
|
95
|
+
3. **Anti-herd check**: si todas las personas coinciden en el hallazgo top,
|
|
96
|
+
generar explícitamente ≥1 contraargumento antes de aceptarlo.
|
|
97
|
+
4. Rankear: `severidad × confianza promedio × número de personas que coinciden`.
|
|
98
|
+
|
|
99
|
+
## Paso 4 — Reporte y persistencia
|
|
100
|
+
|
|
101
|
+
Persistir en `.planning/loops/predecir-[timestamp]/` con
|
|
102
|
+
`hooks/lib/loop-telemetry.js` (tipo `predecir`, columnas `iteracion,
|
|
103
|
+
timestamp, hallazgo, severidad, confianza, personas, archivo_linea`; una fila
|
|
104
|
+
por hallazgo consensuado) + `escribirHandoff` con `source: 'swl:predecir'`,
|
|
105
|
+
`findings` rankeados y `config: {propuesta, scope}`.
|
|
106
|
+
|
|
107
|
+
Reportar al usuario:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
=== Predicción — [propuesta resumida] ===
|
|
111
|
+
Personas: [set] | Hallazgos brutos: N | Tras dedup: M
|
|
112
|
+
|
|
113
|
+
## Top hallazgos (rankeados)
|
|
114
|
+
| # | Hallazgo | Severidad | Confianza | Acuerdo | archivo:línea | Recomendación |
|
|
115
|
+
|
|
116
|
+
## Disensos registrados
|
|
117
|
+
- [persona X sostiene A; persona Y sostiene B — evidencia de cada una]
|
|
118
|
+
|
|
119
|
+
## Veredicto del panel
|
|
120
|
+
[PROCEDER | PROCEDER CON AJUSTES (lista) | REPLANTEAR (razón)]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Paso 5 — Encadenamiento (si `--chain planear-fase`)
|
|
124
|
+
|
|
125
|
+
Ofrecer arrancar `/swl:planear-fase` indicando el directorio del handoff. El
|
|
126
|
+
planificador incorpora los hallazgos `crítico`/`alto` como restricciones del
|
|
127
|
+
PLAN.md — cada uno se atiende con una tarea o se descarta con justificación
|
|
128
|
+
explícita (nunca silenciosamente).
|
|
129
|
+
|
|
130
|
+
## Reglas de comportamiento
|
|
131
|
+
|
|
132
|
+
- NUNCA compartir contexto entre personas — una invocación Agent por persona.
|
|
133
|
+
- NUNCA omitir el anti-herd check cuando hay unanimidad.
|
|
134
|
+
- Los hallazgos con archivo:línea citan código REAL verificado en el Paso 1 —
|
|
135
|
+
regla `verificar-citas-normativas.md § Familia 2` aplica al propio output.
|
|
136
|
+
- El comando NO modifica código — produce análisis. Implementar es del flujo
|
|
137
|
+
planear → ejecutar.
|
|
138
|
+
- Con 0 hallazgos crítico/alto: decirlo claramente y no inflar hallazgos
|
|
139
|
+
menores para justificar el costo del panel.
|
|
@@ -6,7 +6,7 @@ allowed_tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
|
|
|
6
6
|
|
|
7
7
|
# /swl:reflect-skills — Descubrimiento de skills emergentes
|
|
8
8
|
|
|
9
|
-
Analiza los archivos JSONL del historial de Claude Code (`~/.claude/projects/<proyecto>/*.jsonl`) y la cola de feedback del usuario (`.planning/
|
|
9
|
+
Analiza los archivos JSONL del historial de Claude Code (`~/.claude/projects/<proyecto>/*.jsonl`) y la cola de feedback del usuario (`.planning/evolution/feedback-queue.jsonl`) para detectar intenciones repetidas que podrían formalizarse como skills, comandos o agentes nuevos del sistema SWL.
|
|
10
10
|
|
|
11
11
|
Complementa a `/swl:aprender` (que trabaja sobre la sesión actual) y a `/swl:evolucionar` (que opera sobre metadatos de agentes/skills existentes). Este comando mira el **historial acumulado** y propone componentes emergentes basándose en la frecuencia real de uso.
|
|
12
12
|
|
|
@@ -41,7 +41,7 @@ Parámetros:
|
|
|
41
41
|
|
|
42
42
|
El script produce:
|
|
43
43
|
- Reporte textual en stdout
|
|
44
|
-
- Archivo estructurado en `.planning/
|
|
44
|
+
- Archivo estructurado en `.planning/evolution/reflect-skills-report.json`
|
|
45
45
|
|
|
46
46
|
### Paso 2 — Leer y clasificar los candidatos
|
|
47
47
|
|
package/comandos/swl/salud.md
CHANGED
|
@@ -290,7 +290,7 @@ SWL_AUDIT_FRAMEWORKS=1 npx -y @saulwade/swl-ses@latest audit-coverage-frameworks
|
|
|
290
290
|
npx -y @saulwade/swl-ses@latest audit-coverage-frameworks --save
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
-
Persiste en `.planning/
|
|
293
|
+
Persiste en `.planning/evolution/cobertura-frameworks.json` para comparación
|
|
294
294
|
histórica tras incorporar nuevos skills de seguridad.
|
|
295
295
|
|
|
296
296
|
Si `SWL_AUDIT_FRAMEWORKS` no está definida, este paso se omite sin mensaje
|
|
@@ -137,18 +137,30 @@ Verifica que los mensajes de commit siguen la convención del proyecto (si está
|
|
|
137
137
|
|
|
138
138
|
Delega al agente `revisor-codigo-swl` para revisión de código en profundidad.
|
|
139
139
|
|
|
140
|
+
**Presupuesto de contexto (anti-thrashing):** el subagente hereda `CLAUDE.md` +
|
|
141
|
+
reglas globales del proyecto; en proyectos rule-heavy eso consume buena parte de
|
|
142
|
+
su ventana antes de leer código (causa de autocompact thrashing con 0 tokens
|
|
143
|
+
útiles, observado 2026-06-05). Para evitarlo:
|
|
144
|
+
- Pasa al agente SOLO el diff / los archivos del alcance — nunca "revisa el proyecto".
|
|
145
|
+
- Instruye leer los archivos del alcance PRIMERO y cargar skills bajo demanda (solo
|
|
146
|
+
si el alcance lo amerita), no al inicio.
|
|
147
|
+
- Si el alcance > ~15 archivos o > ~2000 LOC, divídelo en lotes y delega uno por
|
|
148
|
+
invocación (cada subagente arranca con ventana limpia).
|
|
149
|
+
|
|
140
150
|
**Instrucción al agente revisor-codigo-swl:**
|
|
141
151
|
|
|
142
152
|
```
|
|
143
|
-
Revisa
|
|
153
|
+
Revisa SOLO los archivos del alcance de la Fase N del proyecto [nombre].
|
|
154
|
+
No explores el codebase completo: tu ventana ya hereda CLAUDE.md + reglas
|
|
155
|
+
globales; lee primero los archivos del alcance para no saturarla.
|
|
144
156
|
|
|
145
157
|
Archivos a revisar (en orden de prioridad):
|
|
146
|
-
[lista del RESUMEN.md]
|
|
158
|
+
[lista del RESUMEN.md / git diff del alcance]
|
|
147
159
|
|
|
148
|
-
Lee también:
|
|
149
|
-
- .planning/fases/0N-CONTEXTO.md (
|
|
150
|
-
- .planning/fases/0N-PLAN.md (
|
|
151
|
-
|
|
160
|
+
Lee también (solo lo necesario, bajo demanda):
|
|
161
|
+
- .planning/fases/0N-CONTEXTO.md (requisitos)
|
|
162
|
+
- .planning/fases/0N-PLAN.md (qué se debía hacer)
|
|
163
|
+
(CLAUDE.md ya está en tu contexto heredado — no lo releas.)
|
|
152
164
|
|
|
153
165
|
Para cada archivo revisado, verifica:
|
|
154
166
|
|
|
@@ -391,7 +403,38 @@ El VERIFICACION.md reportado al usuario al final del loop incluye una sección a
|
|
|
391
403
|
- Estado persistido: `.planning/fases/0N-converge-run-[timestamp].json`
|
|
392
404
|
```
|
|
393
405
|
|
|
394
|
-
### 4.6.7 —
|
|
406
|
+
### 4.6.7 — Telemetría de loop (obligatoria en `--until-converge`)
|
|
407
|
+
|
|
408
|
+
Además del estado estructurado de 4.6.5, cada corrida del loop registra su
|
|
409
|
+
trayectoria en el formato estándar de telemetría de loops
|
|
410
|
+
(`hooks/lib/loop-telemetry.js`), que habilita: inyección de estado por el hook
|
|
411
|
+
`contexto-iteracion.js` (anti-context-rot en sesiones largas), detección de
|
|
412
|
+
plateau, y lectura por `/swl:metricas`.
|
|
413
|
+
|
|
414
|
+
Al iniciar el loop (antes de la pasada 1):
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
node -e "const lt=require('./hooks/lib/loop-telemetry');const r=lt.iniciarCorrida({tipo:'verificar',direccion:'lower_is_better',config:{fase:'0N',maxIter:5}});console.log(r.dir)"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Tras CADA pasada, registrar una fila (métrica = hallazgos CRÍTICO+ALTO+MAYOR):
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
node -e "const lt=require('./hooks/lib/loop-telemetry');lt.registrarIteracion('<dir>',{iteracion:N,metrica:M,delta:D,estado:'keep',descripcion:'pasada N: X criticos, Y altos, Z mayores'})"
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Al cerrar el loop (cualquier señal de salida), escribir el handoff:
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
node -e "const lt=require('./hooks/lib/loop-telemetry');lt.escribirHandoff('<dir>',{source:'swl:verificar',status:'COMPLETO',findings:[/* hallazgos MEDIO/BAJO residuales */],config:{fase:'0N'}})"
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
`status` según la señal: A/D → `COMPLETO`, B → `INTERRUMPIDO`, C → `ACOTADO`.
|
|
433
|
+
Si `analizarTrayectoria()` reporta plateau antes de `--max-iter`, tratarlo como
|
|
434
|
+
señal C anticipada: seguir iterando sin mejora de métrica quema tokens sin
|
|
435
|
+
reducir hallazgos.
|
|
436
|
+
|
|
437
|
+
### 4.6.8 — Protocolo `--ci-aware` (Señal D)
|
|
395
438
|
|
|
396
439
|
Cuando `--ci-aware` está activo, el bucle de convergencia se extiende con un gate adicional ANTES de declarar Señal A como cierre definitivo:
|
|
397
440
|
|
|
@@ -123,12 +123,12 @@ Checklist para cualquier aplicación LLM en producción:
|
|
|
123
123
|
- [ ] Canary token en system prompt + validación en output (capa 2)
|
|
124
124
|
- [ ] Tool whitelist por agente documentada en frontmatter (capa 3)
|
|
125
125
|
- [ ] Confirmación humana para acciones irreversibles (capa 3)
|
|
126
|
-
- [ ] Logs de intentos detectados en `.planning/auto-
|
|
126
|
+
- [ ] Logs de intentos detectados en `.planning/auto-evolution/agentes.jsonl` con campo `tipo_fallo: prompt_injection`
|
|
127
127
|
|
|
128
128
|
## Gotchas / Errores comunes no obvios
|
|
129
129
|
|
|
130
130
|
- **Confiar solo en regex**: los atacantes reescriben payloads trivialmente (sinónimos, ofuscación base64, division de strings, caracteres Unicode similares). Causa: las firmas son conocidas y públicas; cualquier evasión simple las burla. Solución: combinar siempre capa 1 con capa 2 + capa 3; nunca tratar la detección de la capa 1 como suficiente. Si el regex pasa pero el heurístico marca anomalía estructural, escalar a clasificador o a humano.
|
|
131
|
-
- **Ignorar indirect injection porque "los usuarios son confiables"**: el input legítimo del usuario puede contener contenido externo no confiable (URL que se fetchea, archivo subido, documento RAG). Causa: el autor del payload no es quien interactúa — es quien puso el contenido en la ruta del agente. Solución: tratar
|
|
131
|
+
- **Ignorar indirect injection porque "los usuarios son confiables"**: el input legítimo del usuario puede contener contenido externo no confiable (URL que se fetchea, archivo subido, documento RAG). Causa: el autor del payload no es quien interactúa — es quien puso el contenido en la ruta del agente. Solución: tratar todo el contenido externo como datos, nunca como instrucciones; auditar especialmente ingesta de URLs, fetching de páginas web, y documentos en pipelines RAG.
|
|
132
132
|
- **Tratar el clasificador como oráculo binario**: `protectai/deberta-v3-base-prompt-injection-v2` es un modelo, no una verdad absoluta. Tiene falsos positivos y falsos negativos. Causa: los clasificadores son entrenados con datasets específicos; inputs legítimos de dominios no representados en el training pueden marcarse. Solución: usar el score como una señal entre varias, no como veredicto; calibrar el umbral contra un dataset propio de inputs legítimos antes de desplegar.
|
|
133
133
|
- **Canary tokens estáticos en el código**: incluir el mismo canary en repo público o en logs permite al atacante conocerlo y evitar filtrarlo. Causa: si el canary es predecible, el atacante lo filtra del output antes de devolverlo. Solución: generar canary por sesión o por deploy, nunca hardcodeado en el repo visible; rotarlo al menos por release.
|
|
134
134
|
- **Confiar en el frontmatter `tools:` del agente como único control**: el frontmatter es declaración leída por Claude Code, pero un agente con `tools: [Read]` todavía puede pedir `Bash` en su output y el runtime podría ejecutarlo si no hay enforcement. Causa: pre-aprobación ≠ restricción real. Solución: verificar que el permission mode de Claude Code o los hooks PreToolUse enforsan la restricción; no confiar solo en la declaración.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: angular-moderno
|
|
3
3
|
description: Angular v17+/v20+. Signals, standalone components, OnPush, host bindings, nueva sintaxis de control flow (@if/@for/@switch), defer blocks y patrones modernos.
|
|
4
|
-
version: "1.0.
|
|
4
|
+
version: "1.0.1"
|
|
5
|
+
evolved: true
|
|
6
|
+
evolved-from: "1.0.0"
|
|
7
|
+
evolved-at: "2026-06-04"
|
|
8
|
+
evolved-by: "evolucionar"
|
|
9
|
+
evolved-note: "PE-009 patrón Angular 19+ ErrorHandler custom con provideBrowserGlobalErrorListeners (NO listeners manuales window.onerror). Origen: OIC v1.5 Slice 6 2026-06-04."
|
|
5
10
|
herramientasPermitidas: [Read]
|
|
6
11
|
exclusiones:
|
|
7
12
|
- "No cargar para patrones Angular avanzados (zoneless, SSR, Resource API, interceptores funcionales) — para eso cargar `angular-avanzado`."
|
|
@@ -184,3 +189,41 @@ Para ejemplos completos de host bindings con clases dinámicas, services store c
|
|
|
184
189
|
**`input.required<T>()` accedido fuera del contexto de renderizado (en `constructor`) lanza error de runtime**: `this.factura()` dentro del `constructor` de un componente que usa `input.required<Factura>()` lanza `NG0950: Input is required but no value is available yet`. Causa: los inputs signal no tienen valor hasta que Angular completa la inicialización del componente. Fix: acceder a inputs en `ngOnInit`, en `computed()`, o en métodos del template — nunca en el constructor.
|
|
185
190
|
|
|
186
191
|
**`takeUntilDestroyed()` usado fuera del contexto de inyección lanza error**: `takeUntilDestroyed()` sin argumentos requiere un `DestroyRef` del contexto de inyección activo — si se llama dentro de un callback asíncrono (como `.then()` o `setTimeout`), el injection context ya no está activo. Causa: `inject()` solo funciona en contextos de inyección síncronos. Fix: capturar `DestroyRef` en el constructor con `private destroyRef = inject(DestroyRef)` y pasarlo explícitamente: `takeUntilDestroyed(this.destroyRef)`.
|
|
192
|
+
|
|
193
|
+
**Para captura global de errores runtime (window.onerror + unhandledrejection), usar `ErrorHandler` custom con `provideBrowserGlobalErrorListeners` — NO `window.addEventListener('error', ...)` manual** (Angular 19+; patrón portable OIC v1.5 2026-06-04): Angular 19+ provee `provideBrowserGlobalErrorListeners()` que ya registra los listeners nativos. Para reportar errores no controlados al backend (Sentry-style, audit handler, logs centralizados), proveer un `ErrorHandler` custom — listeners manuales duplican el registro y rompen la integración con el ciclo de DI/zone de Angular.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// app.config.ts
|
|
197
|
+
import { ApplicationConfig, ErrorHandler, provideBrowserGlobalErrorListeners } from '@angular/core';
|
|
198
|
+
|
|
199
|
+
export const appConfig: ApplicationConfig = {
|
|
200
|
+
providers: [
|
|
201
|
+
provideBrowserGlobalErrorListeners(), // 1. Listeners nativos
|
|
202
|
+
{ provide: ErrorHandler, useClass: GlobalErrorHandler }, // 2. Tu handler custom
|
|
203
|
+
// ...
|
|
204
|
+
],
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// global-error-handler.service.ts
|
|
208
|
+
@Injectable({ providedIn: 'root' })
|
|
209
|
+
export class GlobalErrorHandler implements ErrorHandler {
|
|
210
|
+
private readonly reporter = inject(ErrorReporterService);
|
|
211
|
+
|
|
212
|
+
handleError(error: unknown): void {
|
|
213
|
+
try {
|
|
214
|
+
this.reporter.reportarErrorGlobal(error, 'global-error-handler');
|
|
215
|
+
} catch {
|
|
216
|
+
// Silenciar fallos del reporter — un handler global NUNCA debe propagar.
|
|
217
|
+
}
|
|
218
|
+
console.error(error); // Preservar visibilidad en DevTools.
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Causa**: con `provideBrowserGlobalErrorListeners` activo, Angular ya escucha `window.onerror`/`unhandledrejection` y los enruta a `ErrorHandler.handleError()`. Registrar listeners manuales propios duplica el callback (cada error se reporta dos veces) y se pierde la integración con el lifecycle Angular (zone tracking, DI context). **Fix**: usar EXCLUSIVAMENTE el `ErrorHandler` custom + `provideBrowserGlobalErrorListeners`.
|
|
224
|
+
|
|
225
|
+
**Reglas del handler**:
|
|
226
|
+
- `try/except` agresivo: el reporter NUNCA debe propagar al `handleError` (riesgo de recursión si el propio POST de reporte falla).
|
|
227
|
+
- Delegar a `console.error(error)` para preservar DevTools (no silenciar para devs).
|
|
228
|
+
- El reporter debe tener throttle propio (ej: 10 eventos/5s) y silenciar respuestas esperadas (401/403/404/429) — los detalles del reporter viven en su propio service, no en el `ErrorHandler`.
|
|
229
|
+
- Anti-bucle: el `ErrorReporterInterceptor` (si existe) debe excluir llamadas al propio endpoint POST de reportes.
|
|
@@ -194,7 +194,7 @@ ver [recursos/referencia-completa.md](recursos/referencia-completa.md).
|
|
|
194
194
|
## Parser de señales de fricción (v1.1)
|
|
195
195
|
|
|
196
196
|
El hook `hooks/auto-evolucion.js` (SubagentStop) alimenta el log
|
|
197
|
-
`.planning/auto-
|
|
197
|
+
`.planning/auto-evolution/agentes.jsonl` y emite *nudges* hacia
|
|
198
198
|
`auto-evolucion-swl` cuando detecta umbrales cruzados. Este skill define
|
|
199
199
|
**qué cuenta como fricción** para que el agente tenga criterio uniforme al
|
|
200
200
|
interpretar el log.
|
|
@@ -233,7 +233,7 @@ Al invocar `auto-evolucion-swl` o `/swl:evolucionar <agente>`:
|
|
|
233
233
|
|
|
234
234
|
1. **Leer el log filtrado por agente:**
|
|
235
235
|
```bash
|
|
236
|
-
grep "\"agente\":\"<nombre>\"" .planning/auto-
|
|
236
|
+
grep "\"agente\":\"<nombre>\"" .planning/auto-evolution/agentes.jsonl | tail -50
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
2. **Cuantificar las 6 categorías** (fallo duro, run vacío, trivial recurrente,
|
|
@@ -7,7 +7,9 @@ description: >
|
|
|
7
7
|
y mejorar skills hasta 95%+ de score. Cargar cuando se quiera mejorar la calidad
|
|
8
8
|
de un skill o agente existente de forma medible y automatizada, o cuando
|
|
9
9
|
auto-evolucion-swl necesite una metodología de mejora iterativa con scoring.
|
|
10
|
-
|
|
10
|
+
La disciplina del loop (mutación atómica, keep/revert, condiciones de salida)
|
|
11
|
+
aplica también al modo --codigo de /swl:autoresearch sobre código del usuario.
|
|
12
|
+
version: "1.1.0"
|
|
11
13
|
herramientasPermitidas: [Read, Bash]
|
|
12
14
|
exclusiones:
|
|
13
15
|
- "No cargar para mejorar el output de una sola sesión; el loop de autoresearch opera sobre el SKILL.md del skill, no sobre outputs puntuales."
|
|
@@ -210,6 +212,18 @@ Mutaciones KEEP: [N] | Mutaciones REVERT: [N]
|
|
|
210
212
|
| 3 reverts consecutivos | **Estancamiento** — cambiar estrategia de mutación |
|
|
211
213
|
| Score baja 2 rounds seguidos | **Degradación** — revertir al mejor score alcanzado |
|
|
212
214
|
|
|
215
|
+
## Variante: loop sobre código del usuario (modo `--codigo`)
|
|
216
|
+
|
|
217
|
+
La misma disciplina aplica cuando el objetivo es código del proyecto en lugar
|
|
218
|
+
de un SKILL.md: el checklist se sustituye por un **comando Verify numérico**
|
|
219
|
+
(mutation score, cobertura, conteo de errores, latencia) y un **Guard** de
|
|
220
|
+
regresión (la suite). El protocolo completo del modo vive en
|
|
221
|
+
`comandos/swl/autoresearch.md § Modo --codigo`; la telemetría de iteraciones
|
|
222
|
+
en `hooks/lib/loop-telemetry.js` (corridas en `.planning/loops/`); las
|
|
223
|
+
métricas de mutación en `Skill("calidad-mutation-testing")`. Las reglas
|
|
224
|
+
invariantes son las mismas: UNA mutación por round, revert sin excepciones si
|
|
225
|
+
la métrica no mejora, salida por plateau.
|
|
226
|
+
|
|
213
227
|
## Integración con auto-evolución SWL
|
|
214
228
|
|
|
215
229
|
```
|
|
@@ -122,7 +122,7 @@ node scripts/benchmark-memoria.js --verbose
|
|
|
122
122
|
### Tracking histórico opcional
|
|
123
123
|
|
|
124
124
|
Si se setea `SWL_BENCHMARK_PERSIST=1`, el benchmark escribe el resumen
|
|
125
|
-
agregado a `.planning/
|
|
125
|
+
agregado a `.planning/evolution/benchmark-memoria.jsonl` (append-only)
|
|
126
126
|
para detectar regresión entre releases:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
@@ -131,7 +131,7 @@ SWL_BENCHMARK_PERSIST=1 node scripts/benchmark-memoria.js
|
|
|
131
131
|
|
|
132
132
|
Comparar entre releases:
|
|
133
133
|
```bash
|
|
134
|
-
tail -5 .planning/
|
|
134
|
+
tail -5 .planning/evolution/benchmark-memoria.jsonl | jq -c '{ts: .timestamp, r5: .promedio.recall_at_5}'
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
---
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: calidad-mutation-testing
|
|
3
|
+
description: >
|
|
4
|
+
Mutation testing: medir la calidad real de una suite de tests inyectando
|
|
5
|
+
mutantes (cambios sintácticos pequeños) en el código y verificando que los
|
|
6
|
+
tests los maten. Cubre herramientas por stack (Stryker, mutmut, cargo-mutants,
|
|
7
|
+
PIT, Stryker.NET, Infection), interpretación de mutantes sobrevivientes,
|
|
8
|
+
modo incremental para que el costo sea viable, y uso del mutation score como
|
|
9
|
+
métrica Verify/Guard en loops de autoresearch o como gate opcional de
|
|
10
|
+
tdd-qa-swl. Cargar cuando la cobertura de líneas es alta pero se sospecha de
|
|
11
|
+
asserts débiles, al endurecer la suite de un módulo crítico, o al configurar
|
|
12
|
+
el gate de mutación en CI.
|
|
13
|
+
version: "1.0.0"
|
|
14
|
+
herramientasPermitidas: [Read, Bash, Grep, Glob]
|
|
15
|
+
exclusiones:
|
|
16
|
+
- "No cargar si la suite no está verde y estable — el mutation testing presupone tests deterministas que pasan; con tests flaky el score es ruido."
|
|
17
|
+
- "No cargar para subir cobertura de líneas — eso es tdd-workflow; la mutación mide calidad de asserts, no cantidad de tests."
|
|
18
|
+
- "No cargar en proyectos sin tests — primero tdd-qa-swl construye la suite; mutar sin tests produce 100% de sobrevivientes sin información."
|
|
19
|
+
evolvable: true
|
|
20
|
+
---
|
|
21
|
+
# Mutation Testing — La Suite que Vigila a la Suite
|
|
22
|
+
|
|
23
|
+
La cobertura de líneas responde "¿qué código ejecutan los tests?". El mutation
|
|
24
|
+
testing responde la pregunta importante: **"¿los tests detectarían un bug?"**.
|
|
25
|
+
Una suite con 90% de cobertura y asserts débiles pasa el gate de `pruebas.md`
|
|
26
|
+
sin proteger nada — los mutantes sobrevivientes lo exponen.
|
|
27
|
+
|
|
28
|
+
**Principio**: un test que no mata mutantes documenta ejecución, no comportamiento.
|
|
29
|
+
|
|
30
|
+
## Cuándo cargar este skill
|
|
31
|
+
|
|
32
|
+
- Módulo crítico (pagos, auth, cálculo) con cobertura alta pero bugs que se
|
|
33
|
+
filtran a producción — sospecha de asserts débiles.
|
|
34
|
+
- Cerrar el ciclo TDD estilo Uncle Bob: spec → tests → código → revisión →
|
|
35
|
+
**mutación** como verificación final de la suite.
|
|
36
|
+
- Configurar mutation score como métrica de un loop `/swl:autoresearch --codigo`
|
|
37
|
+
o como gate opcional en CI.
|
|
38
|
+
- Auditar la suite que entrega `tdd-qa-swl` antes de declarar una fase verde.
|
|
39
|
+
|
|
40
|
+
## Cómo funciona
|
|
41
|
+
|
|
42
|
+
1. La herramienta genera **mutantes**: copias del código con un cambio mínimo
|
|
43
|
+
(`>` → `>=`, `+` → `-`, borrar una llamada, `true` → `false`).
|
|
44
|
+
2. Corre la suite contra cada mutante.
|
|
45
|
+
3. Clasifica: **muerto** (algún test falló — bien), **sobreviviente** (la suite
|
|
46
|
+
pasó con el bug inyectado — gap real), **timeout** (cuenta como muerto),
|
|
47
|
+
**no cubierto** (ningún test lo ejecuta — gap de cobertura clásico).
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
mutation score = mutantes muertos / (mutantes totales − equivalentes) × 100
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Herramientas por stack
|
|
54
|
+
|
|
55
|
+
Antes de instalar, verificar versión vigente con Context7
|
|
56
|
+
(regla `usar-context7.md`) — los nombres de paquete cambian entre majors.
|
|
57
|
+
|
|
58
|
+
| Stack | Herramienta | Comando típico | Score en JSON |
|
|
59
|
+
|-------|------------|----------------|---------------|
|
|
60
|
+
| JS/TS | Stryker (`@stryker-mutator/core`) | `npx stryker run` | `reports/mutation/mutation.json` → `.thresholds` / score en summary |
|
|
61
|
+
| Python | `mutmut` | `mutmut run && mutmut results` | `mutmut junitxml` o parsear `mutmut results` |
|
|
62
|
+
| Python (alterno) | `cosmic-ray` | `cosmic-ray init/exec/dump` | `cr-report --json` |
|
|
63
|
+
| Rust | `cargo-mutants` | `cargo mutants` | `mutants.out/outcomes.json` |
|
|
64
|
+
| Java/Kotlin | PIT (`pitest`) | `mvn org.pitest:pitest-maven:mutationCoverage` | `target/pit-reports/mutations.xml` |
|
|
65
|
+
| C#/.NET | Stryker.NET (`dotnet-stryker`) | `dotnet stryker` | `StrykerOutput/**/mutation-report.json` |
|
|
66
|
+
| PHP | Infection | `vendor/bin/infection` | `infection-log.json` → MSI |
|
|
67
|
+
| Go | `gremlins` | `gremlins unleash` | salida estructurada con `--output` |
|
|
68
|
+
|
|
69
|
+
## Hacer viable el costo — modo incremental SIEMPRE
|
|
70
|
+
|
|
71
|
+
Mutar el proyecto completo es O(mutantes × duración de suite). En un repo
|
|
72
|
+
mediano son horas. Reglas para que sea operable:
|
|
73
|
+
|
|
74
|
+
- **Mutar solo lo que cambió**: Stryker `--since`/modo incremental, `mutmut`
|
|
75
|
+
con `--paths-to-mutate`, `cargo mutants --in-diff <(git diff main)`. El gate
|
|
76
|
+
de PR muta el diff, no el repo.
|
|
77
|
+
- **Acotar al módulo crítico**: configurar `mutate:` solo sobre
|
|
78
|
+
`src/pagos/**`, no sobre todo `src/`. El score global de un repo grande es
|
|
79
|
+
una métrica vanidosa; el score del módulo de dinero es accionable.
|
|
80
|
+
- **Suite rápida primero**: si la suite tarda >2 min, mutar solo con los tests
|
|
81
|
+
unitarios del módulo (los runners permiten filtrar la suite que ejecutan).
|
|
82
|
+
- **Paralelizar**: todos los runners soportan concurrencia
|
|
83
|
+
(`--concurrency`, `--jobs`); default razonable: núcleos − 1.
|
|
84
|
+
|
|
85
|
+
## Interpretar mutantes sobrevivientes
|
|
86
|
+
|
|
87
|
+
Cada sobreviviente es una de tres cosas — diagnosticar antes de actuar:
|
|
88
|
+
|
|
89
|
+
| Diagnóstico | Señal | Acción |
|
|
90
|
+
|-------------|-------|--------|
|
|
91
|
+
| **Assert débil** | el test ejecuta la línea pero no verifica el resultado mutado | Endurecer el assert (caso típico: verifica que no lanza, no el valor) |
|
|
92
|
+
| **Test faltante** | ningún test cubre el comportamiento de esa rama | Escribir test de frontera dirigido al mutante (`tdd-qa-swl`) |
|
|
93
|
+
| **Mutante equivalente** | el mutante no cambia el comportamiento observable (ej: optimización interna) | Excluirlo/anotarlo — NO escribir un test artificial para matarlo |
|
|
94
|
+
|
|
95
|
+
Anti-patrón crítico: escribir tests que asertan detalles de implementación
|
|
96
|
+
solo para matar mutantes — eso acopla la suite y degrada el refactor. El
|
|
97
|
+
mutante manda la pregunta; el test responde al **comportamiento**.
|
|
98
|
+
|
|
99
|
+
## Umbrales recomendados
|
|
100
|
+
|
|
101
|
+
| Contexto | Score objetivo | Justificación |
|
|
102
|
+
|----------|---------------|---------------|
|
|
103
|
+
| Módulo crítico (dinero, auth, cálculo regulatorio) | ≥ 85% | Un bug aquí cuesta más que el CI lento |
|
|
104
|
+
| Lógica de negocio estándar | ≥ 70% | Equilibrio costo/valor |
|
|
105
|
+
| Glue code, configs, controllers delgados | sin gate | El esfuerzo no paga; cubrir con tests de integración |
|
|
106
|
+
|
|
107
|
+
No imponer un score global de repo: produce esfuerzo uniforme sobre código de
|
|
108
|
+
valor desigual. Gates por módulo, declarados en la config del runner.
|
|
109
|
+
|
|
110
|
+
## Uso como métrica en loops SWL
|
|
111
|
+
|
|
112
|
+
El mutation score es la métrica ideal para `/swl:autoresearch --codigo` porque
|
|
113
|
+
es numérica, determinista y su Guard natural es la propia suite:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Goal: subir mutation score de src/pagos/ de 62% a 85%
|
|
117
|
+
Scope: tests/pagos/**
|
|
118
|
+
Metric: mutation score (higher_is_better)
|
|
119
|
+
Verify: npx stryker run --mutate "src/pagos/**" --incremental && <extraer score del JSON>
|
|
120
|
+
Guard: npm test (la suite completa sigue verde)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Cada iteración: agregar/endurecer UN test → correr Verify → keep si el score
|
|
124
|
+
sube y el Guard pasa. Registrar con `hooks/lib/loop-telemetry.js`.
|
|
125
|
+
|
|
126
|
+
**Gate opcional en tdd-qa-swl**: tras alcanzar cobertura ≥80%, correr mutación
|
|
127
|
+
incremental sobre el diff de la fase; sobrevivientes con diagnóstico "assert
|
|
128
|
+
débil" o "test faltante" se atienden antes del cierre (regla
|
|
129
|
+
`arreglar-al-detectar.md`). Es opt-in: requiere runner instalado y suite <2 min.
|
|
130
|
+
|
|
131
|
+
## Cuándo NO cargar
|
|
132
|
+
|
|
133
|
+
- Suite roja o flaky — primero estabilizar (`pruebas.md § deterministas`); el
|
|
134
|
+
mutation testing amplifica el ruido de tests no deterministas.
|
|
135
|
+
- Prototipo o spike de descarte — el costo del setup no se recupera.
|
|
136
|
+
- Presupuesto de CI ya saturado — correr mutación local/nightly, no por PR,
|
|
137
|
+
hasta resolver el presupuesto.
|
|
138
|
+
|
|
139
|
+
## Gotchas / Errores comunes no obvios
|
|
140
|
+
|
|
141
|
+
- **El score baja al agregar código nuevo bien testeado**: el denominador
|
|
142
|
+
crece con mutantes del código nuevo; si el módulo viejo tenía deuda, el
|
|
143
|
+
score agregado oscila. Causa: medir score global en vez de por módulo/diff.
|
|
144
|
+
Solución: gates incrementales (`--since`, `--in-diff`) — el código nuevo se
|
|
145
|
+
evalúa contra su propio diff.
|
|
146
|
+
- **Timeouts contados como éxito inflan el score**: un mutante que cuelga la
|
|
147
|
+
suite cuenta como "muerto" aunque ningún assert lo detectó. Si el módulo
|
|
148
|
+
tiene loops sensibles, revisar el desglose `timeout` vs `killed` antes de
|
|
149
|
+
celebrar — un ratio de timeouts >10% amerita bajar el timeout factor.
|
|
150
|
+
- **Mutación sobre código generado**: mutar archivos generados (protobuf,
|
|
151
|
+
cliente OpenAPI, migraciones) quema horas sin información. Excluirlos
|
|
152
|
+
explícitamente en la config del runner desde el día uno.
|
|
153
|
+
- **mutmut y el cache stale**: `mutmut` cachea resultados en `.mutmut-cache`;
|
|
154
|
+
tras un refactor grande el cache puede reportar resultados de código que ya
|
|
155
|
+
no existe. Ante números inverosímiles: borrar el cache y re-correr.
|
|
156
|
+
- **Stryker incremental tras rebase**: el archivo `.stryker-tmp`/incremental
|
|
157
|
+
referencia commits que el rebase reescribió — el modo incremental se
|
|
158
|
+
degrada a corrida completa sin avisar. Presupuestar la primera corrida
|
|
159
|
+
post-rebase como completa.
|
|
160
|
+
|
|
161
|
+
## Anti-patrones
|
|
162
|
+
|
|
163
|
+
- **Tests escritos para matar mutantes, no para verificar comportamiento** —
|
|
164
|
+
acoplan la suite a la implementación.
|
|
165
|
+
- **Score global de repo como KPI de equipo** — métrica vanidosa; gates por
|
|
166
|
+
módulo crítico.
|
|
167
|
+
- **Correr mutación completa en cada PR** — CI de horas; incremental por diff
|
|
168
|
+
en PR, completa nightly.
|
|
169
|
+
- **Ignorar el desglose y mirar solo el porcentaje** — los sobrevivientes
|
|
170
|
+
individuales son la información; el score es solo el resumen.
|