swl-ses 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CLAUDE.md +425 -0
  2. package/_userland/agentes/.gitkeep +0 -0
  3. package/_userland/habilidades/.gitkeep +0 -0
  4. package/agentes/accesibilidad-wcag-swl.md +683 -0
  5. package/agentes/arquitecto-swl.md +210 -0
  6. package/agentes/auto-evolucion-swl.md +408 -0
  7. package/agentes/backend-api-swl.md +442 -0
  8. package/agentes/backend-node-swl.md +439 -0
  9. package/agentes/backend-python-swl.md +469 -0
  10. package/agentes/backend-workers-swl.md +444 -0
  11. package/agentes/cloud-infra-swl.md +466 -0
  12. package/agentes/consolidador-swl.md +487 -0
  13. package/agentes/datos-swl.md +568 -0
  14. package/agentes/depurador-swl.md +301 -0
  15. package/agentes/devops-ci-swl.md +352 -0
  16. package/agentes/disenador-ui-swl.md +546 -0
  17. package/agentes/documentador-swl.md +323 -0
  18. package/agentes/frontend-angular-swl.md +603 -0
  19. package/agentes/frontend-css-swl.md +700 -0
  20. package/agentes/frontend-react-swl.md +672 -0
  21. package/agentes/frontend-swl.md +483 -0
  22. package/agentes/frontend-tailwind-swl.md +808 -0
  23. package/agentes/implementador-swl.md +235 -0
  24. package/agentes/investigador-swl.md +274 -0
  25. package/agentes/investigador-ux-swl.md +482 -0
  26. package/agentes/migrador-swl.md +389 -0
  27. package/agentes/mobile-android-swl.md +473 -0
  28. package/agentes/mobile-cross-swl.md +501 -0
  29. package/agentes/mobile-ios-swl.md +464 -0
  30. package/agentes/notificador-swl.md +886 -0
  31. package/agentes/observabilidad-swl.md +408 -0
  32. package/agentes/orquestador-swl.md +490 -0
  33. package/agentes/planificador-swl.md +222 -0
  34. package/agentes/producto-prd-swl.md +565 -0
  35. package/agentes/release-manager-swl.md +545 -0
  36. package/agentes/rendimiento-swl.md +691 -0
  37. package/agentes/revisor-codigo-swl.md +254 -0
  38. package/agentes/revisor-seguridad-swl.md +316 -0
  39. package/agentes/tdd-qa-swl.md +323 -0
  40. package/agentes/ux-disenador-swl.md +498 -0
  41. package/bin/swl-ses.js +119 -0
  42. package/comandos/swl/actualizar.md +117 -0
  43. package/comandos/swl/aprender.md +348 -0
  44. package/comandos/swl/auditar-deps.md +390 -0
  45. package/comandos/swl/autoresearch.md +346 -0
  46. package/comandos/swl/checkpoint.md +296 -0
  47. package/comandos/swl/compactar.md +283 -0
  48. package/comandos/swl/crear-skill.md +609 -0
  49. package/comandos/swl/discutir-fase.md +230 -0
  50. package/comandos/swl/ejecutar-fase.md +302 -0
  51. package/comandos/swl/evolucionar.md +377 -0
  52. package/comandos/swl/instalar.md +220 -0
  53. package/comandos/swl/mapear-codebase.md +205 -0
  54. package/comandos/swl/nuevo-proyecto.md +154 -0
  55. package/comandos/swl/planear-fase.md +221 -0
  56. package/comandos/swl/release.md +405 -0
  57. package/comandos/swl/salud.md +382 -0
  58. package/comandos/swl/verificar.md +292 -0
  59. package/habilidades/accesibilidad-a11y/SKILL.md +584 -0
  60. package/habilidades/angular-avanzado/SKILL.md +491 -0
  61. package/habilidades/angular-moderno/SKILL.md +326 -0
  62. package/habilidades/api-rest-diseno/SKILL.md +302 -0
  63. package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
  64. package/habilidades/aprendizaje-continuo/SKILL.md +369 -0
  65. package/habilidades/async-python/SKILL.md +474 -0
  66. package/habilidades/auth-patrones/SKILL.md +488 -0
  67. package/habilidades/auto-evolucion-protocolo/SKILL.md +376 -0
  68. package/habilidades/autoresearch/SKILL.md +248 -0
  69. package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
  70. package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
  71. package/habilidades/checklist-calidad/SKILL.md +247 -0
  72. package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
  73. package/habilidades/checklist-seguridad/SKILL.md +224 -0
  74. package/habilidades/checkpoints-verificacion/SKILL.md +309 -0
  75. package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
  76. package/habilidades/ci-cd-pipelines/SKILL.md +583 -0
  77. package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
  78. package/habilidades/cloud-aws/SKILL.md +497 -0
  79. package/habilidades/compactacion-contexto/SKILL.md +201 -0
  80. package/habilidades/contenedores-docker/SKILL.md +453 -0
  81. package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
  82. package/habilidades/css-moderno/SKILL.md +463 -0
  83. package/habilidades/datos-etl/SKILL.md +486 -0
  84. package/habilidades/dependencias-auditoria/SKILL.md +293 -0
  85. package/habilidades/deprecacion-migracion/SKILL.md +485 -0
  86. package/habilidades/design-tokens/SKILL.md +519 -0
  87. package/habilidades/discutir-fase/SKILL.md +167 -0
  88. package/habilidades/diseno-responsivo/SKILL.md +326 -0
  89. package/habilidades/django-experto/SKILL.md +395 -0
  90. package/habilidades/doc-sync/SKILL.md +259 -0
  91. package/habilidades/ejecutar-fase/SKILL.md +199 -0
  92. package/habilidades/estructura-proyecto-claude/SKILL.md +459 -0
  93. package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
  94. package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +213 -0
  95. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
  96. package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
  97. package/habilidades/event-driven/SKILL.md +580 -0
  98. package/habilidades/extractor-de-aprendizajes/SKILL.md +234 -0
  99. package/habilidades/fastapi-experto/SKILL.md +368 -0
  100. package/habilidades/frontend-avanzado/SKILL.md +555 -0
  101. package/habilidades/git-worktrees-paralelo/SKILL.md +246 -0
  102. package/habilidades/iam-secretos/SKILL.md +511 -0
  103. package/habilidades/instalar-sistema/SKILL.md +140 -0
  104. package/habilidades/kubernetes-orquestacion/SKILL.md +549 -0
  105. package/habilidades/manejo-errores/SKILL.md +512 -0
  106. package/habilidades/mapear-codebase/SKILL.md +199 -0
  107. package/habilidades/microservicios/SKILL.md +473 -0
  108. package/habilidades/mobile-flutter/SKILL.md +566 -0
  109. package/habilidades/mobile-react-native/SKILL.md +493 -0
  110. package/habilidades/monitoring-alertas/SKILL.md +447 -0
  111. package/habilidades/node-experto/SKILL.md +521 -0
  112. package/habilidades/notificaciones-multicanal/SKILL.md +448 -0
  113. package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
  114. package/habilidades/nuevo-proyecto/SKILL.md +183 -0
  115. package/habilidades/patrones-python/SKILL.md +381 -0
  116. package/habilidades/performance-baseline/SKILL.md +243 -0
  117. package/habilidades/planear-fase/SKILL.md +184 -0
  118. package/habilidades/postgresql-experto/SKILL.md +379 -0
  119. package/habilidades/react-experto/SKILL.md +434 -0
  120. package/habilidades/react-optimizacion/SKILL.md +328 -0
  121. package/habilidades/release-semver/SKILL.md +226 -0
  122. package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
  123. package/habilidades/sql-optimizacion/SKILL.md +314 -0
  124. package/habilidades/tailwind-experto/SKILL.md +412 -0
  125. package/habilidades/tdd-workflow/SKILL.md +267 -0
  126. package/habilidades/testing-python/SKILL.md +350 -0
  127. package/habilidades/threat-model-lite/SKILL.md +218 -0
  128. package/habilidades/typescript-avanzado/SKILL.md +454 -0
  129. package/habilidades/ux-diseno/SKILL.md +488 -0
  130. package/habilidades/validacion-ci-sistema/SKILL.md +543 -0
  131. package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
  132. package/habilidades/verificar-trabajo/SKILL.md +208 -0
  133. package/habilidades/wireframes-flujos/SKILL.md +396 -0
  134. package/habilidades/workflow-claude-code/SKILL.md +359 -0
  135. package/hooks/calidad-pre-commit.js +578 -0
  136. package/hooks/escaneo-secretos.js +302 -0
  137. package/hooks/extraccion-aprendizajes.js +550 -0
  138. package/hooks/linea-estado.js +249 -0
  139. package/hooks/monitor-contexto.js +230 -0
  140. package/hooks/proteccion-rutas.js +249 -0
  141. package/manifiestos/hooks-config.json +41 -0
  142. package/manifiestos/modulos.json +318 -0
  143. package/manifiestos/perfiles.json +189 -0
  144. package/package.json +45 -0
  145. package/plantillas/PROJECT.md +122 -0
  146. package/plantillas/REQUIREMENTS.md +132 -0
  147. package/plantillas/ROADMAP.md +143 -0
  148. package/plantillas/STATE.md +109 -0
  149. package/plantillas/research/ARCHITECTURE.md +220 -0
  150. package/plantillas/research/FEATURES.md +175 -0
  151. package/plantillas/research/PITFALLS.md +299 -0
  152. package/plantillas/research/STACK.md +233 -0
  153. package/plantillas/research/SUMMARY.md +165 -0
  154. package/plugin.json +144 -0
  155. package/reglas/accesibilidad.md +269 -0
  156. package/reglas/api-diseno.md +400 -0
  157. package/reglas/arquitectura.md +183 -0
  158. package/reglas/cloud-infra.md +247 -0
  159. package/reglas/docs.md +245 -0
  160. package/reglas/estilo-codigo.md +179 -0
  161. package/reglas/git-workflow.md +186 -0
  162. package/reglas/performance.md +195 -0
  163. package/reglas/pruebas.md +159 -0
  164. package/reglas/seguridad.md +151 -0
  165. package/reglas/skills-estandar.md +473 -0
  166. package/scripts/actualizar.js +51 -0
  167. package/scripts/desinstalar.js +86 -0
  168. package/scripts/doctor.js +222 -0
  169. package/scripts/inicializar.js +89 -0
  170. package/scripts/instalador.js +333 -0
  171. package/scripts/lib/detectar-runtime.js +177 -0
  172. package/scripts/lib/estado.js +112 -0
  173. package/scripts/lib/hooks-settings.js +283 -0
  174. package/scripts/lib/manifiestos.js +138 -0
  175. package/scripts/lib/seguridad.js +160 -0
  176. package/scripts/publicar.js +209 -0
  177. package/scripts/validar.js +120 -0
@@ -0,0 +1,412 @@
1
+ ---
2
+ name: tailwind-experto
3
+ description: Tailwind CSS v4 mejores prácticas. Cubre configuración CSS-first con @theme y @custom-variant, design tokens en Tailwind, patrones de componentes, responsive con breakpoints, dark mode, plugins custom, compilación JIT y anti-patrones como utility soup y @apply abuse.
4
+ ---
5
+
6
+ # Tailwind CSS v4 Experto
7
+
8
+ ## Cambio Fundamental en v4
9
+
10
+ Tailwind v4 abandona el archivo `tailwind.config.js` en favor de **configuración
11
+ directa en CSS**. No más archivo de configuración JavaScript — todo se define
12
+ con `@theme` y directivas CSS nativas.
13
+
14
+ ```css
15
+ /* app/globals.css — la config ahora está aquí */
16
+ @import "tailwindcss";
17
+
18
+ @theme {
19
+ --font-sans: 'Inter', system-ui, sans-serif;
20
+ --font-mono: 'JetBrains Mono', monospace;
21
+
22
+ --color-primario: oklch(56% 0.2 250);
23
+ --color-primario-oscuro: oklch(46% 0.2 250);
24
+ --color-secundario: oklch(70% 0.15 160);
25
+
26
+ --radius-sm: 0.25rem;
27
+ --radius-md: 0.375rem;
28
+ --radius-lg: 0.5rem;
29
+ --radius-xl: 1rem;
30
+
31
+ --spacing-18: 4.5rem;
32
+ --spacing-88: 22rem;
33
+ }
34
+ ```
35
+
36
+ ---
37
+
38
+ ## 1. Configuración @theme
39
+
40
+ ### Tokens de color completos
41
+
42
+ ```css
43
+ @theme {
44
+ /* Sistema de colores semántico */
45
+ --color-fondo: #ffffff;
46
+ --color-superficie: #f8fafc;
47
+ --color-borde: #e2e8f0;
48
+ --color-texto: #0f172a;
49
+ --color-texto-muted: #64748b;
50
+
51
+ /* Paleta de marca */
52
+ --color-marca-50: oklch(97% 0.01 250);
53
+ --color-marca-100: oklch(93% 0.03 250);
54
+ --color-marca-200: oklch(86% 0.06 250);
55
+ --color-marca-300: oklch(77% 0.10 250);
56
+ --color-marca-400: oklch(67% 0.15 250);
57
+ --color-marca-500: oklch(56% 0.20 250);
58
+ --color-marca-600: oklch(46% 0.20 250);
59
+ --color-marca-700: oklch(38% 0.18 250);
60
+ --color-marca-800: oklch(30% 0.14 250);
61
+ --color-marca-900: oklch(22% 0.10 250);
62
+
63
+ /* Estados */
64
+ --color-exito: oklch(65% 0.20 145);
65
+ --color-error: oklch(58% 0.24 27);
66
+ --color-advertencia: oklch(78% 0.18 80);
67
+ --color-info: oklch(62% 0.20 250);
68
+ }
69
+ ```
70
+
71
+ ### Tipografía y escalas
72
+
73
+ ```css
74
+ @theme {
75
+ /* Escala tipográfica fluida */
76
+ --text-xs: clamp(0.70rem, 0.5vw + 0.5rem, 0.75rem);
77
+ --text-sm: clamp(0.85rem, 0.8vw + 0.5rem, 0.875rem);
78
+ --text-base: clamp(1rem, 1vw + 0.5rem, 1rem);
79
+ --text-lg: clamp(1.1rem, 1.5vw + 0.5rem, 1.125rem);
80
+ --text-xl: clamp(1.2rem, 2vw + 0.5rem, 1.25rem);
81
+ --text-2xl: clamp(1.4rem, 3vw + 0.5rem, 1.5rem);
82
+ --text-4xl: clamp(2rem, 5vw + 0.5rem, 2.25rem);
83
+
84
+ /* Line heights */
85
+ --leading-tight: 1.25;
86
+ --leading-snug: 1.375;
87
+ --leading-normal: 1.5;
88
+ --leading-relaxed: 1.625;
89
+ }
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 2. @custom-variant
95
+
96
+ Define variantes personalizadas sin plugin:
97
+
98
+ ```css
99
+ /* Variante para elementos con data attribute */
100
+ @custom-variant data-activo (&[data-activo='true']);
101
+ @custom-variant data-disabled (&[data-disabled='true']);
102
+ @custom-variant data-error (&[data-error='true']);
103
+
104
+ /* Uso en HTML */
105
+ /* <div data-activo="true" class="data-activo:bg-marca-100 data-activo:border-marca-500"> */
106
+ ```
107
+
108
+ ```css
109
+ /* Variante para grupos custom */
110
+ @custom-variant sidebar-collapsed (.sidebar-collapsed &);
111
+ @custom-variant print-hidden (@media print { & { display: none; } });
112
+
113
+ /* Uso */
114
+ /* <nav class="w-64 sidebar-collapsed:w-16 transition-all"> */
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 3. Patrones de Componentes
120
+
121
+ ### Patrón: clases base + variantes
122
+
123
+ ```tsx
124
+ // components/Button.tsx
125
+ import { cva, type VariantProps } from 'class-variance-authority';
126
+
127
+ const buttonVariants = cva(
128
+ // Clases base siempre presentes
129
+ 'inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-marca-500 disabled:pointer-events-none disabled:opacity-50',
130
+ {
131
+ variants: {
132
+ variante: {
133
+ primario: 'bg-marca-500 text-white hover:bg-marca-600 active:bg-marca-700',
134
+ secundario: 'bg-transparent border border-marca-500 text-marca-600 hover:bg-marca-50',
135
+ ghost: 'hover:bg-gray-100 text-gray-700',
136
+ destructivo: 'bg-error text-white hover:bg-red-700',
137
+ },
138
+ tamano: {
139
+ sm: 'h-8 px-3 text-sm',
140
+ md: 'h-10 px-4 text-base',
141
+ lg: 'h-12 px-6 text-lg',
142
+ },
143
+ },
144
+ defaultVariants: {
145
+ variante: 'primario',
146
+ tamano: 'md',
147
+ },
148
+ }
149
+ );
150
+
151
+ interface ButtonProps
152
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
153
+ VariantProps<typeof buttonVariants> {}
154
+
155
+ export function Button({ variante, tamano, className, ...props }: ButtonProps) {
156
+ return (
157
+ <button
158
+ className={buttonVariants({ variante, tamano, className })}
159
+ {...props}
160
+ />
161
+ );
162
+ }
163
+ ```
164
+
165
+ ### Patrón: composición de layouts
166
+
167
+ ```tsx
168
+ // Layout de dos columnas con sidebar
169
+ function DashboardLayout({ children }: { children: React.ReactNode }) {
170
+ return (
171
+ <div className="flex min-h-screen bg-fondo">
172
+ <aside className="w-64 shrink-0 border-r border-borde bg-superficie">
173
+ <NavSidebar />
174
+ </aside>
175
+ <main className="flex-1 overflow-auto p-6">
176
+ {children}
177
+ </main>
178
+ </div>
179
+ );
180
+ }
181
+ ```
182
+
183
+ ### Patrón: tarjeta con variantes
184
+
185
+ ```tsx
186
+ const cardVariants = cva('rounded-lg border bg-superficie', {
187
+ variants: {
188
+ elevacion: {
189
+ none: 'shadow-none',
190
+ sm: 'shadow-sm',
191
+ md: 'shadow-md',
192
+ lg: 'shadow-lg ring-1 ring-black/5',
193
+ },
194
+ padding: {
195
+ none: 'p-0',
196
+ sm: 'p-4',
197
+ md: 'p-6',
198
+ lg: 'p-8',
199
+ },
200
+ },
201
+ defaultVariants: { elevacion: 'sm', padding: 'md' },
202
+ });
203
+ ```
204
+
205
+ ---
206
+
207
+ ## 4. Responsive con Breakpoints
208
+
209
+ ### Sistema de breakpoints (v4 por defecto)
210
+
211
+ | Prefijo | Ancho mínimo | Dispositivo típico |
212
+ |---------|-------------|-------------------|
213
+ | `sm:` | 640px | Móvil grande |
214
+ | `md:` | 768px | Tablet |
215
+ | `lg:` | 1024px | Laptop |
216
+ | `xl:` | 1280px | Desktop |
217
+ | `2xl:` | 1536px | Pantalla grande |
218
+
219
+ ### Mobile-first (siempre)
220
+
221
+ ```html
222
+ <!-- Correcto: móvil base, desktop override -->
223
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
224
+
225
+ <!-- MAL: desktop base, móvil override (requiere sobreescribir) -->
226
+ <div class="grid grid-cols-3 max-md:grid-cols-1">
227
+ ```
228
+
229
+ ### Breakpoints personalizados en v4
230
+
231
+ ```css
232
+ @theme {
233
+ --breakpoint-xs: 475px;
234
+ --breakpoint-3xl: 1920px;
235
+ }
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 5. Dark Mode en v4
241
+
242
+ ```css
243
+ /* globals.css */
244
+ @import "tailwindcss";
245
+
246
+ /* Modo oscuro por sistema */
247
+ @theme {
248
+ --color-fondo: #ffffff;
249
+ --color-texto: #0f172a;
250
+ }
251
+
252
+ @media (prefers-color-scheme: dark) {
253
+ @theme {
254
+ --color-fondo: #0f172a;
255
+ --color-texto: #f1f5f9;
256
+ }
257
+ }
258
+ ```
259
+
260
+ ```tsx
261
+ // Dark mode con clase manual (toggle)
262
+ <html className={tema === 'oscuro' ? 'dark' : ''}>
263
+
264
+ // En el CSS
265
+ @custom-variant dark (&:where(.dark, .dark *));
266
+ ```
267
+
268
+ ```html
269
+ <!-- Uso de variante dark -->
270
+ <div class="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">
271
+ ```
272
+
273
+ ---
274
+
275
+ ## 6. Plugins Custom
276
+
277
+ ```typescript
278
+ // plugins/animation-plugin.ts
279
+ import plugin from 'tailwindcss/plugin';
280
+
281
+ export const animacionesPlugin = plugin(
282
+ ({ addUtilities, addComponents, theme }) => {
283
+ // Utilidades de animación custom
284
+ addUtilities({
285
+ '.animate-fade-in': {
286
+ animation: 'fadeIn 0.3s ease-in-out',
287
+ },
288
+ '.animate-slide-up': {
289
+ animation: 'slideUp 0.4s ease-out',
290
+ },
291
+ });
292
+
293
+ // Componente de badge
294
+ addComponents({
295
+ '.badge': {
296
+ display: 'inline-flex',
297
+ alignItems: 'center',
298
+ padding: `${theme('spacing.1')} ${theme('spacing.2')}`,
299
+ borderRadius: theme('borderRadius.full'),
300
+ fontSize: theme('fontSize.xs'),
301
+ fontWeight: '500',
302
+ },
303
+ });
304
+ },
305
+ {
306
+ // Keyframes del plugin
307
+ theme: {
308
+ extend: {
309
+ keyframes: {
310
+ fadeIn: {
311
+ from: { opacity: '0' },
312
+ to: { opacity: '1' },
313
+ },
314
+ slideUp: {
315
+ from: { transform: 'translateY(10px)', opacity: '0' },
316
+ to: { transform: 'translateY(0)', opacity: '1' },
317
+ },
318
+ },
319
+ },
320
+ },
321
+ }
322
+ );
323
+ ```
324
+
325
+ ---
326
+
327
+ ## 7. Compilación JIT y Purging
328
+
329
+ Tailwind v4 usa JIT por defecto. Solo genera las clases que realmente usas:
330
+
331
+ ```css
332
+ /* globals.css */
333
+ @import "tailwindcss";
334
+
335
+ /* Tailwind escanea automáticamente archivos .tsx, .ts, .jsx, .js, .html */
336
+ /* Para archivos especiales, agregar fuentes adicionales: */
337
+ @source "../node_modules/mi-libreria-ui/dist/**/*.js";
338
+ ```
339
+
340
+ ### Valores arbitrarios (escape hatch)
341
+
342
+ ```html
343
+ <!-- Valor exacto que no está en el sistema -->
344
+ <div class="top-[117px] w-[calc(100%-2rem)] bg-[#1da1f2]">
345
+
346
+ <!-- Variable CSS arbitraria -->
347
+ <div class="text-(--color-personalizado)">
348
+ ```
349
+
350
+ ---
351
+
352
+ ## 8. Anti-patrones
353
+
354
+ ### Utility Soup — demasiadas clases en un elemento
355
+
356
+ ```html
357
+ <!-- MAL: ilegible, difícil de mantener -->
358
+ <button class="inline-flex items-center justify-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 active:bg-blue-800 disabled:cursor-not-allowed disabled:opacity-50">
359
+
360
+ <!-- BIEN: extraer componente con cva() -->
361
+ <Button variante="primario" tamano="sm">Guardar</Button>
362
+ ```
363
+
364
+ ### @apply Abuse — recrear Sass con Tailwind
365
+
366
+ ```css
367
+ /* MAL: el propósito de @apply es crear un nuevo .scss, no reutilizar utilidades */
368
+ .card {
369
+ @apply rounded-lg border border-gray-200 bg-white p-6 shadow-sm;
370
+ }
371
+ .card-header {
372
+ @apply border-b border-gray-200 pb-4 mb-4;
373
+ }
374
+
375
+ /* BIEN: si el patrón es reutilizable, hacer componente React/Vue/Angular */
376
+ /* Si es un patrón CSS puro, usar custom property o @layer componentes */
377
+ @layer componentes {
378
+ .card {
379
+ border-radius: var(--radius-lg);
380
+ border: 1px solid var(--color-borde);
381
+ background: var(--color-superficie);
382
+ padding: var(--spacing-6);
383
+ box-shadow: var(--shadow-sm);
384
+ }
385
+ }
386
+ ```
387
+
388
+ ### Clases condicionales con interpolación de string
389
+
390
+ ```tsx
391
+ // MAL: Tailwind JIT no puede detectar clases dinámicas construidas con interpolación
392
+ const color = 'azul';
393
+ <div className={`bg-${color}-500`}> // 'bg-azul-500' no existe en el output
394
+
395
+ // BIEN: objeto de mapeo completo
396
+ const colorMap = {
397
+ azul: 'bg-blue-500',
398
+ rojo: 'bg-red-500',
399
+ verde: 'bg-green-500',
400
+ } as const;
401
+ <div className={colorMap[color]}>
402
+ ```
403
+
404
+ ### Olvidar el orden de variantes
405
+
406
+ ```html
407
+ <!-- MAL: dark: debe aplicarse DESPUÉS del estado base en la misma propiedad -->
408
+ <div class="dark:bg-gray-900 bg-white hover:dark:bg-gray-800 hover:bg-gray-50">
409
+
410
+ <!-- BIEN: agrupar por propiedad, modificadores al final -->
411
+ <div class="bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-800">
412
+ ```
@@ -0,0 +1,267 @@
1
+ ---
2
+ name: tdd-workflow
3
+ description: Flujo completo de Test-Driven Development. Ciclo RED (el test falla) → GREEN (implementación mínima) → REFACTOR (limpieza). Incluye cobertura mínima obligatoria, tests de frontera, factories, fixtures y estrategias para diferentes tipos de código (APIs, services, componentes Angular).
4
+ ---
5
+
6
+ # Habilidad: TDD Workflow Completo
7
+
8
+ ## Propósito
9
+
10
+ TDD no es "escribir tests después" ni "escribir tests antes por costumbre". Es
11
+ un método de diseño donde los tests guían la API pública del código antes de
12
+ que exista la implementación. El resultado es código que hace exactamente lo
13
+ que los tests exigen — ni más, ni menos.
14
+
15
+ ## Cuándo activar
16
+
17
+ - CONTEXT.md o PLAN.md indica que la fase requiere TDD
18
+ - Se implementa lógica de negocio crítica (cálculos, validaciones, permisos)
19
+ - El usuario pide explícitamente TDD
20
+ - Se trabaja en un módulo con historial de bugs
21
+
22
+ ---
23
+
24
+ ## El ciclo fundamental RED → GREEN → REFACTOR
25
+
26
+ ### Fase RED — El test debe fallar por la razón correcta
27
+
28
+ **Paso 1**: Escribir el test que describe el comportamiento esperado.
29
+
30
+ ```python
31
+ # RED: Este test falla porque calcular_descuento no existe todavía
32
+ def test_descuento_cliente_premium_es_15_porciento():
33
+ cliente = ClienteFactory(tipo="premium")
34
+ resultado = calcular_descuento(cliente, monto=100.0)
35
+ assert resultado == 15.0
36
+ ```
37
+
38
+ **Verificar que el test falla BIEN**:
39
+ - Falla con `NameError` o `ImportError` si la función no existe: CORRECTO
40
+ - Falla con `AssertionError` si el comportamiento es incorrecto: CORRECTO
41
+ - Falla con `TypeError` si la firma es incorrecta: CORRECTO
42
+ - Pasa sin que exista implementación: SEÑAL DE ALARMA — el test no prueba nada
43
+
44
+ **NUNCA avanzar a GREEN si el test pasa en RED.**
45
+
46
+ ### Fase GREEN — Implementación mínima
47
+
48
+ **Regla de oro**: Implementar solo lo que hace pasar el test. Nada más.
49
+
50
+ ```python
51
+ # GREEN: Implementación mínima que hace pasar el test
52
+ def calcular_descuento(cliente: Cliente, monto: float) -> float:
53
+ if cliente.tipo == "premium":
54
+ return monto * 0.15
55
+ return 0.0
56
+ ```
57
+
58
+ **Anti-patrón GREEN**: implementar todos los casos de una vez sin tests que los
59
+ exijan. Si no hay un test para clientes "gold", no implementes el descuento gold.
60
+
61
+ **Verificar**: `pytest -v test_descuentos.py` pasa con el test nuevo.
62
+
63
+ ### Fase REFACTOR — Limpieza sin cambiar comportamiento
64
+
65
+ **Qué refactorizar en esta fase**:
66
+ - Nombres de variables o funciones poco claros
67
+ - Duplicación de lógica (si ya existe en otro test)
68
+ - Magic numbers que deberían ser constantes
69
+ - Estructura de código que anticipa el próximo test
70
+
71
+ ```python
72
+ # REFACTOR: Extraer constante y mejorar legibilidad
73
+ DESCUENTO_POR_TIPO = {
74
+ "premium": 0.15,
75
+ "gold": 0.20,
76
+ "standard": 0.0,
77
+ }
78
+
79
+ def calcular_descuento(cliente: Cliente, monto: float) -> float:
80
+ tasa = DESCUENTO_POR_TIPO.get(cliente.tipo, 0.0)
81
+ return monto * tasa
82
+ ```
83
+
84
+ **Verificar**: todos los tests siguen pasando después del refactor.
85
+
86
+ ---
87
+
88
+ ## Tests de frontera (boundary tests)
89
+
90
+ Para toda función que procesa datos, escribir tests de:
91
+
92
+ | Tipo de frontera | Ejemplo |
93
+ |----------------|---------|
94
+ | Valor cero | `monto=0.0` |
95
+ | Valor negativo | `monto=-100.0` |
96
+ | Valor máximo | `monto=999_999_999.99` |
97
+ | String vacío | `nombre=""` |
98
+ | None / null | `cliente=None` |
99
+ | Lista vacía | `items=[]` |
100
+ | Un solo elemento | `items=[item]` |
101
+ | Muchos elementos | `items=lista_de_10000` |
102
+ | Valor fuera de dominio | `tipo="inexistente"` |
103
+ | Caracteres especiales | `nombre="<script>alert(1)</script>"` |
104
+
105
+ ---
106
+
107
+ ## Factories y Fixtures
108
+
109
+ ### Factories (para datos de test)
110
+
111
+ Las factories crean objetos con valores válidos por defecto. Los tests solo
112
+ sobreescriben lo que importa para ese test específico.
113
+
114
+ **Python con factory_boy**:
115
+ ```python
116
+ import factory
117
+ from myapp.models import Cliente, Pedido
118
+
119
+ class ClienteFactory(factory.Factory):
120
+ class Meta:
121
+ model = Cliente
122
+
123
+ id = factory.Sequence(lambda n: f"cliente-{n}")
124
+ nombre = factory.Faker("name", locale="es_MX")
125
+ email = factory.Faker("email")
126
+ tipo = "standard" # default explícito
127
+ activo = True
128
+
129
+ # Uso en test
130
+ def test_descuento_premium():
131
+ # Solo especificar lo que importa para este test
132
+ cliente = ClienteFactory(tipo="premium")
133
+ assert calcular_descuento(cliente, 100.0) == 15.0
134
+ ```
135
+
136
+ **TypeScript con factory functions**:
137
+ ```typescript
138
+ // factories/user.factory.ts
139
+ export const createUser = (overrides: Partial<User> = {}): User => ({
140
+ id: 'user-1',
141
+ name: 'Test User',
142
+ email: 'test@example.com',
143
+ role: 'standard',
144
+ active: true,
145
+ ...overrides,
146
+ });
147
+
148
+ // Uso en test
149
+ it('should show admin panel for admin users', () => {
150
+ const user = createUser({ role: 'admin' });
151
+ // ...
152
+ });
153
+ ```
154
+
155
+ ### Fixtures (para estado persistente)
156
+
157
+ ```python
158
+ # conftest.py
159
+ import pytest
160
+ from sqlalchemy.ext.asyncio import AsyncSession
161
+
162
+ @pytest.fixture
163
+ async def db_session():
164
+ """Sesión de BD en transacción que hace rollback al terminar."""
165
+ async with AsyncSessionLocal() as session:
166
+ async with session.begin():
167
+ yield session
168
+ await session.rollback()
169
+
170
+ @pytest.fixture
171
+ async def cliente_premium(db_session: AsyncSession):
172
+ """Cliente premium persistido en BD de test."""
173
+ cliente = ClienteFactory.build(tipo="premium")
174
+ db_session.add(cliente)
175
+ await db_session.flush()
176
+ return cliente
177
+ ```
178
+
179
+ ---
180
+
181
+ ## TDD por tipo de código
182
+
183
+ ### Services (lógica de negocio)
184
+
185
+ ```python
186
+ # Orden de tests para un service nuevo:
187
+ # 1. Caso feliz principal
188
+ # 2. Validaciones de input inválido
189
+ # 3. Casos de borde del dominio
190
+ # 4. Interacciones con dependencias (mocks)
191
+
192
+ @pytest.mark.asyncio
193
+ async def test_crear_pedido_valida_stock_disponible():
194
+ producto = ProductoFactory(stock=5)
195
+ with pytest.raises(StockInsuficienteError):
196
+ await PedidoService.crear(producto_id=producto.id, cantidad=10)
197
+ ```
198
+
199
+ ### Endpoints FastAPI
200
+
201
+ ```python
202
+ # Usar TestClient de FastAPI
203
+ from fastapi.testclient import TestClient
204
+
205
+ def test_endpoint_requiere_autenticacion():
206
+ response = client.get("/api/v1/pedidos")
207
+ assert response.status_code == 401
208
+
209
+ def test_endpoint_retorna_solo_pedidos_del_usuario(cliente_autenticado):
210
+ pedido_propio = PedidoFactory(usuario_id=cliente_autenticado.id)
211
+ pedido_ajeno = PedidoFactory(usuario_id="otro-usuario")
212
+
213
+ response = cliente_autenticado.get("/api/v1/pedidos")
214
+ ids = [p["id"] for p in response.json()["items"]]
215
+
216
+ assert pedido_propio.id in ids
217
+ assert pedido_ajeno.id not in ids # IDOR check
218
+ ```
219
+
220
+ ### Componentes Angular
221
+
222
+ ```typescript
223
+ // Usar TestBed + ComponentHarness
224
+ describe('PedidosComponent', () => {
225
+ it('should display empty state when no orders exist', async () => {
226
+ const mockService = { getPedidos: () => of({ items: [], total: 0 }) };
227
+ await TestBed.configureTestingModule({
228
+ providers: [{ provide: PedidosService, useValue: mockService }]
229
+ }).compileComponents();
230
+
231
+ const fixture = TestBed.createComponent(PedidosComponent);
232
+ fixture.detectChanges();
233
+
234
+ const emptyState = fixture.nativeElement.querySelector('[data-testid="empty-state"]');
235
+ expect(emptyState).toBeTruthy();
236
+ });
237
+ });
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Cobertura mínima obligatoria
243
+
244
+ | Tipo de módulo | Cobertura mínima |
245
+ |---------------|-----------------|
246
+ | Services (lógica crítica) | 90% |
247
+ | Endpoints (API) | 85% |
248
+ | Utilities / helpers | 95% |
249
+ | Componentes Angular | 75% |
250
+ | Modelos ORM | 70% |
251
+
252
+ **Verificar** con reporte de cobertura antes de marcar tarea como completada:
253
+ ```bash
254
+ pytest --cov=src/services --cov-fail-under=90
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Anti-patrones TDD a evitar
260
+
261
+ | Anti-patrón | Descripción | Solución |
262
+ |-------------|-------------|---------|
263
+ | Test del mock | El test solo verifica que se llamó el mock, no el comportamiento real | Testear el efecto observable |
264
+ | Test omnibus | Un solo test que verifica 10 cosas a la vez | Un test, un comportamiento |
265
+ | Test frágil | Falla si cambias nombres internos sin cambiar comportamiento | Testear comportamiento, no implementación |
266
+ | Fixture global | Un fixture que modifica estado global compartido entre tests | Fixtures con scope limitado, rollback |
267
+ | Skip como solución | `@pytest.mark.skip` para tests que fallan | Arreglar el bug o eliminar el test |