@saulwade/swl-ses 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md
CHANGED
|
@@ -5,12 +5,12 @@ description: >
|
|
|
5
5
|
testing con httpx. Incluye el anti-patrón crítico MissingGreenlet (lazy loading
|
|
6
6
|
en async). Cargar cuando se implementen endpoints FastAPI, schemas Pydantic v2,
|
|
7
7
|
queries SQLAlchemy async, WebSockets, SSE o tests de integración con httpx.
|
|
8
|
-
version: "1.
|
|
8
|
+
version: "1.2.0"
|
|
9
9
|
evolved: true
|
|
10
|
-
evolved-from: "1.1.
|
|
11
|
-
evolved-at: "2026-05-
|
|
10
|
+
evolved-from: "1.1.2"
|
|
11
|
+
evolved-at: "2026-05-10"
|
|
12
12
|
evolved-by: "aprender"
|
|
13
|
-
evolved-note: "
|
|
13
|
+
evolved-note: "2 reglas nuevas en sesión SIGM Opción B: response_model=dict obsoleto vs Envelope[T] tipado, RETURNING * sin soporte de JOINs (refactor a 2 queries con _SQL_X_ENRIQUECIDA)"
|
|
14
14
|
herramientasPermitidas: [Read]
|
|
15
15
|
exclusiones:
|
|
16
16
|
- "No cargar para proyectos Django o Flask — los patrones de ORM sync, Class-Based Views y middleware difieren fundamentalmente; cargar `django-experto` o el skill del framework correspondiente."
|
|
@@ -219,6 +219,8 @@ class Factura(Base):
|
|
|
219
219
|
- **`return result or {}` después de UPDATE/INSERT enmascara errores de BD silenciosamente**: patrón típico `result = await repo.actualizar_estatus(...); return result or {}` devuelve `{}` al cliente cuando el UPDATE no encontró la fila (race condition, RLS, FK violado). El cliente recibe HTTP 200 con body vacío en lugar del 404/500 esperado, los bugs quedan invisibles en monitoring. Causa: `or {}` trata `None` como "datos no disponibles" cuando en realidad significa "el UPDATE falló post-INSERT". Solución: explicit None check con raise — usar `if result is None: raise HTTPException(404, "Recurso no encontrado")` cuando el ID viene del cliente, o `raise HTTPException(500, "Error interno...")` cuando es un invariante post-INSERT (la fila acaba de crearse, debe existir).
|
|
220
220
|
- **`detail=str(exc)` en HTTPException — solo aceptable cuando la excepción es de DOMINIO con mensaje diseñado para usuario**: las excepciones de capa externa (MinIO/S3, BD driver, HTTP client de un PSP, parser PDF) tienen mensajes que pueden contener bucket/host/paths/credenciales parciales/stack traces. Pasar `str(exc)` directo al `detail` los expone al cliente. Causa: tratar todas las excepciones igual sin distinguir dominio (controlado) de capa externa (no controlado). Solución: dos patrones distintos. Para excepciones de dominio (`MIMENoPermitidoError("MIME 'X' no permitido")`, `EmailDuplicadoError`): `except DominioError as exc: raise HTTPException(422, detail=str(exc))` OK. Para capa externa (`ErrorEvidencia`, `boto3.ClientError`, `httpx.RequestError`): `except CapaExternaError as exc: logger.exception("contexto"); raise HTTPException(502, detail="Error genérico al cliente")`. La diferencia es que el mensaje de DominioError fue diseñado para el usuario; el de CapaExternaError no.
|
|
221
221
|
- **Vocabulario interno (nombres de funciones PL/pgSQL, schemas, tablas, "RLS", "transaccional") en `detail` al cliente fuga arquitectura**: detail genérico al cliente y detail con detalles de infraestructura para diagnóstico **no son lo mismo**. Patrones de fuga típicos: `detail=f"fn_evaluar_X no retornó resultado"` (revela nombre de función PL/pgSQL), `detail="Posible inconsistencia transaccional o RLS"` (revela motor + capa de seguridad), `detail=f"Recurso {id} no encontrado en activacion tras INSERT"` (revela UUID interno y secuencia de operaciones). Causa: el desarrollador escribe el mensaje pensando en debug, no en exposición. Solución: detail al cliente = mensaje genérico orientado al recurso ("Error interno al activar el recurso. Contacte al administrador."); detalles internos solo en `logger.error("contexto detallado %s %s", uuid, programa_id, ...)` con format strings estructurados (NO concatenación con `+`). Aplica a todos los HTTPException 500/503; el 404/422 puede ser más específico si el mensaje es del dominio.
|
|
222
|
+
- **`response_model=dict` produce `{[key:string]:unknown}` en `openapi-typescript` — tipos inútiles para frontend**: declarar endpoints con `response_model=dict` (placeholder) hace que el codegen `openapi-typescript` genere responses tipados como objeto vacío. El frontend lee campos via `.id`, `.monto` con type assertions implícitas → mismatches silenciosos en runtime cuando los nombres del backend cambian. Causa: FastAPI sin schema concreto no documenta el shape en `/openapi.json`. Fix: SIEMPRE declarar `response_model=EnvelopeResponse[Schema]`, `EnvelopePaginatedResponse[Schema]`, `EnvelopeOffsetResponse[Schema]` o `EnvelopeResponse[MensajeResponse]` con un schema Pydantic concreto. Genéricos `EnvelopeResponse[T] / EnvelopePaginatedResponse[T] / EnvelopeOffsetResponse[T] / MensajeResponse` deben vivir en `app/common/response.py` (Pydantic v2 + Generic[T]). Excepción única documentable: `StreamingResponse` (PDF/CSV) puede usar `response_model=dict` con comentario explicativo. Caso real: 155 endpoints SIGM refactorizados (2026-05-10) tras descubrir que el codegen producía tipos vacíos por `response_model=dict` heredado.
|
|
223
|
+
- **Pydantic + PostgreSQL `RETURNING *` no soporta JOINs en INSERT/UPDATE — refactor a 2 queries**: para mutaciones que devuelven un schema enriquecido con JOINs (ej: `cajero_nombre` desde `usuario.usuario`, `clave_catastral` desde `cuenta_predial`), `INSERT/UPDATE ... RETURNING *` no permite agregar JOINs. Causa: PostgreSQL `RETURNING` solo accede a las columnas de la tabla afectada. Fix: refactorizar a 2 queries: (1) `INSERT/UPDATE ... RETURNING id`; (2) `SELECT ... FROM tabla LEFT JOIN ... WHERE id = $1`. Costo: 1 round-trip extra (sub-1ms en LAN). Beneficio: el método siempre devuelve el shape enriquecido, mappers consistentes entre `crear`, `obtener` y `listar`. Patrón DRY: extraer la query SELECT a constante de clase (`_SQL_X_ENRIQUECIDA`) para reusar entre métodos. Caso: `crear_solicitud_descuento`, `autorizar_descuento`, `obtener_solicitud_descuento` y `listar_solicitudes_pendientes` comparten `_SQL_SOLICITUD_ENRIQUECIDA` con LEFT JOIN a `cuenta_predial` + `usuario` (cajero/supervisor).
|
|
222
224
|
|
|
223
225
|
## Referencias especializadas
|
|
224
226
|
|
|
@@ -4,7 +4,12 @@ description: >
|
|
|
4
4
|
Next.js App Router: Server Components, Client Components, Server Actions,
|
|
5
5
|
streaming con Suspense, ISR, route handlers y middleware. Cargar cuando se
|
|
6
6
|
implementen páginas Next.js, data fetching, mutaciones o rutas de API.
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.1.0"
|
|
8
|
+
evolved: true
|
|
9
|
+
evolved-from: "1.0.0"
|
|
10
|
+
evolved-at: "2026-05-10"
|
|
11
|
+
evolved-by: "aprender"
|
|
12
|
+
evolved-note: "3 gotchas operativos confirmados en sesión SIGM Opción B: useSearchParams Suspense bailout, cache .next stale, brace-expansion override CVE-2025-5889"
|
|
8
13
|
herramientasPermitidas: [Read]
|
|
9
14
|
exclusiones:
|
|
10
15
|
- "No cargar para proyectos Next.js con Pages Router (pages/index.tsx) — el modelo de getServerSideProps y getStaticProps es diferente al App Router; este skill solo cubre App Router."
|
|
@@ -316,6 +321,12 @@ export default function Reloj() {
|
|
|
316
321
|
|
|
317
322
|
**Variables de entorno sin `NEXT_PUBLIC_` no disponibles en el cliente en runtime**: `process.env.MI_VAR` en un Client Component retorna `undefined` en el browser aunque esté definida en `.env.local`. Causa: Next.js solo serializa al bundle del cliente las variables con prefijo `NEXT_PUBLIC_`. Fix: renombrar a `NEXT_PUBLIC_MI_VAR` si debe estar en el cliente, o leerla en un Server Component/Action y pasarla como prop.
|
|
318
323
|
|
|
324
|
+
**`useSearchParams()` requiere `<Suspense>` boundary durante prerender estático**: build de producción falla con `⨯ useSearchParams() should be wrapped in a suspense boundary at page "/X". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout`. Causa: el prerender estático no puede ejecutar el hook → CSR bailout → build aborta. Fix: en `page.tsx` envolver el componente que usa `useSearchParams()` en `<Suspense fallback={null}>` (o un esqueleto). El shell se prerenderiza vacío; el componente hidrata en cliente con los search params disponibles. Mismo patrón aplica a `useRouter()` y `usePathname()` cuando se usan en componentes prerenderizados. Caso real: SIGM agregó `useSearchParams()` a `useLoginForm.ts` para honrar `?siguiente=`; tsc + vitest pasaban pero `npm run build` falló hasta envolver `<LoginForm />` en `<Suspense>`.
|
|
325
|
+
|
|
326
|
+
**Cache `.next/` puede ocultar errores TypeScript reales tras refactor de tipos masivo**: `npx tsc --noEmit` local pasa pero CI falla con errores TS2724/TS2305 sobre tipos eliminados. Causa: `.next/types/*.d.ts` y `tsconfig.tsbuildinfo` cachean los types del estado anterior; CI clona limpio y ve los errores reales. Fix: cuando se modifican imports/exports masivos en `lib/*/tipos.ts` (refactor de tipos, eliminación de interfaces, codegen openapi-typescript), borrar cache antes de validar local: `cd frontend && rm -rf .next && npx tsc --noEmit`. Si CI falla con TSC y local pasa: 90% de las veces es cache stale.
|
|
327
|
+
|
|
328
|
+
**Override de `brace-expansion@^5` rompe minimatch (eslint depende)**: `npm install` aplica el override pero `eslint . --ext .ts,.tsx` falla con `TypeError: expand is not a function` en `@eslint/config-array → minimatch → brace-expansion`. Causa: brace-expansion v3+ cambió la API (de export default `expand` function a export con shape distinto); minimatch espera la API v2. Fix: para CVE-2025-5889 (ReDoS), usar `"brace-expansion": "^2.0.2"` (parcheado desde 2.0.2 — NO requiere v5). Validación pre-override: `npm ls brace-expansion` para revisar consumidores conocidos antes de bumpear major. Mismo patrón aplicable a otros overrides de transitives — validar cadena de consumers antes de bumpear major.
|
|
329
|
+
|
|
319
330
|
## Checklist de verificación
|
|
320
331
|
|
|
321
332
|
- [ ] "use client" solo en componentes con hooks, eventos o browser APIs
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: react-experto
|
|
3
3
|
description: React + Next.js mejores prácticas modernas. Cubre Server Components vs Client Components, data fetching patterns (RSC, React Query, SWR, Server Actions), state management (useState, Zustand, Jotai), performance (memo, lazy, Suspense, streaming) y Next.js App Router patterns.
|
|
4
|
-
version: "1.
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
evolved: true
|
|
6
|
+
evolved-from: "1.0.0"
|
|
7
|
+
evolved-at: "2026-05-10"
|
|
8
|
+
evolved-by: "aprender"
|
|
9
|
+
evolved-note: "Patrón useSyncExternalStore para hidratación cliente-only confirmado en sesión SIGM (evita la regla nueva react-hooks/set-state-in-effect)"
|
|
5
10
|
herramientasPermitidas: [Read]
|
|
6
11
|
exclusiones:
|
|
7
12
|
- "No cargar para optimización de rendimiento React (memo, useMemo, useCallback, virtualización, code splitting) — para rendimiento cargar `react-optimizacion`."
|
|
@@ -207,3 +212,13 @@ Para ejemplos completos de React Query (mutations + invalidacion), Server Action
|
|
|
207
212
|
**Server Action que actualiza datos sin `revalidatePath` o `revalidateTag` muestra datos obsoletos**: después de un Server Action exitoso (crear/actualizar/eliminar), el cliente sigue viendo el caché anterior del RSC. Causa: Next.js cachea agresivamente los datos del servidor; los Server Actions no invalidan el caché automáticamente. Fix: llamar `revalidatePath('/ruta/afectada')` o `revalidateTag('tag-del-dato')` al final del Server Action antes de `redirect()` o `return`.
|
|
208
213
|
|
|
209
214
|
**`useState` con objeto como valor inicial no se actualiza con shallow comparison en re-renders del padre**: `useState({ nombre: '', email: '' })` inicializa el estado una sola vez — si el componente padre pasa nuevos valores como prop para reinicializar el formulario, el estado no se actualiza. Causa: `useState` solo usa el valor inicial en el primer render. Fix: usar `key` en el componente para forzar remonte cuando cambien los datos base, o usar `useEffect` con las props como dependencias para sincronizar explícitamente.
|
|
215
|
+
|
|
216
|
+
**Patrón `useState + useEffect(() => setState(true), [])` para detectar cliente dispara la regla `react-hooks/set-state-in-effect` en eslint-plugin-react-hooks v7+**: build CI falla con `Calling setState synchronously within an effect body causes cascading renders`. Causa: la regla nueva (Next.js 16+) detecta el patrón clásico de "mounted flag" para hidratación segura como anti-patrón de performance. Fix idiomático React 19: usar `useSyncExternalStore`:
|
|
217
|
+
```tsx
|
|
218
|
+
const mounted = useSyncExternalStore(
|
|
219
|
+
() => () => {}, // subscribe — no-op (la 'store' no cambia tras hidratación)
|
|
220
|
+
() => true, // getSnapshot — cliente siempre montado
|
|
221
|
+
() => false, // getServerSnapshot — SSR siempre 'no montado'
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
Equivalente funcional al patrón mounted flag, sin disparar la regla. Type-safe, lint-compliant. Usar en Header/Sidebar/cualquier componente que lea localStorage o `window` y necesite render distinto en SSR vs cliente.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saulwade/swl-ses",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot con 59 agentes,
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot con 59 agentes, 154 habilidades, 42 comandos, 64 reglas y 40 hooks. Soporta 11 lenguajes y 5 runtimes: Claude Code, Copilot, OpenCode, Codex y Gemini CLI. 100% en espanol (Mexico). Incluye gateway bidireccional con relay Telegram a Claude Code.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"swl-ses": "bin/swl-ses.js",
|
|
7
7
|
"swl-telegram-bot": "bin/swl-telegram-bot.js",
|
package/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swl-ses",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 59 agentes, 154 habilidades, 42 comandos, 64 reglas y 40 hooks. 62 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI.",
|
|
5
5
|
"author": "Saul Wade Leon",
|
|
6
6
|
"license": "MIT",
|