`packages/shadcn-ui` contiene código de terceros/vendor. No refactorizar, reformatear, reorganizar ni modificar su implementación salvo instrucción explícita del usuario. Puede consultarse su API pública para integrarlo desde código propio.
`archive/` conserva funcionalidades retiradas y sus archivos de integración. No modificar ni aplicarles las convenciones del código activo. Su recuperación se documenta en [funcionalidades archivadas](docs/funcionalidades-archivadas.md).
El espaciado vertical se define en `.lintrc`, cargado por `eslint.config.mjs`. Separar funciones, clases y métodos con una línea vacía; mantener agrupadas propiedades y firmas de sobrecarga. `pnpm format` combina Biome con estas reglas. Véase [formato y espaciado](docs/formato-y-espaciado-del-codigo.md).
- Priorizar un diseño de BD normalizado, con columnas y tablas relacionadas. No almacenar valores en JSON salvo necesidad estricta; en ese caso plantearlo al usuario antes de implementarlo. JSON como formato de transporte HTTP no sustituye el diseño relacional.
- Menor privilegio y validación de toda entrada externa.
- Autenticación y autorización antes de acceder a datos de clientes.
- El cliente atendido no es el tenant de la empresa que presta soporte.
- Verificar siempre permisos y pertenencia; no confiar en companyId enviado por el navegador.
- No exponer SQL, stacks, datos sensibles o credenciales en respuestas ni logs.
- Registrar identificadores de petición y resultados suficientes para diagnosticar fallos.
- MariaDB y Sequelize v6 para persistencia; ORM exclusivamente en Infrastructure.
- Consultas parametrizadas, transacciones para cambios atómicos y restricciones en base de datos.
- Migraciones explícitas; no usar sync({ alter: true }) como sustituto.
- Toda entidad de negocio, incluidas las entidades internas de un agregado, tiene identidad UniqueID (UUID v7 para nuevas entidades), siguiendo factuges-erp.
- Declarar Value Objects, DomainEntity y AggregateRoot según docs/arquitectura/modelo-ddd-e-identificadores.md.
- Mantener semántica de create, rehydrate y update según la arquitectura.
- Comparar cambios de dominio semánticamente, no mediante JSON.stringify.
- Añadir idempotencia donde una repetición pueda producir efectos duplicados.
Antes de realizar tareas de frontend o UI/UX, consultar [DESIGN.md](DESIGN.md), referencia del sistema visual. Los patrones especializados siguen siendo normativos en su ámbito; comprobar las APIs de los componentes y los tokens reales antes de implementarlos. Las mejoras pendientes documentadas no se consideran implementadas. Validar los cambios visuales relevantes en navegador cuando sea viable, siguiendo el procedimiento de Playwright MCP.
Todo código web nuevo de módulos, entidades y operaciones debe seguir [la estructura web por entidades y operaciones](docs/estructura-web-por-entidades-y-operaciones.md): `modules/<module>/src/web/<entity>/<operation>/`, con ámbitos `shared` según sus consumidores e índices únicamente donde haya contenido exportable para otras unidades. Esta guía es la fuente normativa de la organización interna del frontend; no cambia las capas del servidor ni la implementación vendor.
React, TanStack Query, Tailwind y shadcn/ui (Base UI, estilo Vega). Los componentes propios compartidos viven en @repo/rdx-ui y reutilizan las primitivas vendor de @repo/shadcn-ui. TanStack Table y React Hook Form ya tienen consumidores; seguir los patrones existentes.
Todos los diálogos nuevos o modificados siguen el [patrón de diseño de cuadros de diálogo](docs/patron-de-dialogos.md), fuente normativa para títulos uniformes, separación entre grupos, distribución responsive, alturas iguales por fila, secciones plegables, filas compactas, ordenación accesible y selección múltiple. Reutilizar los componentes de @repo/rdx-ui indicados en el patrón. En todos los `PickerList`, mantener altura estable, columna `selection` compacta al principio y selección al pulsar zonas no interactivas de la fila. Aplicar cada pauta según el tipo de contenido; conservar los campos y restricciones propios de cada entidad.
Antes de crear o modificar pantallas, reconocer activamente patrones visuales y de interacción comunes entre ellas, así como componentes repetidos dentro de una misma pantalla. Aplicar soluciones coherentes a todos los casos equivalentes, conservando las necesidades propias de cada entidad.
Reutilizar y extender la biblioteca visual existente antes de duplicar estructura, estilos o comportamiento. Extraer componentes compartidos cuando haya repetición real: cabeceras, listados, formularios modales, grupos de campos, selectores con alta, estados y ayuda contextual.
Construir progresivamente una biblioteca visual básica con contratos claros, accesibilidad, textos traducibles y composición sencilla. Mantener los componentes genéricos independientes de entidades, DTO, consultas y endpoints de Metis Desk; el módulo consumidor aporta datos, textos y acciones. Documentar patrones y componentes en docs/componentes-de-interfaz.md y en el estándar correspondiente.
Preparar esa independencia para poder agrupar o extraer posteriormente componentes en un paquete reutilizable en otras aplicaciones. Aprovechar @repo/shadcn-ui y crear abstracciones conforme aparezcan consumidores reales.
Las pantallas de listado de entidades siguen [el patrón de listados](docs/patron-de-listados-de-entidades.md), reutilizando los componentes comunes de @repo/rdx-ui y las primitivas de @repo/shadcn-ui.
En cualquier listado, incluidas tablas, sublistados y PickerList, las celdas sin valor muestran exclusivamente un guion largo (`—`). No usar textos como «Sin indicar», «Sin valor», «Sin especificar», «Sin asignación» o equivalentes traducidos. Conservar valores reales como cero y los estados de negocio; esta regla no sustituye los mensajes de carga, error o listado vacío.
Los listados no muestran formularios de alta ni edición junto a los datos. El alta se inicia con un botón en la cabecera, a la derecha; formularios en modal shadcn/ui como norma general, con anchura y desplazamiento adaptados a su contenido. La edición se abre mediante una acción explícita, siguiendo el mismo criterio. Mantener foco, cierre por teclado, cancelación y errores dentro del formulario. Incluir ayuda contextual con finalidad, ámbito y ejemplos, sin rellenar los campos automáticamente. En formularios, colocar la ayuda en la parte inferior, inmediatamente antes del pie del modal con Guardar y Cancelar.
Estas reglas complementan las convenciones y documentos de arquitectura existentes. Antes de introducir un patrón nuevo, comprobar cómo resuelve el repositorio casos equivalentes y mantener coherencia con ellos.
### Idioma
- Responder, explicar decisiones, documentar cambios y redactar comentarios y TSDoc en castellano.
- Mantener código, identificadores, nombres de tipos, funciones, variables, componentes, hooks y ficheros en inglés.
- No traducir nombres de APIs, bibliotecas, protocolos ni conceptos técnicos cuando su denominación habitual esté en inglés.
- Los mensajes destinados al usuario final deben pasar por el sistema de internacionalización cuando corresponda.
### TypeScript
- Mantener TypeScript en modo estricto y resolver los errores de tipos en su origen; no debilitarlos para conseguir que compile.
- Preferir inferencia cuando el tipo sea evidente y anotaciones explícitas cuando formen parte de un contrato público o mejoren realmente la comprensión.
- Usar `unknown` para datos cuyo tipo todavía no haya sido validado. No sustituir `unknown` por `any` para evitar realizar narrowing.
- Validar todos los datos procedentes de fronteras externas: HTTP, base de datos cuando corresponda, variables de entorno, almacenamiento, mensajes, APIs externas y datos serializados.
- No considerar un cast `as Tipo` como validación.
- Preferir narrowing, type guards, schemas y discriminated unions frente a assertions.
- Preferir `satisfies` cuando se quiera comprobar que un valor cumple un contrato conservando su inferencia concreta.
- Evitar enums cuando una unión de literales o un objeto constante tipado represente mejor un conjunto cerrado.
- Hacer exhaustivos los `switch` sobre discriminated unions mediante `never` cuando sea útil.
- Evitar parámetros booleanos cuyo significado no resulte evidente en la llamada; utilizar opciones o tipos semánticos cuando mejoren la legibilidad.
- No crear tipos contenedores, aliases o genéricos que solamente oculten otro tipo sin aportar semántica.
- No duplicar tipos que puedan derivarse de una única fuente normativa.
- Mantener la opcionalidad exacta del dominio: distinguir cuando sea relevante entre propiedad ausente, `undefined` y `null`.
- Preferir estructuras inmutables cuando los datos no deban modificarse.
- No capturar excepciones únicamente para volver a lanzarlas sin aportar contexto, conversión o recuperación.
- No usar excepciones como flujo normal para resultados de negocio esperables.
- Las funciones deben tener contratos claros y evitar efectos laterales inesperados.
### Node.js
- No bloquear el event loop con operaciones síncronas de I/O dentro de rutas, servicios ejecutados por petición o procesos concurrentes.
- Preferir APIs asíncronas para filesystem, red, criptografía y procesos externos cuando formen parte del flujo de ejecución normal.
- Toda operación externa susceptible de quedar bloqueada debe disponer de timeout cuando la API utilizada lo permita.
- Utilizar `AbortSignal` o mecanismos equivalentes cuando sea necesario propagar cancelación.
- No dispersar accesos a `process.env` por la aplicación. Cargar, validar y normalizar la configuración en un punto definido durante el arranque.
- Fallar durante el arranque si falta configuración imprescindible, en lugar de descubrirla durante una petición.
- No mantener estado mutable global dependiente de una petición.
- No almacenar en memoria información cuya persistencia o coherencia dependa de varias instancias del servidor.
- Gestionar explícitamente el cierre ordenado de recursos persistentes cuando corresponda: servidor HTTP, conexiones de base de datos, colas y otros recursos externos.
- No ocultar `unhandledRejection`, `uncaughtException` ni errores de infraestructura para mantener artificialmente el proceso funcionando.
### Express
- Mantener las rutas y controladores pequeños.
- El controlador HTTP traduce entre HTTP y la aplicación; no contiene lógica de negocio.
- No acceder directamente al ORM desde routers o controladores salvo que la arquitectura documentada establezca expresamente otra cosa.
- Validar `params`, `query`, `headers` y `body` en la frontera HTTP antes de utilizarlos.
- No confiar en tipos TypeScript para considerar válidos datos recibidos por HTTP.
- Centralizar la transformación de errores a respuestas HTTP.
- No repetir bloques `try/catch` en cada handler si el mecanismo de errores de Express ya propaga correctamente los rechazos.
- Distinguir errores esperables de aplicación, errores de validación, autenticación/autorización, conflictos y errores inesperados de infraestructura.
- No enviar al cliente mensajes internos de excepciones, SQL, stack traces ni detalles de implementación.
- Una petición debe producir una única respuesta. Evitar ramas que puedan continuar después de enviar la respuesta.
- Los middleware deben tener una responsabilidad concreta y efectos explícitos.
- No utilizar middleware global para comportamiento que solo pertenece a una ruta o módulo.
- Mantener la composición de routers, middleware, controladores, casos de uso y repositorios según la arquitectura documentada del proyecto.
### React 19
- Los componentes y hooks deben ser puros durante render.
- No producir efectos laterales durante render.
- No modificar props, estado, valores recibidos por hooks ni objetos pertenecientes a renders anteriores.
- No llamar componentes como funciones ordinarias; renderizarlos mediante JSX.
- Respetar estrictamente las Rules of Hooks.
- No utilizar hooks condicionalmente ni después de retornos que hagan variar su orden entre renders.
#### Organización de componentes
- Como norma general, cada fichero `.tsx` contiene un único componente React principal.
- No agrupar varios componentes funcionales relevantes en un mismo fichero por comodidad.
- Extraer un componente a su propio fichero cuando tenga props propias, estado, hooks, lógica de interacción, lógica de renderizado significativa o una responsabilidad identificable.
- Un subcomponente privado solo puede permanecer en el mismo fichero cuando sea pequeño, puramente presentacional, inseparable del componente principal y no dificulte la lectura.
- Si aparecen varios subcomponentes privados en el mismo fichero, reconsiderar la descomposición y extraerlos.
- No crear componentes únicamente para reducir el número de líneas de JSX. Extraer por responsabilidad, semántica o reutilización real.
- Evitar componentes monolíticos que mezclen consulta de datos, estado complejo, transformación de datos y grandes bloques de presentación.
- Mantener próximos entre sí el componente y los elementos que sean exclusivamente suyos, sin convertir los directorios en estructuras artificialmente profundas.
#### Estado y efectos
- Mantener el estado mínimo necesario.
- No guardar en estado valores que puedan calcularse correctamente a partir de props, estado existente o datos de consulta durante render.
- No utilizar `useEffect` para sincronizar un estado React con otro estado React.
- No utilizar `useEffect` para responder a una acción concreta del usuario; ejecutar esa lógica desde el event handler correspondiente.
- Utilizar efectos para sincronización real con sistemas externos, suscripciones, APIs imperativas del navegador u otros recursos externos.
- Todo efecto que cree una suscripción, listener, temporizador o recurso debe realizar su cleanup correspondiente.
- Diseñar efectos de forma que soporten correctamente montaje, desmontaje y nueva ejecución.
- No introducir `useMemo` o `useCallback` por defecto. Utilizarlos cuando exista una razón concreta de rendimiento, estabilidad referencial requerida por un contrato o evidencia obtenida mediante profiling.
- No duplicar en estado local datos gestionados por TanStack Query salvo que exista una necesidad explícita de edición local temporal.
- No copiar props a estado salvo que exista una semántica concreta que lo justifique.
- Preferir estado local al componente que lo consume y elevarlo únicamente cuando varios consumidores necesiten compartirlo.
#### Hooks
- Un custom hook debe encapsular comportamiento React reutilizable o una responsabilidad React claramente identificable.
- No crear hooks como simple sustitución de funciones normales.
- No crear hooks genéricos anticipando reutilización futura sin consumidores reales.
- Mantener los hooks independientes de detalles visuales cuando sea posible.
- No ocultar efectos importantes o llamadas remotas dentro de hooks con nombres ambiguos.
- Seleccionar una skill principal según el objetivo y complementarias solo cuando aporten valor. Comprobar su disponibilidad y leer sus `SKILL.md` y referencias aplicables antes de utilizar sus criterios en diseño, implementación y revisión.
- Respetar AGENTS.md, DESIGN.md y los patrones especializados; comprobar las APIs reales instaladas. Las skills complementan estas fuentes, no las sustituyen; una desviación del código respecto al patrón se señala, no se convierte en norma.
- Reutilizar shadcn/ui y `@repo/rdx-ui`. Las recomendaciones genéricas de una skill no autorizan regenerar o modificar vendor, introducir dependencias ni crear un sistema visual alternativo.
- Comprobar comportamiento visual, accesibilidad, teclado, foco, usabilidad, responsive y estados de carga/error/vacío mediante [Playwright MCP](docs/playwright-mcp.md) cuando corresponda y el entorno lo permita. Un checklist no sustituye las pruebas; seguir el procedimiento de evidencias y datos sintéticos existente.
- Informar qué skills se han consultado y cómo han influido, cuáles faltan y qué comprobaciones no se han ejecutado. No afirmar una aplicación o validación no realizada. La ausencia de auditores especializados no impide las verificaciones disponibles con Playwright MCP ni autoriza instalarlos automáticamente.
- Los componentes presentacionales no deben conocer endpoints, Express, Sequelize ni detalles de persistencia.
- Los componentes reutilizables no deben depender directamente de DTOs específicos de Metis Desk cuando puedan expresarse mediante props más pequeñas y semánticas.
- Preferir composición mediante `children` y props explícitas frente a componentes con multitud de flags booleanos.
- Evitar componentes con muchas variantes booleanas que produzcan combinaciones de estados difíciles de comprender.
- Mantener accesibilidad semántica: elemento HTML correcto, etiquetas, relaciones entre controles, navegación por teclado y foco.
- No implementar manualmente primitivas que shadcn/ui o Base UI ya proporcionen adecuadamente.
El diseño visual y de interacción sigue [el patrón de diálogos](docs/patron-de-dialogos.md); las siguientes reglas definen la organización de sus componentes.
- Páginas y listados no deben implementar directamente el contenido de ventanas modales funcionales. Los diálogos de alta, edición, confirmación, selección o detalle son componentes independientes.
- Toda ventana modal, cuadro de diálogo o formulario modal con contenido y responsabilidad propios debe extraerse a un componente React independiente y vivir en su propio fichero `.tsx`.
- Esta regla se aplica especialmente a componentes construidos con primitivas de shadcn/ui como `Dialog`, `AlertDialog`, `Sheet`, `Drawer` o componentes propios como `FormDialog`, pero no depende del nombre concreto utilizado.
- Si un bloque representa una interacción modal reconocible por sí misma —por ejemplo alta, edición, confirmación, selección, detalle, ayuda o cambio de estado— debe considerarse un componente independiente.
- El componente padre debe encargarse de decidir cuándo se abre el diálogo y de suministrarle los datos o callbacks necesarios, evitando contener internamente todo el JSX del modal.
- El componente del diálogo debe encapsular su estructura visual y la lógica de interacción que le pertenezca.
- No mantener grandes bloques de JSX de `DialogContent`, formularios completos o confirmaciones dentro del fichero de una página o listado cuando puedan identificarse como una unidad funcional independiente.
- Los formularios que se muestran dentro de un diálogo pueden separarse además en un componente propio cuando tengan suficiente entidad, reutilización o complejidad.
- No extraer a otro fichero un `Dialog` trivial cuyo único contenido sea un mensaje mínimo sin lógica, estado, formulario ni responsabilidad propia, salvo que forme parte de un patrón reutilizado.
- Antes de crear un diálogo nuevo, comprobar si ya existe un componente equivalente o un patrón común reutilizable en el proyecto.
- En las pruebas funcionales, de integración de listados y auditorías visuales, utilizar un conjunto amplio de datos sintéticos: al menos 15 compañías y una compañía principal de pruebas con al menos 50 personas, 50 activos y 50 recursos. Extender este volumen a puestos de trabajo, perfiles de usuario y otras entidades relevantes para el flujo, respetando sus restricciones de dominio.
- Mantener relaciones coherentes y variedad representativa: nombres largos y con tildes, distintos estados, plantillas, sedes y asociaciones. Aprovechar el volumen para comprobar búsqueda, filtros, ordenación, paginación, selección y conservación del contexto; utilizar las demás compañías para verificar el aislamiento de sus datos.
- Preparar estos datos mediante procedimientos reproducibles en un entorno aislado y desechable, exclusivamente sobre `metis_desk_test`, siguiendo el procedimiento de Playwright cuando corresponda. No poblar ni modificar bases de datos reales para cumplir esta regla.
- Conservar las pruebas unitarias pequeñas y los escenarios específicos de vacío, uno y pocos registros como complemento. Informar del volumen realmente utilizado y de cualquier escenario de volumen pendiente; no declarar comprobaciones con datos abundantes si no se han ejecutado.
1. Buscar implementaciones equivalentes dentro del repositorio.
2. Consultar la documentación normativa del proyecto aplicable.
3. Identificar qué capa es responsable del comportamiento.
4. Comprobar si ya existe un componente, hook, schema, utilidad, Value Object o servicio reutilizable.
5. Elegir la solución más pequeña que respete los contratos existentes.
6. Evitar introducir una abstracción nueva si un patrón existente resuelve adecuadamente el problema.
Durante la implementación:
- No ampliar el alcance para corregir cuestiones no relacionadas.
- Si se detecta una mejora ajena a la tarea, mencionarla al finalizar en lugar de modificarla automáticamente.
- No alterar APIs públicas, contratos HTTP, schemas, migraciones o comportamiento compartido como efecto secundario accidental.
- No crear compatibilidad hacia atrás que nadie haya solicitado cuando se pueda actualizar de forma controlada a todos los consumidores.
- No dejar código muerto, implementaciones alternativas comentadas ni TODO vagos.
Al finalizar:
- Revisar el diff completo, no únicamente los ficheros editados individualmente.
- Comprobar que no se hayan introducido duplicaciones, componentes excesivamente grandes, varios componentes relevantes por fichero, imports no usados, casts innecesarios ni efectos React evitables.
## Auditorías visuales y pruebas interactivas con Playwright MCP
- Usar el servidor `playwright` de `.codex/config.toml` para inspeccionar interfaces, comprobar interacciones y generar capturas cuando el entorno lo permita. Sigue el [procedimiento de Playwright MCP](docs/playwright-mcp.md).
- En un WSL nuevo, ejecutar `bash scripts/setup-playwright-wsl.sh` antes de usar el MCP. Es idempotente: reutiliza la imagen fijada y comprueba el navegador y la conectividad al host sin instalar Chromium en WSL.
- Comprobar `/mcp` en la sesión activa: que el CLI liste la configuración no garantiza que las herramientas estén disponibles en una sesión ya abierta. Si el cliente no carga la entrada de proyecto, usar explícitamente `bash scripts/codex-playwright-wsl.sh` desde WSL, siguiendo las limitaciones de cliente documentadas; no cambiar ni duplicar la configuración global automáticamente.
- El servidor utiliza Docker, stdio y la imagen oficial fijada por digest. No instalar Chromium en WSL, publicar puertos MCP ni crear una configuración global duplicada.
- Confirmar la URL anunciada por Vite y el puerto real antes de navegar. Con la red host configurada, utilizar el mismo origen local que Metis; no sustituirlo por el puerto de la API ni por `host.docker.internal` sin comprobar Vite y APP_ORIGIN.
- Para comprobaciones reproducibles usar una API y datos de prueba; toda prueba con persistencia utiliza exclusivamente `metis_desk_test`. No crear usuarios, guardar formularios ni modificar datos reales como parte de una auditoría visual sin autorización específica.
- Para inspecciones autenticadas, utilizar `python3 scripts/visual-audit.py`: prepara una instalación desechable en otro contenedor, accede por el formulario real con un usuario ordinario y proporciona una sesión privada para el lanzador de Codex. No ejecutar las fixtures destructivas de integración para preparar la auditoría ni conectar este procedimiento a una base existente. El administrador de preparación solo existe durante la vida del contenedor temporal.
- Mantener el proceso de auditoría abierto mientras Codex utiliza su sesión. Pasar `METIS_PLAYWRIGHT_STORAGE_STATE` únicamente al lanzador documentado; no copiar cookies, contraseñas ni `storageState` a mensajes, configuración global, código o Git. Al terminar, cerrar el proceso para retirar los servicios temporales y borrar la sesión. `--check` verifica el recorrido y limpia automáticamente.
- Comprobar teclado, foco, estados de carga/error/vacío, tamaños estrechos y amplios y contenido extenso según el cambio. Guardar evidencia local en `.artifacts/playwright/` y mantenerla fuera de Git; no guardar credenciales ni estados de sesión en el repositorio.
- Abrir e inspeccionar las capturas antes de afirmar una revisión visual. Una imagen PNG válida, un snapshot accesible y una prueba funcional son evidencias distintas; indicar qué se ha ejecutado realmente.
- Si Docker, la aplicación o el MCP no están disponibles, diagnosticar el fallo y comunicar los escenarios pendientes. No presentar como superadas comprobaciones que no se han ejecutado.