@saulwade/swl-ses 1.7.2 → 1.7.4

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 CHANGED
@@ -1,4 +1,4 @@
1
- # CLAUDE.md — @saulwade/swl-ses v1.7.2
1
+ # CLAUDE.md — @saulwade/swl-ses v1.7.4
2
2
 
3
3
  ## Reglas de máxima prioridad (aplican SIEMPRE, sin excepción)
4
4
 
@@ -95,7 +95,7 @@ NUNCA asumir, sugerir como hecho consumado, ni escribir en ADRs/manifiestos/CHAN
95
95
  ## Qué es este repositorio
96
96
 
97
97
  Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo).
98
- 11 lenguajes, 7 runtimes (Claude, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot), 61 agentes, 177 skills, 44 comandos, 71 reglas, 43 hooks.
98
+ 11 lenguajes, 7 runtimes (Claude, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot), 61 agentes, 178 skills, 44 comandos, 71 reglas, 43 hooks.
99
99
 
100
100
  ## Estructura del repositorio
101
101
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # swl-ses v1.7.2
1
+ # swl-ses v1.7.4
2
2
 
3
3
  > El paquete anterior `@saulwadeleon/swl-software-engineering-system` está deprecado. Migrar a `@saulwade/swl-ses` (npmjs.org canónico) o `@saul-wade/swl-ses` (mirror en GitHub Packages) — el CLI `swl-ses` no cambia.
4
4
 
@@ -195,7 +195,7 @@ claude
195
195
  | `mobile` | Android + iOS + React Native/Flutter + UX |
196
196
  | `devops` | CI/CD + cloud + observabilidad + releases + seguridad |
197
197
  | `polyglot` | Todos los lenguajes: 11 lenguajes + revisores + build resolvers |
198
- | `completo` | Todo: 61 agentes + 177 habilidades + 44 comandos + 71 reglas + 43 hooks |
198
+ | `completo` | Todo: 61 agentes + 178 habilidades + 44 comandos + 71 reglas + 43 hooks |
199
199
 
200
200
  ### Targets soportados
201
201
 
@@ -0,0 +1,368 @@
1
+ ---
2
+ name: calidad-anti-patrones-universales
3
+ description: >
4
+ Catálogo de 10 anti-patrones universales de calidad de código, nombrados con
5
+ vocabulario común y ejemplos MAL/BIEN en Python y TypeScript. Cubre auditoría
6
+ de reutilización, proliferación de parámetros, abstracciones con fuga, tipado
7
+ por string, condicionales anidadas, variantes copy-paste, actualizaciones sin
8
+ efecto, race conditions TOCTOU, operaciones demasiado amplias y estado
9
+ redundante. Cargar desde revisor-codigo-swl o revisores especializados cuando
10
+ el código bajo revisión presenta señales (>4 parámetros, magic strings en
11
+ condiciones, if anidadas ≥3 niveles, queries en loops, dos funciones casi
12
+ idénticas, mutaciones de estado sin guard). NO sustituye a los revisores —
13
+ los enriquece con vocabulario nombrado para reportes consistentes.
14
+ version: "1.0.0"
15
+ herramientasPermitidas: [Read, Grep, Glob]
16
+ exclusiones:
17
+ - "No cargar como sustituto de revisor-codigo-swl ni de los revisores especializados — este skill aporta vocabulario y ejemplos, los revisores ejecutan el análisis."
18
+ - "No cargar para code review con foco en seguridad (OWASP, inyección, autenticación) — para eso cargar checklist-seguridad."
19
+ - "No cargar para análisis de rendimiento (queries lentas, profiling) — eso corresponde a rendimiento-swl + performance-baseline."
20
+ - "No cargar para refactor agresivo de código heredado — eso corresponde a legacy-code-rescue."
21
+ evolvable: true
22
+ ---
23
+
24
+ # Habilidad: Catálogo de anti-patrones universales de calidad
25
+
26
+ Catálogo de 10 anti-patrones nombrados que aparecen cross-lenguaje. Cada entrada
27
+ incluye señal de detección, ejemplo MAL, ejemplo BIEN y vocabulario para el
28
+ reporte del revisor.
29
+
30
+ ## Cuándo cargar
31
+
32
+ Desde `revisor-codigo-swl` o un revisor especializado (`revisor-typescript-swl`,
33
+ `revisor-react-swl`, `revisor-python-swl`, etc.) cuando el código bajo revisión
34
+ muestra al menos una de las señales:
35
+
36
+ - Función con ≥5 parámetros posicionales
37
+ - Condicionales anidadas con ≥3 niveles
38
+ - Magic strings repetidos en condiciones o switches
39
+ - Dos funciones que difieren solo en nombres de campos
40
+ - Acceso a propiedades de un ORM o cliente HTTP desde código de negocio
41
+ - Llamadas a BD dentro de un `for` (loops con `await` en cada iteración)
42
+ - Mutación de estado sin guard previo
43
+ - Función con verbo genérico (`process`, `handle`, `manage`) que afecta múltiples dominios
44
+
45
+ ## Cuándo NO cargar
46
+
47
+ - Code review enfocado en seguridad → `checklist-seguridad`
48
+ - Profiling y queries lentas → `rendimiento-swl` + `performance-baseline`
49
+ - Refactor de código heredado → `legacy-code-rescue`
50
+ - Implementación de feature nueva → cargar el skill del stack (`fastapi-experto`, `react-experto`, etc.)
51
+
52
+ ---
53
+
54
+ ## Los 10 anti-patrones
55
+
56
+ ### 1. Auditoría de reutilización (reuse audit)
57
+
58
+ Antes de aceptar código nuevo, buscar utilidades existentes que ya hagan lo mismo.
59
+
60
+ **MAL** — debounce hecho a mano cuando `utils/debounce.ts` ya existe:
61
+ ```typescript
62
+ function debounce(fn: Function, ms: number) {
63
+ let timer: ReturnType<typeof setTimeout>;
64
+ return (...args: unknown[]) => {
65
+ clearTimeout(timer);
66
+ timer = setTimeout(() => fn(...args), ms);
67
+ };
68
+ }
69
+ ```
70
+
71
+ **BIEN** — importar la utilidad del proyecto:
72
+ ```typescript
73
+ import { debounce } from '@/utils/debounce';
74
+ ```
75
+
76
+ **Reporte**: "Reuse audit — `<archivo:línea>` reimplementa `<utilidad-existente>`. Importar en su lugar."
77
+
78
+ ---
79
+
80
+ ### 2. Proliferación de parámetros (parameter sprawl)
81
+
82
+ Una función con ≥5 parámetros posicionales no escala. Cada nueva necesidad agrega otro.
83
+
84
+ **MAL**:
85
+ ```python
86
+ def crear_usuario(nombre, email, rol, equipo, activo, avatar_url, zona_horaria):
87
+ ...
88
+ ```
89
+
90
+ **BIEN** — objeto de parámetros con defaults:
91
+ ```python
92
+ from dataclasses import dataclass
93
+
94
+ @dataclass
95
+ class CrearUsuarioParams:
96
+ nombre: str
97
+ email: str
98
+ rol: Rol = Rol.MIEMBRO
99
+ equipo: str | None = None
100
+ activo: bool = True
101
+ avatar_url: str | None = None
102
+ zona_horaria: str = "America/Mexico_City"
103
+
104
+ def crear_usuario(params: CrearUsuarioParams) -> Usuario:
105
+ ...
106
+ ```
107
+
108
+ **Reporte**: "Parameter sprawl — `<función>` recibe N parámetros. Consolidar en dataclass/interface."
109
+
110
+ Señal adicional: parámetros booleanos mutuamente exclusivos (`activar_x`, `desactivar_y`) → enum o strategy pattern.
111
+
112
+ ---
113
+
114
+ ### 3. Abstracciones con fuga (leaky abstractions)
115
+
116
+ Exponer detalles internos obliga al llamador a conocerlos.
117
+
118
+ **MAL** — el caller recibe un objeto ORM:
119
+ ```python
120
+ def listar_usuarios():
121
+ return session.query(Usuario).filter(Usuario.activo == True).all()
122
+ ```
123
+
124
+ **BIEN** — el caller recibe un DTO de dominio:
125
+ ```python
126
+ def listar_usuarios_activos() -> list[UsuarioDTO]:
127
+ filas = usuario_repo.find_activos()
128
+ return [UsuarioDTO.from_row(f) for f in filas]
129
+ ```
130
+
131
+ **Reporte**: "Leaky abstraction — `<función>` retorna objeto ORM/cliente; envolver en DTO."
132
+
133
+ ---
134
+
135
+ ### 4. Tipado por string (stringly-typed)
136
+
137
+ Magic strings esparcidos por el código en lugar de constantes o enums.
138
+
139
+ **MAL**:
140
+ ```typescript
141
+ if (usuario.rol === 'admin') { ... }
142
+ if (usuario.rol === 'Admin') { ... } // typo silencioso
143
+ if (pedido.estado === 'pendiente') { ... }
144
+ ```
145
+
146
+ **BIEN** — enum tipado:
147
+ ```typescript
148
+ enum Rol { Admin = 'admin', Miembro = 'miembro' }
149
+ enum EstadoPedido { Pendiente = 'pendiente', Confirmado = 'confirmado' }
150
+
151
+ if (usuario.rol === Rol.Admin) { ... }
152
+ ```
153
+
154
+ **Reporte**: "Stringly-typed — `<archivo:línea>` compara contra string literal repetido. Extraer enum/constante."
155
+
156
+ ---
157
+
158
+ ### 5. Condicionales anidadas (nested conditionals)
159
+
160
+ `if/else` con ≥3 niveles o cadenas ternarias rompen la lectura lineal.
161
+
162
+ **MAL**:
163
+ ```python
164
+ if usuario:
165
+ if usuario.activo:
166
+ if usuario.tiene_permiso("editar"):
167
+ if recurso.disponible:
168
+ return editar(usuario, recurso)
169
+ ```
170
+
171
+ **BIEN** — early return con guard clauses:
172
+ ```python
173
+ def editar_recurso(usuario, recurso):
174
+ if not usuario or not usuario.activo:
175
+ raise UsuarioInvalidoError
176
+ if not usuario.tiene_permiso("editar"):
177
+ raise PermisoDenegadoError
178
+ if not recurso.disponible:
179
+ raise RecursoNoDisponibleError
180
+ return editar(usuario, recurso)
181
+ ```
182
+
183
+ **Reporte**: "Nested conditionals — `<función>` tiene N niveles de anidamiento. Aplicar guard clauses con early return."
184
+
185
+ ---
186
+
187
+ ### 6. Variantes copy-paste (copy-paste variants)
188
+
189
+ Dos bloques de código casi idénticos que solo difieren en nombres de campos o constantes.
190
+
191
+ **MAL**:
192
+ ```python
193
+ def calcular_iva(monto):
194
+ return monto * 0.16
195
+
196
+ def calcular_ieps(monto):
197
+ return monto * 0.08
198
+
199
+ def calcular_isr(monto):
200
+ return monto * 0.30
201
+ ```
202
+
203
+ **BIEN** — abstracción con tabla de tasas:
204
+ ```python
205
+ TASAS = {"iva": 0.16, "ieps": 0.08, "isr": 0.30}
206
+
207
+ def calcular_impuesto(monto: Decimal, tipo: str) -> Decimal:
208
+ return monto * TASAS[tipo]
209
+ ```
210
+
211
+ **Reporte**: "Copy-paste variants — funciones `<a>`, `<b>` difieren solo en constantes. Consolidar."
212
+
213
+ Cuidado: NO consolidar si las funciones evolucionan independientemente por razones de negocio distintas (regla de tres).
214
+
215
+ ---
216
+
217
+ ### 7. Actualizaciones sin efecto (no-op updates)
218
+
219
+ Disparar `setState`, `UPDATE` o re-render sin verificar que el valor cambió.
220
+
221
+ **MAL** — re-render cada vez aunque `usuario` sea el mismo:
222
+ ```typescript
223
+ useEffect(() => {
224
+ setUsuarioActivo(usuario);
225
+ }, [usuario]);
226
+ ```
227
+
228
+ **BIEN** — comparar antes de actualizar:
229
+ ```typescript
230
+ useEffect(() => {
231
+ if (usuario && usuario.id !== usuarioActivoActual?.id) {
232
+ setUsuarioActivo(usuario);
233
+ }
234
+ }, [usuario, usuarioActivoActual]);
235
+ ```
236
+
237
+ **Reporte**: "No-op update — `<archivo:línea>` actualiza estado sin guard de cambio. Verificar si el valor difiere."
238
+
239
+ ---
240
+
241
+ ### 8. TOCTOU — Time-of-Check vs Time-of-Use
242
+
243
+ Entre la verificación y el uso, el estado puede cambiar. Race condition silenciosa.
244
+
245
+ **MAL**:
246
+ ```python
247
+ if os.path.exists(ruta):
248
+ with open(ruta) as f: # archivo puede haber desaparecido aquí
249
+ return f.read()
250
+ ```
251
+
252
+ **BIEN** — intentar y manejar excepción:
253
+ ```python
254
+ try:
255
+ with open(ruta) as f:
256
+ return f.read()
257
+ except FileNotFoundError:
258
+ return None
259
+ ```
260
+
261
+ **MAL** en BD — check-then-act sin transacción:
262
+ ```python
263
+ saldo = obtener_saldo(cuenta_id)
264
+ if saldo >= monto:
265
+ debitar(cuenta_id, monto) # otro proceso pudo cambiar el saldo
266
+ ```
267
+
268
+ **BIEN** — lock pesimista o transacción atómica:
269
+ ```python
270
+ async with conn.transaction():
271
+ saldo = await obtener_saldo_for_update(cuenta_id) # SELECT ... FOR UPDATE
272
+ if saldo < monto:
273
+ raise SaldoInsuficienteError
274
+ await debitar(cuenta_id, monto)
275
+ ```
276
+
277
+ **Reporte**: "TOCTOU — `<archivo:línea>` verifica condición fuera de transacción/lock. Reordenar a try-catch o SELECT FOR UPDATE."
278
+
279
+ ---
280
+
281
+ ### 9. Operaciones demasiado amplias (overly broad operations)
282
+
283
+ Funciones con verbos genéricos (`process`, `handle`, `manage`) que tocan múltiples dominios.
284
+
285
+ **MAL**:
286
+ ```typescript
287
+ async function procesarPedido(pedido: Pedido) {
288
+ await validarCliente(pedido.clienteId);
289
+ await debitarInventario(pedido.items);
290
+ await cobrarTarjeta(pedido.pago);
291
+ await enviarEmail(pedido.clienteId);
292
+ await registrarAuditoria(pedido);
293
+ await notificarAlmacen(pedido);
294
+ }
295
+ ```
296
+
297
+ **BIEN** — fases nombradas, una responsabilidad por función:
298
+ ```typescript
299
+ async function emitirPedido(pedido: Pedido) {
300
+ const validado = await validarPedido(pedido);
301
+ const cobrado = await procesarCobro(validado);
302
+ const confirmado = await confirmarPedido(cobrado);
303
+ await notificarParticipantes(confirmado);
304
+ return confirmado;
305
+ }
306
+ ```
307
+
308
+ **Reporte**: "Overly broad operation — `<función>` orquesta N dominios distintos. Dividir por fase o extraer use cases."
309
+
310
+ ---
311
+
312
+ ### 10. Estado redundante (redundant state)
313
+
314
+ Mantener estado derivable de otro estado introduce drift garantizado.
315
+
316
+ **MAL**:
317
+ ```typescript
318
+ const [items, setItems] = useState<Item[]>([]);
319
+ const [total, setTotal] = useState(0); // derivable de items
320
+ const [vacio, setVacio] = useState(true); // derivable de items.length
321
+ ```
322
+
323
+ **BIEN** — derivar con `useMemo`:
324
+ ```typescript
325
+ const [items, setItems] = useState<Item[]>([]);
326
+ const total = useMemo(() => items.reduce((s, i) => s + i.precio, 0), [items]);
327
+ const vacio = items.length === 0;
328
+ ```
329
+
330
+ **Reporte**: "Redundant state — `<componente>` mantiene N campos derivables de uno solo. Computar con `useMemo` o getter."
331
+
332
+ ---
333
+
334
+ ## Formato de reporte sugerido
335
+
336
+ Cuando un revisor detecta uno o más anti-patrones, integrarlo en su reporte
337
+ estándar con esta forma:
338
+
339
+ ```markdown
340
+ ### Anti-patrones detectados
341
+
342
+ | # | Patrón | Archivo:línea | Severidad | Remediación |
343
+ |---|---|---|---|---|
344
+ | 1 | Parameter sprawl | usuarios/service.py:42 | P1 | Extraer a `CrearUsuarioParams` |
345
+ | 2 | Stringly-typed | pedidos/handler.ts:88 | P2 | Crear `enum EstadoPedido` |
346
+ | 3 | TOCTOU | inventario/repo.py:120 | P0 | Envolver en `SELECT FOR UPDATE` |
347
+ ```
348
+
349
+ Mapeo a severidades estándar de revisor-codigo-swl:
350
+ - **P0** (mata credibilidad): TOCTOU sin lock cuando hay concurrencia real
351
+ - **P1** (arreglar antes de merge): parameter sprawl >6, leaky abstraction cross-capa
352
+ - **P2** (pulir): stringly-typed con 2-3 usos, no-op updates cosméticos
353
+
354
+ ## Lo que esta skill NO hace
355
+
356
+ - No reemplaza al revisor — el revisor ejecuta el análisis y decide.
357
+ - No verifica seguridad (eso es `checklist-seguridad`).
358
+ - No mide rendimiento (eso es `rendimiento-swl`).
359
+ - No corrige código — solo nombra el patrón y propone forma idiomática.
360
+
361
+ ## Origen
362
+
363
+ Adaptación curada en es-MX de `reference/code-quality-universal.md` del repo
364
+ `anthropic-skills/code-review-skill` analizado en `temp/code-review-skill-main/`
365
+ (sesión 2026-05-24). Los 10 anti-patrones del catálogo original se tradujeron
366
+ y se ajustaron al stack Python + TypeScript predominante en proyectos SWL.
367
+ Material original bajo licencia compatible; adaptación con vocabulario
368
+ es-MX y ejemplos contextualizados.
@@ -9,7 +9,7 @@ description: >
9
9
  cualquier prosa destinada a humanos. Dos modos: rewrite (reescribe) y detect
10
10
  (solo señala). NO cargar para código fuente, JSON de configuración, schemas,
11
11
  o salida estrictamente determinista.
12
- version: "1.1.0"
12
+ version: "1.1.1"
13
13
  herramientasPermitidas: [Read, Bash]
14
14
  exclusiones:
15
15
  - "No cargar para reescribir código fuente, JSON de configuración, schemas, trazas JSONL, mensajes de error del sistema o cualquier salida determinista — este skill opera solo sobre prosa destinada a humanos."
@@ -282,6 +282,57 @@ párrafo y es la palabra correcta, las tres veces se quedan.
282
282
  "Los líderes de la industria coinciden" — sin nombrar al experto, estudio o
283
283
  líder. Citar fuente específica o afirmar directamente sin la atribución.
284
284
 
285
+ ### Agencia falsa (cosas inanimadas haciendo verbos humanos)
286
+
287
+ IA evita nombrar al actor humano dándole agencia a cosas inanimadas. Las
288
+ quejas no "se transforman en arreglos"; las decisiones no "emergen"; los
289
+ datos no "nos dicen". Una persona hace algo para que esas cosas ocurran.
290
+
291
+ | Patrón | Problema | Reescritura |
292
+ |---|---|---|
293
+ | "los datos nos dicen que..." | Los datos no hablan; alguien los lee | "Al revisar los logs vi que..." |
294
+ | "la decisión emergió" | Las decisiones no emergen | "El equipo decidió que..." |
295
+ | "la queja se transformó en un arreglo" | La queja no hizo nada | "El equipo arregló el bug esa semana" |
296
+ | "el mercado recompensa" | Los mercados no recompensan | "Los clientes pagaron por..." |
297
+ | "la cultura se desplaza hacia" | Las culturas no se desplazan solas | "El equipo cambió cómo..." |
298
+ | "la conversación se mueve hacia" | Las conversaciones no se mueven | "Alguien redirigió la discusión hacia..." |
299
+ | "el código se autoexplica" | El código no explica | "El lector entiende el código sin comentarios" |
300
+
301
+ **Regla**: si una cosa inanimada es sujeto de un verbo humano (decir,
302
+ decidir, querer, transformar, moverse), buscar al humano real. Si no hay
303
+ uno específico, usar "tú" para poner al lector en el lugar de la acción.
304
+
305
+ ### Voz de narrador-distancia
306
+
307
+ Flotar sobre la escena en vez de poner al lector dentro. Aleja, no acerca.
308
+
309
+ | Patrón | Reescritura |
310
+ |---|---|
311
+ | "Nadie diseñó esto" | "Tú no te sientas un día a decidir..." |
312
+ | "Esto pasa porque..." | "Cuando X ocurre, tú ves Y" |
313
+ | "La gente tiende a..." | "Si has trabajado en X, sabes que..." |
314
+ | "Es común que los equipos..." | "Los equipos de SIGAF caen en..." |
315
+
316
+ **Regla**: cambiar tercera persona genérica ("la gente", "los equipos",
317
+ "nadie", "uno") por segunda persona ("tú") con escenario concreto, o por
318
+ nombres específicos.
319
+
320
+ ### Aperturas Wh-
321
+
322
+ Oraciones que empiezan con "Lo que", "Cómo", "Por qué", "Cuándo", "Dónde"
323
+ seguidas de cláusula explicativa son muletilla retórica. Reestructurar
324
+ para liderar con el sujeto o el verbo real.
325
+
326
+ | Patrón | Reescritura |
327
+ |---|---|
328
+ | "Lo que hace difícil esto es X" | "La restricción es X" o nombrar X directamente |
329
+ | "Por qué importa esto: ..." | Quitar la pregunta y afirmar el por qué |
330
+ | "Cómo funciona: ..." | "El flujo es: ..." o describir el flujo sin anuncio |
331
+ | "Cuándo aplica X..." | "X aplica cuando..." |
332
+
333
+ Excepción: títulos de sección H2/H3 que sí preguntan ("Cuándo cargar") son
334
+ estructura legítima de skill, no muletilla retórica.
335
+
285
336
  ### Frases relleno
286
337
 
287
338
  - "Es importante notar que" → (solo afirmarlo)