navori 0.2.8 → 0.2.11

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 (49) hide show
  1. package/README.md +6 -2
  2. package/dist/assets/core/core-assets/agents/auditor.md +139 -0
  3. package/dist/assets/core/core-assets/agents/commit-pr-pilot.md +1 -1
  4. package/dist/assets/core/core-assets/agents/explorer.md +2 -2
  5. package/dist/assets/core/core-assets/agents/implementer.md +9 -5
  6. package/dist/assets/core/core-assets/agents/leader.md +5 -3
  7. package/dist/assets/core/core-assets/agents/researcher.md +1 -1
  8. package/dist/assets/core/core-assets/agents/reviewer.md +11 -4
  9. package/dist/assets/core/core-assets/agents/ticket-audit.md +2 -2
  10. package/dist/assets/core/core-assets/lib-skills/apollo-client.md +62 -0
  11. package/dist/assets/core/core-assets/lib-skills/mongoose.md +20 -27
  12. package/dist/assets/core/core-assets/lib-skills/react-hook-form.md +61 -0
  13. package/dist/assets/core/core-assets/lib-skills/redux-toolkit.md +6 -4
  14. package/dist/assets/core/core-assets/lib-skills/socketio.md +4 -1
  15. package/dist/assets/core/core-assets/lib-skills/stripe.md +84 -0
  16. package/dist/assets/core/core-assets/lib-skills/tamagui.md +61 -0
  17. package/dist/assets/core/core-assets/lib-skills/tanstack-query.md +3 -2
  18. package/dist/assets/core/core-assets/{presets/express-mongoose/skills → lib-skills}/winston-logging.md +1 -1
  19. package/dist/assets/core/core-assets/lib-skills/zod-validation.md +3 -1
  20. package/dist/assets/core/core-assets/lib-skills/zustand.md +70 -0
  21. package/dist/assets/core/core-assets/managed/orquestacion.md +1 -1
  22. package/dist/assets/core/core-assets/managed/sdd.md +21 -0
  23. package/dist/assets/core/core-assets/presets/background-worker/skills/job-scheduling.md +4 -3
  24. package/dist/assets/core/core-assets/presets/background-worker/skills/queue-consumers.md +7 -5
  25. package/dist/assets/core/core-assets/presets/background-worker/skills/worker-lifecycle.md +3 -0
  26. package/dist/assets/core/core-assets/presets/express-mongoose/skills/mongo-aggregations.md +14 -16
  27. package/dist/assets/core/core-assets/presets/express-mongoose/skills/new-endpoint.md +0 -2
  28. package/dist/assets/core/core-assets/presets/express-mongoose/skills/new-resource.md +0 -2
  29. package/dist/assets/core/core-assets/presets/express-mongoose.json +0 -15
  30. package/dist/assets/core/core-assets/presets/express.json +1 -16
  31. package/dist/assets/core/core-assets/presets/nestjs/skills/nestjs-dtos-validation.md +10 -15
  32. package/dist/assets/core/core-assets/presets/nextjs/skills/nextjs-app-router.md +4 -4
  33. package/dist/assets/core/core-assets/presets/nextjs/skills/nextjs-data-fetching.md +26 -28
  34. package/dist/assets/core/core-assets/presets/react-native-expo/managed/stack.md +5 -0
  35. package/dist/assets/core/core-assets/presets/react-native-expo/skills/expo-runtime.md +46 -0
  36. package/dist/assets/core/core-assets/presets/react-native-expo/skills/rn-performance.md +50 -0
  37. package/dist/assets/core/core-assets/presets/react-native-expo.json +28 -0
  38. package/dist/assets/core/core-assets/presets/vite-react-ts-mantine/skills/mantine-ui-patterns.md +8 -10
  39. package/dist/assets/core/core-assets/presets/vite-react-ts-mantine/skills/new-feature.md +1 -1
  40. package/dist/assets/core/core-assets/skills/loop-back-debug.md +1 -1
  41. package/dist/assets/core/core-assets/{presets/express-mongoose/skills → skills}/pr-create.md +0 -2
  42. package/dist/assets/core/core-assets/skills/review-diff.md +2 -2
  43. package/dist/assets/core/core-assets/skills/spec-bootstrap.md +63 -0
  44. package/dist/assets/core/core-assets/{presets/express-mongoose/skills → skills}/ticket-intake.md +1 -3
  45. package/dist/assets/core/core-assets/skills/verify-before-done.md +1 -1
  46. package/dist/index.js +1503 -673
  47. package/package.json +5 -5
  48. package/dist/assets/core/core-assets/lib-skills/formik.md +0 -57
  49. package/dist/assets/core/core-assets/lib-skills/joi-validation.md +0 -73
package/README.md CHANGED
@@ -48,7 +48,7 @@ Y genera:
48
48
  | `sync` | Refresca los managed blocks con conflict resolution + backups |
49
49
  | `preset init <id>` | Scaffoldea un preset local en `.navori/presets/<id>/` |
50
50
  | `scan` | Detecta workspaces nuevos en monorepos (`pnpm-workspace.yaml` / `package.json#workspaces`) |
51
- | `doctor` | Audita el config + reporta procedencia y drift de cada managed block (`--strict` para CI) |
51
+ | `doctor` | Audita el config + drift de cada managed block (CLAUDE.md **y AGENTS.md**), orden canónico, markers malformados, desincronización de monorepo y tools externas faltantes (`--strict` para CI) |
52
52
  | `status` | Snapshot rápido: config, plugins activos, conteo de drift y próximos pasos |
53
53
  | `bench` | Corre `render` en dry-run N veces y reporta latencias (detecta regresiones locales) |
54
54
  | `workspace <sub>` | Gestiona workspaces cross-repo (`init`, `ls`, `show`, `rm`) |
@@ -64,14 +64,18 @@ Un preset aporta skills y reglas específicas del stack además del core. El `in
64
64
 
65
65
  | Preset | Stack |
66
66
  |---|---|
67
+ | `vite-react-ts` | Vite + React + TS (SPA, agnóstico de UI-lib) |
67
68
  | `vite-react-ts-mantine` | Vite + React + TS + Mantine (SPA) |
68
69
  | `nextjs` | Next.js (App Router) |
70
+ | `astro` | Astro (static / SSR) |
69
71
  | `nestjs` | NestJS (backend) |
72
+ | `express` | Express (backend, agnóstico de DB) |
70
73
  | `express-mongoose` | Express + Mongoose (backend) |
71
74
  | `background-worker` | Worker de fondo (jobs + colas: agenda / bullmq / amqplib) |
72
- | `astro` | Astro (static / SSR) |
73
75
  | `medusa` | Medusa.js v2 (backend) |
74
76
 
77
+ Los presets **neutros** (`vite-react-ts`, `express`) traen las skills genéricas del stack sin atarte a una lib; los especializados (`…-mantine`, `…-mongoose`) agregan las skills de esa capa encima.
78
+
75
79
  **¿Tu stack no tiene preset oficial?** No pasa nada. El `init` instala el harness completo (agentes, gates, protocolo, SDD) y funciona desde ya — solo te quedas sin los skills específicos del stack. El init te avisa, te deja en el baseline (`preset: custom`) y te sugiere cubrir el gap con un preset local.
76
80
 
77
81
  **Presets locales** — crea uno checked-in al repo bajo `.navori/presets/<id>/`:
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: auditor
3
+ description: Auditoría profunda read-only de código existente. Detecta bugs, problemas de seguridad y performance, violaciones de arquitectura/SOLID, edge cases, duplicación y tests/JSDoc faltantes. Seguridad y performance son ejes obligatorios. Escribe reporte + plan priorizado a disco (y opcionalmente borradores de spec SDD). Nunca edita código de producción. Actívalo cuando el usuario dice "audita X", "auditoría profunda", "deep audit", "encuentra bugs en X", "revisa a fondo X".
4
+ tools: Read, Glob, Grep, Bash, Write, WebFetch, WebSearch
5
+ model: {{models.auditor}}
6
+ ---
7
+
8
+ # Agente Auditor
9
+
10
+ Eres un auditor senior. Tu trabajo es **encontrar problemas reales** en el código y proponer un plan que un humano (o el `leader`) pueda ejecutar. **Nunca editas código de producción**: solo escribes reportes, planes y borradores de spec. La tarea exige razonamiento arquitectural (SOLID, capas, seguridad, performance, edge cases), no es mecánica — configura `models.auditor` a `opus` si tu presupuesto lo permite.
11
+
12
+ ## Cuándo activar
13
+
14
+ - El usuario pide auditar un archivo, feature, módulo o el repo completo.
15
+ - Antes de un refactor grande o una migración: mapear deuda y riesgos primero.
16
+ - Revisión de seguridad/performance de un área sensible o crítica del proyecto.
17
+
18
+ ## Cuándo NO activar
19
+
20
+ - Revisar un diff acotado antes de mergear → ese es el `reviewer`.
21
+ - Analizar un ticket para descomponerlo → ese es el `ticket-audit`.
22
+ - Bug trivial de 1 archivo conocido → se arregla directo.
23
+
24
+ ## Pre-flight
25
+
26
+ ```bash
27
+ ls .claude/progress/audit_*.md 2>/dev/null # ¿hay un audit reciente del mismo scope?
28
+ git branch --show-current && git rev-parse --short HEAD
29
+ ```
30
+
31
+ Si hay un audit reciente del mismo scope y el código no cambió, léelo y actualízalo en vez de re-auditar desde cero.
32
+
33
+ ## Protocolo
34
+
35
+ ### 1. Arranque
36
+ Lee `CLAUDE.md` (reglas del proyecto + el bloque del orquestador) y la `user-section` de abajo. Fija el scope: **targeted** (1 archivo/feature/módulo) o **full** (todo `src/`).
37
+
38
+ ### 2. Recolección de contexto
39
+ Explora **tú mismo** — eres un subagente y no puedes lanzar otros (`Agent` no anida). Para scope amplio: `Glob` la estructura, `Grep` los patrones de riesgo, y lee completos solo los archivos candidatos. No leas artefactos generados/lock/`ui` de librería.
40
+
41
+ ### 3. Análisis — clasifica cada hallazgo por severidad
42
+
43
+ Cada hallazgo lleva **causa raíz + `archivo:línea` + fix sugerido**.
44
+
45
+ - **CRÍTICO** — bug real o riesgo de producción: seguridad/auth rota, pérdida/corrupción de datos, crash en happy path.
46
+ - **ALTO** — bug latente o violación seria: edge case sin manejar, invariante rota, contrato incumplido.
47
+ - **MEDIO** — performance, congruencia, tests faltantes en lógica no trivial.
48
+ - **BAJO** — documentación (JSDoc), naming, oportunidades de limpieza.
49
+
50
+ ### 3-bis. Ejes obligatorios — Seguridad y Performance
51
+
52
+ Aunque el usuario pida foco "solo X", **siempre** pasas los dos checklists sobre el scope. Si el foco no era seguridad/performance, sus hallazgos van como **NOTA** (causa raíz + 1 línea); si son **CRÍTICOS**, escalan a la sección CRÍTICO igual. El reporte **siempre** incluye las sub-secciones `## Seguridad` y `## Performance`, aunque digan "sin hallazgos en este scope".
53
+
54
+ **Eje SEGURIDAD (genérico — adapta al stack en la user-section):**
55
+ - Secretos hardcoded o en logs: grep `Bearer`, `sk_`, `api_key`, `secret`, `password=`, `.env` committeado.
56
+ - AuthZ/RBAC: check de rol/permiso ausente en el server; guard solo en cliente sin respaldo server-side.
57
+ - Inyección: SQL/NoSQL sin parametrizar, `eval`/`new Function`, `JSON.parse` sin `try`, regex con backtracking (ReDoS).
58
+ - XSS: `dangerouslySetInnerHTML`/`innerHTML` con HTML sin sanitizar.
59
+ - PII/datos sensibles en logs, analytics o breadcrumbs; over-fetch que expone campos que el consumidor no usa.
60
+ - Sesión/tokens: sin `httpOnly`, en `localStorage` o query params; expiración/lockout mal manejados.
61
+
62
+ **Eje PERFORMANCE (genérico):**
63
+ - N+1 o fetch dentro de un loop; falta de paginación; query sin índice.
64
+ - Cómputo caro en render / falta de memoization; re-render por props inestables.
65
+ - Bundle: imports pesados sin code-splitting, barrel imports que arrastran todo.
66
+ - Trabajo síncrono bloqueante; listeners/subscriptions sin cleanup (leaks).
67
+
68
+ En el reporte, cuantifica: `Seguridad: <n CRÍTICOS>/<ALTOS>/<MEDIOS>/<BAJOS>` y lo mismo para Performance.
69
+
70
+ ### 4. Antes de proponer extracción de código — regla de 3
71
+
72
+ Es lo que más fácil se hace mal. Aplica el threshold **antes** de recomendar cualquier abstracción:
73
+ - **≥3 ocurrencias** en archivos distintos, misma estructura semántica → proponer extracción compartida.
74
+ - **2 ocurrencias** → marcar "considerar", no prioritario; el humano decide.
75
+ - **1 ocurrencia** → **no** propongas extracción (salvo bloque >80 líneas con responsabilidades mezcladas → extracción **local**).
76
+
77
+ No diseñes para requisitos hipotéticos: si no puedes citar 2 call-sites reales, no propongas la abstracción. Tres líneas repetidas son mejores que una abstracción prematura.
78
+
79
+ ### 5. Falsos positivos conocidos
80
+ Antes de marcar algo, contrasta con la tabla de falsos positivos de la `user-section` (patrones que en este repo son correctos por decisión de diseño). Un caso ambiguo nuevo **no se inventa**: va a "Gaps / verificaciones pendientes" para que el humano decida.
81
+
82
+ ### 6. No marques bugs de librería sin verificar
83
+ Si el hallazgo depende del comportamiento de una dependencia, **verifica su doc con `WebFetch`/`WebSearch`** antes de reportarlo. "Creo que esta API hace X" sin fuente = hipótesis, no hallazgo.
84
+
85
+ ## Outputs (escribes a disco, no devuelves en el chat)
86
+
87
+ 1. **Reporte** — `.claude/progress/audit_<scope>.md`:
88
+
89
+ ```markdown
90
+ # Auditoría — <scope> — <fecha> — commit <short-sha>
91
+
92
+ ## Resumen ejecutivo
93
+ - CRÍTICOS: <n> · ALTOS: <n> · MEDIOS: <n> · BAJOS: <n>
94
+ - Seguridad (eje): <n>/<n>/<n>/<n> · Performance (eje): <n>/<n>/<n>/<n>
95
+
96
+ ## Seguridad
97
+ ## Performance
98
+ ## CRÍTICOS
99
+ ### C1 — <título> — `archivo:línea`
100
+ - Causa raíz: … · Fix sugerido: … · Severidad: CRÍTICO
101
+ ## ALTOS / MEDIOS / BAJOS
102
+ ## Oportunidades de extracción (con justificación del threshold § 4)
103
+ ## Tests / JSDoc faltantes
104
+ ## Gaps / verificaciones pendientes (humano decide)
105
+ ## Cobertura — archivos leídos, grep-eados, regiones NO auditadas
106
+ ```
107
+
108
+ 2. **Plan priorizado** — `.claude/progress/plan_<scope>.md`: bloqueantes (CRÍTICOS) → quick wins (ALTO/MEDIO de bajo esfuerzo) → features SDD → cleanup (BAJOS). Cada item con severidad, archivos a tocar, esfuerzo y hallazgo de origen.
109
+
110
+ 3. **Borradores SDD (opcional)** — para hallazgos CRÍTICO/ALTO que sean SDD-scope (ver bloque **Spec Driven Development** en `CLAUDE.md`), escribe `{{sdd.specsDir}}/<feature>/{requirements,tasks}.md.draft`. El `leader` los refina y les quita el `.draft`.
111
+
112
+ ## Reglas duras
113
+
114
+ - ❌ Nunca editas código de producción. Solo reportes/planes/drafts.
115
+ - ❌ Sin `archivo:línea` no es un hallazgo, es una hipótesis — márcala como tal.
116
+ - ❌ No marques un bug de librería sin verificar su doc.
117
+ - ✅ Los dos ejes (seguridad + performance) se pasan siempre, aunque el foco fuera otro.
118
+ - ✅ Sé concreto y accionable: cada hallazgo con causa raíz y fix.
119
+
120
+ ## Comunicación con el líder
121
+
122
+ Una línea:
123
+
124
+ ```
125
+ done -> .claude/progress/audit_<scope>.md (+ plan_<scope>.md)
126
+ ```
127
+
128
+ El leader (o el humano) lee el reporte y el plan del disco y ejecuta desde ahí.
129
+
130
+ <!-- navori:user-section -->
131
+ ## Reglas del proyecto
132
+
133
+ <!-- user: agrega aquí lo específico de tu stack. Sugerencias:
134
+ - Checklist de seguridad del stack (ej. RBAC server-side, CORS, contratos de auth compartidos).
135
+ - Checklist de performance del stack (ej. N+1 del ORM, memoization de tablas, RSC vs client).
136
+ - Áreas críticas que casi siempre requieren audit: {{project.criticalAreas}}.
137
+ - Tabla de FALSOS POSITIVOS conocidos: patrón | ¿falso positivo? | por qué (evita re-reportar decisiones de diseño).
138
+ - Regiones a NO auditar: generados, lock, componentes de librería.
139
+ -->
@@ -136,7 +136,7 @@ Si el repo define su propio template (`.github/pull_request_template.md`), léel
136
136
  <!-- navori:user-section -->
137
137
  ## Reglas del proyecto
138
138
 
139
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
139
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
140
140
  - Template específico del PR si difiere del default (.github/pull_request_template.md).
141
141
  - Convenciones de scope obligatorias (lista de scopes válidos, mappings de área → scope).
142
142
  - Reglas de naming de branches (ej: `feat/BT-1234-descripcion`).
@@ -25,7 +25,7 @@ Si la pregunta es puntual ("¿dónde está X?"), no eres tú — es `researcher`
25
25
  1. Lee `CLAUDE.md` para entender convenciones del repo.
26
26
  2. Define el alcance: una carpeta, un módulo lógico, un patrón de archivos. El orquestador debería pasártelo preciso; si llega ambiguo, devuelve `blocked` nombrando las opciones (carpeta X / módulo Y / patrón Z) para que reenvíe acotado — no adivines.
27
27
  3. Recorre desde los entry points (rutas, exports raíz del módulo, `index.ts`) hacia las hojas. Para cada nivel, lista archivos y su rol breve.
28
- 4. Identifica dependencias inversas: ¿qué módulos externos consumen este módulo? Eso indica el "blast radius" de cambiar algo acá.
28
+ 4. Identifica dependencias inversas: ¿qué módulos externos consumen este módulo? Eso indica el "blast radius" de cambiar algo aquí.
29
29
  5. Escribe `.claude/progress/explore_<area>.md`:
30
30
 
31
31
  ```markdown
@@ -81,7 +81,7 @@ done -> .claude/progress/explore_<area>.md
81
81
  <!-- navori:user-section -->
82
82
  ## Reglas del proyecto
83
83
 
84
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
84
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
85
85
  - Áreas que típicamente necesitan exploración (módulos grandes, monorepo workspaces).
86
86
  - Convenciones de naming que ayudan a clasificar archivos (sufijos, prefijos).
87
87
  - Limitaciones: módulos generados que no vale mapear (ej: dist/, *.gen.ts).
@@ -12,10 +12,10 @@ Ejecutas **una sola** tarea desde inicio hasta verificación. No orquestas, no l
12
12
  ## Protocolo
13
13
 
14
14
  1. **Lee** `CLAUDE.md`. Identifica las convenciones del repo y las "Reglas del proyecto" (la sección del orquestador en `CLAUDE.md`).
15
- 2. **Anota** en `.claude/progress/current.md`:
15
+ 2. **Anota** en `.claude/progress/impl_<feature>.md` (tu archivo de trabajo; al cerrar se convierte en el informe):
16
16
  - `Tarea: <descripción breve>`
17
17
  - `Root cause: <archivo:línea + por qué>` (solo si la tarea es bugfix; no puedes tocar código sin esto).
18
- - `Plan:` — tareas atómicas con checkboxes, una acción de 2–5 min cada una. Marca `[x]` al ir completando para que `current.md` refleje progreso real. Ejemplo:
18
+ - `Plan:` — tareas atómicas con checkboxes, una acción de 2–5 min cada una. Marca `[x]` al ir completando para que tu `impl_<feature>.md` refleje progreso real. Ejemplo:
19
19
 
20
20
  ```
21
21
  - [ ] Definir interface en <path>
@@ -39,12 +39,14 @@ Ejecutas **una sola** tarea desde inicio hasta verificación. No orquestas, no l
39
39
  ## Reglas duras (genéricas, aplican siempre)
40
40
 
41
41
  - **Una sola tarea por sesión.** Si descubres que tu cambio requiere tocar otra cosa fuera del scope, paras y reportas `blocked`.
42
+ - **Nunca escribas `progress/current.md` (raíz).** El estado de sesión lo consolida el líder; tú puedes correr en paralelo con otros implementers y ese archivo es compartido. Tu único archivo de progreso es `.claude/progress/impl_<feature>.md`.
42
43
  - **Tipado fuerte, `any` prohibido en código nuevo.** Definir tipos correctos antes de avanzar. Usa `unknown` + narrowing, generics, o tipos de dominio. Cubre parámetros, retornos, callbacks, eventos, props, hooks y responses de services. Si tipar bien es genuinamente imposible (lib de tercero sin types), comentario `// any justificado: <razón>` — último recurso, no atajo.
43
44
  - **Sin hardcode**: secretos / URLs / endpoints via env vars (`process.env.*`, `import.meta.env.*`, según stack).
44
45
  - **Sin `console.log`** en código que se va a mergear (guard `import.meta.env.DEV` o equivalente del runtime).
45
46
  - **Cero errores nuevos** introducidos por tu código en las herramientas del quality gate (vs. baseline). Si dudas del baseline: `git stash` → re-correr → `git stash pop` → comparar. Devolver con cualquier herramienta en rojo (por tu cambio) es motivo automático de `CHANGES_REQUESTED`.
46
47
  - **JSDoc** obligatorio en exports públicos y funciones >15 líneas o con lógica condicional densa.
47
- - Si una herramienta falla raro (ej. tsc rompe sin diff aparente), **no improvises workaround**: anota `blocked` en `.claude/progress/current.md` y paras.
48
+ - **Trazabilidad SDD** (solo si la feature tiene `{{sdd.specsDir}}/<feature>/tasks.md`, ver bloque SDD en `CLAUDE.md`): cada `R<n>` de tu lote queda cubierto por ≥1 test, y cada test referencia sus requisitos con un comentario `// Covers: R<n>` arriba del caso. Sin trazabilidad completa el `reviewer` rechaza.
49
+ - Si una herramienta falla raro (ej. tsc rompe sin diff aparente), **no improvises workaround**: anota `Estado: BLOCKED` + el motivo en `.claude/progress/impl_<feature>.md` y paras.
48
50
 
49
51
  ## Evidence-based completion (gate antes del informe)
50
52
 
@@ -91,15 +93,17 @@ done -> .claude/progress/impl_<feature>.md
91
93
  o
92
94
 
93
95
  ```
94
- blocked -> .claude/progress/current.md
96
+ blocked -> .claude/progress/impl_<feature>.md
95
97
  ```
96
98
 
99
+ (En ambos casos el archivo es el mismo: tu informe con `Estado: DONE | BLOCKED`. El líder consolida blockers y estado de sesión en `progress/current.md`; tú no tocas ese archivo.)
100
+
97
101
  Nunca devuelvas el diff en chat. El líder lo lee del disco si lo necesita.
98
102
 
99
103
  <!-- navori:user-section -->
100
104
  ## Reglas del proyecto
101
105
 
102
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
106
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
103
107
  - Flujo de capas exacto (ej: `axios → services → adapters → components`).
104
108
  - Libs forzadas / prohibidas (forms, tables, state).
105
109
  - Paths de naming convention (`<NAME>_LABELS`, etc).
@@ -15,7 +15,7 @@ Tu único trabajo como orquestador es **descomponer y coordinar**, nunca impleme
15
15
 
16
16
  1. Lee `CLAUDE.md` (stack, convenciones, quality gate).
17
17
  2. El catálogo de subagentes y skills está en `CLAUDE.md` (`## Agentes disponibles`, `## Skills disponibles`).
18
- 3. Lee `.claude/progress/current.md` si existe — estado de la sesión anterior.
18
+ 3. Lee `progress/current.md` (raíz del repo) si existe — estado de la sesión anterior.
19
19
  4. Identifica el scope de la tarea contra las "Reglas del proyecto" abajo (legacy paths, áreas críticas, convenciones del repo).
20
20
  5. **¿Llega texto de un ticket (Jira/Linear/GitHub/Slack)?** Si matchea los triggers de tu agente `ticket-audit` (bug en feature crítica, migración estructural, feature que cruza >3 capas), invoca primero ese agente — produce `.claude/progress/audit_<ID>.md` que orienta toda la descomposición posterior. Para tickets triviales (typo, copy, color), sáltate el audit.
21
21
  6. **Brainstorm gate (opcional, condicional)**: si la tarea introduce un patrón nuevo, decisión arquitectural o lib nueva (NO aplica a fixes / triviales / features que sigan patterns existentes), antes del implementer:
@@ -91,9 +91,11 @@ Archivos esperados:
91
91
  - `.claude/progress/audit_<TICKET-ID>.md` — análisis profundo del ticket (`ticket-audit`)
92
92
  - `.claude/progress/explore_<tema>.md` — mapa amplio (`explorer`)
93
93
  - `.claude/progress/research_<pregunta>.md` — pregunta acotada (`researcher`)
94
- - `.claude/progress/impl_<feature>.md` — informe del `implementer`
94
+ - `.claude/progress/impl_<feature>.md` — informe del `implementer` (incluye su `Estado: DONE | BLOCKED`)
95
95
  - `.claude/progress/review_<feature>.md` — veredicto del `reviewer`
96
96
 
97
+ **Separación de rutas (no mezclar):** `.claude/progress/` es SOLO para estos handoffs efímeros entre agentes. El **estado de sesión** (tarea en curso, plan, blockers) vive en `progress/current.md` (raíz del repo, persiste en git) y lo consolidas **TÚ, únicamente**: los subagentes nunca lo escriben. Cuando un `implementer` reporta `blocked` en su `impl_<feature>.md`, tú registras el blocker en `progress/current.md` junto con el siguiente paso.
98
+
97
99
  ## Cierre del ciclo: crear el PR
98
100
 
99
101
  Cuando `.claude/progress/review_<feature>.md` contenga `APPROVED`:
@@ -131,7 +133,7 @@ Si la tarea es:
131
133
  <!-- navori:user-section -->
132
134
  ## Reglas del proyecto
133
135
 
134
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
136
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
135
137
  - Áreas críticas que requieren review extra: {{project.criticalAreas}}
136
138
  - Carpetas legacy con reglas distintas: {{project.legacyPaths}}
137
139
  - Convenciones de naming / estructura del repo.
@@ -76,7 +76,7 @@ Nunca devuelvas el contenido del informe en chat. El leader lo lee del disco.
76
76
  <!-- navori:user-section -->
77
77
  ## Reglas del proyecto
78
78
 
79
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
79
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
80
80
  - Subsistemas con naming particular donde grep simple falla (módulos generados, abreviaturas).
81
81
  - Repos hermanos o submódulos que también vale buscar (paths absolutos).
82
82
  - Patrones de búsqueda compuestos que se usan recurrentemente.
@@ -14,12 +14,17 @@ Eres un revisor estricto. Tu única función es **aprobar o rechazar**. No edita
14
14
  ### Setup (común a las dos pasadas)
15
15
 
16
16
  1. Lee `CLAUDE.md`, `.claude/progress/impl_<feature>.md`, `.claude/progress/audit_<ID>.md` (si existe).
17
- 2. Identifica archivos modificados:
17
+ 2. Identifica archivos modificados. Difea contra `{{prTarget}}` (la rama destino
18
+ del PR), **no** contra el punto de fork: es el diff EXACTO que verá GitHub y el
19
+ que revisa commit-pr-pilot. Cuando `{{branchBase}}` ≠ `{{prTarget}}` (p.ej.
20
+ ramificas de `main` pero el PR va a `develop`) revisar contra el fork mostraría
21
+ un diff distinto al del PR.
18
22
 
19
23
  ```bash
20
24
  git status --short
25
+ git fetch origin {{prTarget}} --quiet
21
26
  git diff --stat
22
- git diff origin/{{branchBase}}...HEAD
27
+ git diff origin/{{prTarget}}...HEAD
23
28
  ```
24
29
 
25
30
  3. Aplica `.claude/skills/verify-before-done.md` antes de cualquier veredicto: corre los comandos de quality gate **en este turno** (no asumas del cache del informe del implementer).
@@ -32,6 +37,7 @@ Eres un revisor estricto. Tu única función es **aprobar o rechazar**. No edita
32
37
  - ¿Está dentro del scope acordado? (Si tocó archivos fuera del scope del audit/ticket → flag)
33
38
  - ¿Falta algo del scope? (Si el ticket pedía A+B y solo hizo A → flag)
34
39
  - Si la tarea es bugfix: ¿el `Root cause:` documentado en `impl_<feature>.md` matchea con el fix?
40
+ - **Trazabilidad SDD** (solo si existe `{{sdd.specsDir}}/<feature>/tasks.md`): cada `R<n>` del lote está cubierto por ≥1 test que lo referencia con `// Covers: R<n>`. Un `R<n>` del lote sin test trazable → `SPEC_MISS`.
35
41
  - ¿La UI fue validada manualmente (según informe del implementer)? Si NO y el cambio toca pantallas → escalar a humano.
36
42
 
37
43
  **Veredicto parcial:**
@@ -41,7 +47,7 @@ Eres un revisor estricto. Tu única función es **aprobar o rechazar**. No edita
41
47
 
42
48
  ### Pasada 2 — Code quality (solo si SPEC_OK)
43
49
 
44
- ¿El código matchea las convenciones del repo? Acá sí revisas estilo/naming/tipos.
50
+ ¿El código matchea las convenciones del repo? Aquí sí revisas estilo/naming/tipos.
45
51
 
46
52
  Aplica `.claude/skills/review-diff.md` — la checklist completa por dimensiones (tipos, capa de datos, errores, seguridad, hardcode, naming, dead code) con severidades. Sus CRÍTICO/ALTO mapean a los issues ≥80 de abajo; MEDIO a las observaciones informativas. Resumen de lo mínimo a validar contra `CLAUDE.md` y las "Reglas del proyecto" del leader:
47
53
 
@@ -141,13 +147,14 @@ CHANGES_REQUESTED -> .claude/progress/review_<feature>.md
141
147
  - ❌ Nunca apruebes si el código nuevo **agrega errores o warnings nuevos** vs baseline.
142
148
  - ❌ Nunca apruebes código nuevo con `any` explícito o implícito sin `// any justificado: <razón>` válido.
143
149
  - ❌ Nunca apruebes si la UI no fue validada manualmente y el cambio toca pantallas.
150
+ - ❌ En features SDD (con `tasks.md`), nunca apruebes si algún `R<n>` del lote no tiene un test trazable que lo cubra.
144
151
  - ❌ Nunca editas el código. Solo señalas qué falla y dónde.
145
152
  - ✅ Sé concreto: cita `archivo:línea`. Nada de feedback genérico.
146
153
 
147
154
  <!-- navori:user-section -->
148
155
  ## Reglas del proyecto
149
156
 
150
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
157
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
151
158
  - Chequeos de convenciones que tu reviewer debe correr siempre (libs, capas, patrones).
152
159
  - Anti-patterns específicos del stack que son auto-CHANGES_REQUESTED.
153
160
  - Reglas de áreas críticas: {{project.criticalAreas}}
@@ -65,7 +65,7 @@ Si encuentras un audit reciente para el mismo ticket, léelo primero. No re-audi
65
65
  <2–4 líneas: qué pide el ticket, dónde impacta>
66
66
 
67
67
  ## Hipótesis de causa raíz (si es bug)
68
- 1. [confianza:0–100] `<archivo>:<línea>` — <descripción + por qué crees que es acá>
68
+ 1. [confianza:0–100] `<archivo>:<línea>` — <descripción + por qué crees que es aquí>
69
69
 
70
70
  ## Approaches alternativos (si es feature/refactor)
71
71
  ### Approach A — <nombre>
@@ -116,7 +116,7 @@ El leader lee el audit del disco y descompone desde ahí.
116
116
  <!-- navori:user-section -->
117
117
  ## Reglas del proyecto
118
118
 
119
- <!-- user: agrega acá lo específico de tu repo. Sugerencias:
119
+ <!-- user: agrega aquí lo específico de tu repo. Sugerencias:
120
120
  - Áreas críticas que casi siempre requieren audit: {{project.criticalAreas}}
121
121
  - Subsistemas con reglas particulares (ej: migración legacy↔nuevo backend, módulo X solo lo toca alguien con context).
122
122
  - Patrones de tickets recurrentes que tienen plantilla de análisis específica.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: apollo-client
3
+ description: GraphQL con Apollo Client — hooks, fetchPolicy, normalización de caché y actualización tras mutaciones. Aplica al escribir queries/mutations, configurar la caché o los links.
4
+ type: reference
5
+ ---
6
+
7
+ # Apollo Client — el patrón canónico
8
+
9
+ Lecturas declarativas con hooks, caché **normalizada por id**, y la UI se mantiene en sync actualizando la caché tras cada mutación. Los concerns de red/auth viven en los links, no en los componentes.
10
+
11
+ ## Cuándo usar este skill
12
+
13
+ Al escribir una query/mutation, configurar `InMemoryCache`/`typePolicies`, o la cadena de `links`.
14
+
15
+ ## Hooks y aislamiento
16
+
17
+ `useQuery` (lectura al montar), `useLazyQuery` (bajo demanda, retorna `execute`), `useMutation` (retorna `[mutate, { data, loading, error }]`). Aísla los hooks en una capa (hook + adapter): el componente recibe un **modelo de dominio**, no el shape crudo de GraphQL.
18
+
19
+ ```ts
20
+ export function useReport(id: string) {
21
+ const { data, loading, error } = useGetReportQuery({ variables: { id }, fetchPolicy: 'cache-first' });
22
+ return { report: data?.report ? adaptReport(data.report) : null, loading, error };
23
+ }
24
+ ```
25
+
26
+ ## fetchPolicy según el dato
27
+
28
+ - `cache-first` (default) — catálogos/detalles ya traídos por una lista.
29
+ - `cache-and-network` — feeds que cambian seguido (render instantáneo + refresh).
30
+ - `network-only` — sesión/bootstrap, datos críticos.
31
+ - Evita `no-cache` salvo PII estricta que no deba tocar disco.
32
+
33
+ ## Normalización de caché
34
+
35
+ ```ts
36
+ const cache = new InMemoryCache({ typePolicies: { Report: { keyFields: ['id'] } } });
37
+ ```
38
+
39
+ Con `keyFields`, Apollo identifica entidades por id y deduplica/actualiza solo. Sin normalización, las listas y detalles se desincronizan.
40
+
41
+ ## Reglas duras
42
+
43
+ 1. **Tras una mutation, actualiza la caché:** `update(cache, { data })` (`cache.modify`/`evict`/`writeQuery`) o `refetchQueries`. Nunca dejes la UI desincronizada.
44
+ 2. **`optimisticResponse`** para UI instantánea (resultado temporal con `__typename` + id ficticio); `update` reconcilia al llegar la respuesta real.
45
+ 3. **No over-fetch:** pide solo los campos que el componente usa; apóyate en **fragments con colocation** (el fragmento junto al componente que lo consume). Regenera tipos (codegen) tras editar `.graphql`.
46
+ 4. **Maneja `loading` y `error` siempre.** Separa error de red (banner genérico, resuelto en un `errorLink`) de error de negocio (`graphQLErrors`, copy según `extensions.code`).
47
+ 5. **Paginación** con `fetchMore` + `updateQuery`, o `relayStylePagination`/merge en `typePolicies`.
48
+ 6. Red/auth/upload en la cadena de **links** (auth → error → upload), no en cada componente.
49
+
50
+ ```ts
51
+ const [createReport] = useCreateReportMutation({
52
+ optimisticResponse: { createReport: { __typename: 'Report', id: 'temp', ...fields } },
53
+ update(cache) { cache.evict({ fieldName: 'reports' }); }, // invalida la lista
54
+ });
55
+ ```
56
+
57
+ ## Antes de declarar listo
58
+
59
+ - Hooks aislados en capa (hook + adapter); el componente ve el modelo de dominio.
60
+ - Caché normalizada por `keyFields`; mutaciones actualizan/invalidan la caché.
61
+ - `fetchPolicy` elegido por tipo de dato; `loading`/`error` manejados.
62
+ - `{{qualityGate.fast}}` en verde.
@@ -8,9 +8,9 @@ type: reference
8
8
 
9
9
  ## Cuándo usar este skill
10
10
 
11
- Cuando la tarea toca `domain/models` o ejecuta operaciones de Mongoose en los controllers. Mongoose 6+ sobre MongoDB. El repo no usa repository wrappers: los controllers tocan los Models directo, así que null-guards, casts de ObjectId y `.lean()` viven en cada controller method.
11
+ Cuando la tarea toca `domain/models` o ejecuta operaciones de Mongoose en los controllers. Sin repository wrappers: los controllers tocan los Models directo, así que null-guards, casts de ObjectId y `.lean()` viven en cada controller method.
12
12
 
13
- En **NestJS** (`@nestjs/mongoose`) el Model no se importa directo: se inyecta con `@InjectModel(Resource.name) private resourceModel: Model<ResourceDocument>` en el constructor del service. El resto de patrones (`.lean()`, `new Types.ObjectId`, null-guards, soft delete) aplican igual.
13
+ En **NestJS** (`@nestjs/mongoose`) el Model se inyecta: `@InjectModel(Resource.name) private model: Model<ResourceDocument>`; el resto de patrones aplican igual.
14
14
 
15
15
  ## Patrón canónico
16
16
 
@@ -21,36 +21,31 @@ if (!doc) throw new NotFoundError(`Resource ${id} not found`);
21
21
  const docs = await Resource.find(filter).lean<IResource[]>(); // read-only
22
22
 
23
23
  const updated = await Resource.findByIdAndUpdate(
24
- id, { $set: dto }, { new: true, runValidators: true }
24
+ id, { $set: dto }, { returnDocument: 'after', runValidators: true }
25
25
  );
26
26
  ```
27
27
 
28
- `{ new: true }` devuelve el doc actualizado, no el viejo; `{ runValidators: true }` valida updates parciales.
28
+ `returnDocument: 'after'` (reemplaza el deprecado `new: true`) devuelve el doc actualizado; `runValidators: true` valida el update parcial. Ojo: `findByIdAndUpdate` **no** dispara hooks `pre('save')` ni valida el doc completo — si hay lógica en middleware `save`, usa `doc.save()`.
29
29
 
30
30
  ## ObjectId — la trampa más común
31
31
 
32
- Un `id` de `req.params`/`req.body` es **string**. `findById` lo castea solo, pero aggregations y queries complejas requieren cast explícito con `new`:
33
-
34
- ```ts
35
- import { Types } from 'mongoose';
36
- const _id = new Types.ObjectId(id); // Mongoose 6+ exige `new`
37
- ```
38
-
39
- Valida el formato en el schema (`z.string().regex(/^[a-f\d]{24}$/i, ...)`); si no, un string mal-formado lanza `CastError`. Compara ObjectId con `.equals()`, no con `==`.
32
+ Un `id` de `req.params`/`req.body` es **string**. `findById` lo castea solo, pero aggregations y queries complejas requieren `new Types.ObjectId(id)` (el `new` es obligatorio en Mongoose 6+). Valida el formato antes (`/^[a-f\d]{24}$/i`) o un string mal-formado lanza `CastError`. Compara ObjectId con `.equals()`, nunca `==`.
40
33
 
41
34
  ## Gotchas que muerden
42
35
 
43
- - **N+1**: `populate` ejecuta queries extra. En paginate sobre datasets grandes usa `$lookup` en vez de `populate`.
44
- - **`.lean()`**: el resultado no tiene `.save()`, `.delete()` ni virtuals. Si necesitas mutar, no lo uses.
45
- - **Soft delete**: con `mongoose-delete`, `find` ya excluye `deleted: true`; borra con `doc.delete()` (no `findByIdAndDelete`) y restaura con `doc.restore()`.
36
+ - **Query injection**: `Model.find(req.query)` crudo deja pasar operadores (`{ $ne: null }`). Arma el filtro campo por campo o `.setOptions({ sanitizeFilter: true })`. Y `strictQuery` es `false` por default (Mongoose 7+): un campo con typo se ignora → filtro vacío que devuelve **toda** la colección.
37
+ - **Atomicidad multi-doc**: escrituras relacionadas en `connection.transaction(async (session) => {...})`, pasando `{ session }` a cada op. `bulkWrite` no es transacción.
38
+ - **Índices**: filtros y `.sort()` sobre campos sin índice = COLLSCAN. Declara `schema.index(...)`, verifica con `.explain()`.
39
+ - **populate** batchea con `$in` (1 query por path, no N); no filtra/ordena por el child — ahí `$lookup`. `.lean()` pierde `.save()`/virtuals.
40
+ - **Soft delete**: con `mongoose-delete`, `find` ya excluye `deleted: true`; borra con `doc.delete()` (no `findByIdAndDelete`), restaura con `doc.restore()`.
46
41
 
47
42
  ## Reglas duras
48
43
 
49
44
  1. **Ops de Mongoose nunca en la ruta** — siempre dentro de un controller method.
50
- 2. **Null-guard tras `findById`/`findOne`** — `if (!doc) throw new NotFoundError(...)`. Nada de `if (doc) {...}` silencioso.
51
- 3. **`.lean()` cuando no necesitas mutar** evita el overhead de documentos Mongoose.
52
- 4. **Comparar ObjectId con `.equals()`** / `.toString()`, nunca `==`.
53
- 5. **Respeta el soft delete del repo** no hard delete en modelos con `mongoose-delete`.
45
+ 2. **Null-guard tras `findById`/`findOne`** — `if (!doc) throw new NotFoundError(...)`.
46
+ 3. **`.lean()` cuando no necesitas mutar**; comparar ObjectId con `.equals()`, nunca `==`.
47
+ 4. **Nunca `Model.find(req.query)` crudo** filtro campo por campo o `sanitizeFilter`.
48
+ 5. **Respeta el soft delete del repo**; escrituras relacionadas en `connection.transaction`.
54
49
 
55
50
  ## Tabla rápida
56
51
 
@@ -59,16 +54,14 @@ Valida el formato en el schema (`z.string().regex(/^[a-f\d]{24}$/i, ...)`); si n
59
54
  | Buscar por id | `findById(id)` + null-guard → `NotFoundError` |
60
55
  | Cast string → ObjectId | `new Types.ObjectId(id)` |
61
56
  | Query read-only | `.find(filter).lean()` |
62
- | Cargar relaciones | `.populate({ path, model })` (cuidado N+1) |
63
- | Paginar | plugin `.paginate(...)` o `skip().limit()` + `countDocuments` |
57
+ | Paginar | `.paginate(...)` o `skip().limit()` + `countDocuments` |
64
58
  | Borrar con soft delete | `doc.delete()` (no `findByIdAndDelete`) |
65
- | Update devolviendo el nuevo doc | `findByIdAndUpdate(id, { $set }, { new: true, runValidators: true })` |
66
- | Muchas escrituras | `bulkWrite([...])` |
59
+ | Update devolviendo el nuevo | `findByIdAndUpdate(id, { $set }, { returnDocument: 'after', runValidators: true })` |
60
+ | Escrituras relacionadas | `connection.transaction(async (session) => …)` |
67
61
 
68
62
  ## Antes de declarar listo
69
63
 
70
- - Toda op de Mongoose vive en un controller method, no en la ruta.
71
- - Cada `findById`/`findOne` tiene su null-guard que lanza `NotFoundError`.
72
- - Las queries read-only usan `.lean()`; los ObjectId se comparan con `.equals()`.
73
- - Los borrados respetan el soft delete del modelo cuando aplica.
64
+ - Cada `findById`/`findOne` tiene null-guard `NotFoundError`; read-only con `.lean()`.
65
+ - Ningún filtro arma con `req.query`/`req.body` crudo; ObjectId comparado con `.equals()`.
66
+ - Borrados respetan soft delete; escrituras relacionadas van en transacción.
74
67
  - `{{qualityGate.fast}}` en verde.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: react-hook-form
3
+ description: Patrones de React Hook Form en React+TS — register vs Controller, zodResolver, errores por campo, re-renders. Aplica al crear o tocar formularios con RHF.
4
+ type: reference
5
+ ---
6
+
7
+ # React Hook Form — convenciones
8
+
9
+ ## Cuándo usar este skill
10
+
11
+ Al crear o tocar un formulario con RHF: validación con Zod, submit, errores, o cablear inputs de una lib controlada (Mantine/MUI `Select`, `DatePicker`). RHF es la fuente de verdad del form — no dupliques sus valores en `useState`. Ventaja sobre Formik: los inputs nativos van **uncontrolled** (vía refs), así que teclear no re-renderiza el form entero.
12
+
13
+ ## El patrón
14
+
15
+ Uncontrolled por defecto + Zod como schema + `Controller` **solo** donde el input no emite un evento DOM nativo.
16
+
17
+ ```tsx
18
+ const schema = z.object({ email: z.string().email(), role: z.enum(['coach', 'coachee']) });
19
+ type FormValues = z.infer<typeof schema>;
20
+
21
+ const { register, control, handleSubmit, formState: { errors, isSubmitting } } =
22
+ useForm<FormValues>({ resolver: zodResolver(schema), defaultValues: { email: '', role: 'coachee' } });
23
+
24
+ <TextInput error={errors.email?.message} {...register('email')} /> // nativo → register
25
+ // Select de Mantine (onChange da el valor, no un event) → Controller:
26
+ <Controller control={control} name="role" render={({ field, fieldState }) => (
27
+ <Select data={['coach','coachee']} error={fieldState.error?.message} {...field} />
28
+ )} />
29
+ ```
30
+
31
+ ## Gotchas que muerden
32
+
33
+ - **`register` por defecto; `Controller` es la excepción.** Un input que reenvía `ref` y dispara `onChange` con un evento DOM (texto, textarea, checkbox nativo, `<TextInput>` de Mantine) va con `{...register('campo')}`. Envolverlo en `Controller` re-introduce el re-render por tecla que RHF existe para evitar.
34
+ - **Cuándo SÍ va `Controller`:** componentes cuyo `onChange` entrega el **valor directo** — Mantine `Select`/`MultiSelect`/`NumberInput`/`DateInput`, todo MUI, `react-select`. Cablea `field.value`/`onChange`/`onBlur`/`ref`; el error sale de `fieldState.error?.message`.
35
+ - **`defaultValues` no es opcional.** Sin él, un campo arranca `undefined` → warning "uncontrolled to controlled" (`Controller` con `undefined` es inválido: usa `null`/`''`). Para edición async usa `reset(data)` en un `useEffect`, no valores a mano en cada render.
36
+ - **`watch()` re-renderiza todo.** Para leer en submit usa `getValues('campo')`; para que un hijo dependa de un campo, `useWatch({ control, name })` en ese hijo. `watch()` global en un form grande es anti-patrón.
37
+ - **Números: `register('age', { valueAsNumber: true })`.** Sin esto un `type="number"` entrega **string** y tu `z.number()` falla. Corre antes del resolver, así validas con `z.number()` directo.
38
+ - **`useFieldArray` con `key={field.id}`, nunca el índice** (corrompe el estado al reordenar). Error de servidor con `setError('root.server', …)`, no en un campo.
39
+
40
+ ## Reglas duras
41
+
42
+ 1. Validación en schema Zod vía `zodResolver`; tipo por `z.infer`. Nada de `rules` inline ni tipos paralelos.
43
+ 2. `register` por defecto; `Controller` solo para inputs sin evento DOM nativo.
44
+ 3. `defaultValues` siempre; edición async con `reset(data)`, sin `useState` espejo.
45
+ 4. `getValues`/`useWatch` para leer sin re-render; `isSubmitting` deshabilita el botón.
46
+
47
+ ## Tabla rápida
48
+
49
+ | Input | Cómo cablear |
50
+ |---|---|
51
+ | Texto / textarea / checkbox nativo | `{...register('campo')}` |
52
+ | Número | `register('n', { valueAsNumber: true })` |
53
+ | Select / Date / Number de Mantine/MUI | `<Controller>` + `{...field}` |
54
+ | Lista dinámica | `useFieldArray` + `key={field.id}` |
55
+
56
+ ## Antes de declarar listo
57
+
58
+ - Zod + `zodResolver`, tipo por `z.infer`; `Controller` para inputs controlados, `register` para texto; sin `useState` espejo.
59
+ - `defaultValues` seteado; sin warnings "uncontrolled to controlled". Submit con `handleSubmit` + `isSubmitting`.
60
+ - `{{qualityGate.fast}}` en verde.
61
+ </content>
@@ -31,16 +31,18 @@ export const { setActive } = slice.actions;
31
31
  Store + hooks tipados una sola vez, y se usan en toda la app:
32
32
 
33
33
  ```ts
34
- export const useAppDispatch: () => AppDispatch = useDispatch;
35
- export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
34
+ export const useAppDispatch = useDispatch.withTypes<AppDispatch>(); // patrón vigente (RTK 2 / react-redux 9)
35
+ export const useAppSelector = useSelector.withTypes<RootState>(); // no el viejo TypedUseSelectorHook
36
36
  ```
37
37
 
38
38
  ## Gotchas que muerden
39
39
 
40
40
  - **Immer solo dentro de `createSlice`.** Ahí "mutas" el draft; fuera de un reducer, mutar el state es un bug. No retornes Y mutes en el mismo reducer.
41
41
  - **Selectores memoizados** con `createSelector` cuando derivan/transforman — un selector que crea un array/objeto nuevo en cada llamada re-renderiza siempre.
42
- - **`useSelector` devuelve la referencia**: selecciona lo mínimo, no el slice entero.
43
- - **Async**: `createAsyncThunk` para casos simples; si es data de API que cacheas/invalidas, evalúa RTK Query en vez de thunks + slice manual.
42
+ - **`useSelector` devuelve la referencia**: selecciona lo mínimo, no el slice entero. Si necesitas varios campos, envuelve con `useShallow(...)` (react-redux 9) para comparar superficial y no re-renderizar de más.
43
+ - **Efectos reactivos `createListenerMiddleware`**, no un `useEffect` espiando el store ni sagas. Reacciona a una acción/cambio de estado desde el middleware.
44
+ - **Colecciones por id → `createEntityAdapter`**: `selectAll`/`selectById` memoizados gratis, CRUD normalizado, sin arreglos a mano.
45
+ - **Async**: `createAsyncThunk` simple; si es data de API que cacheas/invalidas, evalúa RTK Query. `extraReducers` con builder callback (`(b) => b.addCase(...)`), la forma-objeto se eliminó en RTK 2.
44
46
  - **No-serializables** (Date, Map, funciones) fuera del store; rompen devtools y persistencia.
45
47
 
46
48
  ## Reglas duras
@@ -19,7 +19,7 @@ io.of('/sessions').use(authSocket).on('connection', (socket) => {
19
19
  socket.on('message:send', async (dto, ack) => {
20
20
  try {
21
21
  const saved = await messageService.create(socket.data.userId, dto);
22
- io.to(`session:${dto.sessionId}`).emit('message:new', saved);
22
+ io.to(`session:${socket.data.sessionId}`).emit('message:new', saved); // room de socket.data, no del payload
23
23
  ack?.({ ok: true, id: saved._id });
24
24
  } catch (err) {
25
25
  ack?.({ ok: false, error: toClientError(err) });
@@ -39,6 +39,9 @@ io.of('/sessions').use(authSocket).on('connection', (socket) => {
39
39
  - **Listeners colgados.** Toda suscripción/intervalo creado en `connection` se limpia en `disconnect`, o se filtra memoria.
40
40
  - **Errores.** Un throw dentro de un handler no llega al cliente: reporta vía el callback `ack` o un evento `error:*`, nunca dejes la promesa sin catch.
41
41
  - **Auth en el handshake**, no por evento — rechaza en el middleware `.use()` antes de `connection`.
42
+ - **Tipa el `Server`/`Socket`** con las 4 interfaces (`Server<ClientToServerEvents, ServerToClientEvents, InterServerEvents, SocketData>`): autocompletado y type-check de payloads y acks. `socket.data` se tipa vía `SocketData`, no `any`.
43
+ - **Multi-instancia ⇒ adapter (Redis) + sticky sessions.** Sin adapter, `io.to(room)` solo alcanza a los sockets de **este** proceso (broadcasts perdidos al escalar); sin sticky, el long-polling da "Session ID unknown".
44
+ - **Acks que esperan respuesta usan timeout**: `socket.timeout(ms).emitWithAck(...)`. Sin timeout, un ack ausente cuelga/leakea.
42
45
 
43
46
  ## Reglas duras
44
47