# Instrucciones de Metis Desk ## Alcance y entorno Proyecto independiente de factuges-erp. Se conserva su estilo, no sus funcionalidades ni dependencias locales. El repositorio vive en `/home/david/dev/metis-desk`, en WSL2 Ubuntu-24.04. Ejecutar las herramientas del proyecto dentro de WSL con bash, nunca desde Windows, rutas UNC o /mnt/c. Antes de modificar código: comprobar pwd, git status --short, node --version y pnpm --version. Usar Node.js 24.x y la versión de PNPM fijada en package.json. No imprimir, copiar ni versionar .env locales o secretos. `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. ## Convenciones 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). - Código e identificadores en inglés; comentarios, TSDoc y documentación en castellano. - Ficheros y directorios en kebab-case. Clases, tipos e interfaces en PascalCase. - Variables, funciones y métodos en camelCase; constantes globales en UPPER_SNAKE_CASE. - Interfaces con prefijo I cuando el área ya siga esa convención. - Documentación en /docs, con nombres de ficheros en castellano. - Comentarios que expliquen decisiones, riesgos o contratos, nunca que repitan el código. - Mantener es y en para textos de interfaz; no insertar literales en áreas con i18n. - Utilizar siempre llaves en if, else, for, while y estructuras equivalentes, incluso cuando el bloque contenga una sola instrucción. ## Disciplina Aplicar SOLID, Clean Code, DRY, KISS, YAGNI, SSOT, fail fast y programación defensiva. Priorizar claridad y explicitud frente a abstracción prematura. Cambios incrementales y acotados. No renombrar, reformatear o mover código ajeno a la tarea. No crear capas, factories, genéricos, eventos, contenedores DI ni repositorios base sin necesidad real. Justificar cada nueva dependencia. Preferir APIs nativas cuando basten. No añadir supresiones de TypeScript o Biome sin justificación. No eliminar legado sin buscar y clasificar sus referencias en todo el monorepo. ## TypeScript y asincronía - Importar tipos explícitamente con import type en la cabecera. - No usar import("...").Tipo en código ordinario. - Evitar any, assertions y non-null assertions salvo garantías verificables. - Inferir DTOs de schemas Zod; no duplicarlos mediante interfaces. - Usar unions para conjuntos cerrados y mantener switches exhaustivos. - No usar Partial si el contrato ya representa la opcionalidad. - No usar el operador void para descartar promesas. - Toda promesa se espera, se devuelve o gestiona su error expresamente. - No dejar promesas flotantes ni capturas que oculten fallos. - Comprobar el contrato del callback antes de convertirlo a async. - Usar Result para fallos de negocio y Maybe para ausencia semántica cuando se implementen esas operaciones. - Collection solo cuando el conjunto necesite comportamiento de dominio; no introducir estos patrones sin consumidores. ## Arquitectura y contratos Las fuentes normativas se consultan, no se duplican: - [Capas del servidor](docs/arquitectura/capas-del-servidor.md) - [Composición de módulos](docs/arquitectura/composicion-de-modulos.md) - [Flujo HTTP](docs/arquitectura/flujo-http.md) - [Contratos HTTP](docs/arquitectura/contratos-http.md) - [Pruebas](docs/arquitectura/pruebas.md) - [Modelo DDD e identificadores](docs/arquitectura/modelo-ddd-e-identificadores.md) - [Estructura web por entidades y operaciones](docs/estructura-web-por-entidades-y-operaciones.md) ## Seguridad y persistencia - 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. ## Frontend 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//src/web///`, 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 compartidos viven en @repo/shadcn-ui. Incorporar TanStack Table y React Hook Form cuando haya consumidores. Estado remoto en TanStack Query; claves centralizadas e invalidación explícita después de mutaciones. No duplicar estado remoto ni usar efectos para derivar valores calculables durante render. Mantener accesibilidad y estados de carga, error, vacío y acciones deshabilitadas. Separar componentes por responsabilidades reales, sin fragmentar JSX trivial. 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/shadcn-ui. 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. ## Validación y entrega Ejecutar pnpm typecheck, pnpm lint, pnpm test, pnpm check y pnpm build en cambios transversales. Preferir comprobaciones dirigidas al paquete cuando sean suficientes. Añadir pruebas de comportamiento observable: válidos, inválidos, límites e idempotencia cuando corresponda. Tests deterministas, sin esperas arbitrarias ni desactivaciones. Informar cambios, archivos principales, comandos ejecutados, resultados y pendientes reales. No afirmar validaciones no ejecutadas ni ocultar fallos conocidos. Precedencia: instrucciones actuales del usuario, AGENTS.md más cercano, este archivo y convenciones existentes. ## Buenas prácticas de implementación 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. #### Componentes visuales - 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. #### Diálogos, modales y ventanas emergentes - 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. ### Formularios - Mantener una única fuente de verdad para cada campo. - Utilizar React Hook Form cuando el formulario tenga complejidad suficiente y siguiendo los patrones existentes del repositorio. - Utilizar schemas Zod como fuente normativa de validación cuando corresponda. - No duplicar manualmente reglas de validación entre schema, formulario y llamada HTTP. - Mostrar errores junto al contexto que los origina. - Diferenciar errores de campo, errores globales del formulario y errores inesperados del servidor. - Evitar envíos duplicados y reflejar visualmente estados pendientes. - No limpiar o cerrar un formulario hasta confirmar que la operación que lo requiere se ha completado correctamente. ### Acceso a datos en frontend - Centralizar query keys y mantenerlas estables. - Las funciones de acceso a API no deben depender de componentes React. - No realizar `fetch` directamente dentro de componentes si el módulo dispone de una capa de API o query establecida. - No utilizar `useEffect` como sustituto de TanStack Query para cargar datos remotos. - Después de una mutación, actualizar o invalidar únicamente las queries afectadas con una estrategia explícita. - No invalidar indiscriminadamente toda la caché para evitar razonar sobre dependencias. - Gestionar explícitamente estados de carga inicial, actualización, error, ausencia de datos y permisos insuficientes. ### Estructura de ficheros - Cada fichero debe tener una responsabilidad reconocible. - Evitar ficheros genéricos como `utils.ts`, `helpers.ts`, `common.ts`, `types.ts` o `constants.ts` convertidos en cajones de sastre. - Ubicar utilidades, tipos y constantes junto al dominio o funcionalidad a la que pertenecen. - Extraerlos a una ubicación compartida únicamente cuando existan consumidores reales en diferentes áreas. - No crear ficheros `index.ts` o barrels indiscriminadamente. - Evitar barrels que oculten dependencias, provoquen ciclos de importación o dificulten localizar el origen de un símbolo. - No crear directorios con un único fichero sin que aporten una frontera conceptual útil. - No reorganizar carpetas solamente por preferencia personal durante una tarea no relacionada. ### Funciones y responsabilidades - Preferir funciones pequeñas con una responsabilidad definida, pero no fragmentar una operación sencilla en multitud de funciones triviales. - Los nombres deben describir intención, no implementación accidental. - Una función que realiza una consulta no debe modificar estado salvo que su contrato lo indique claramente. - Separar cálculo puro de I/O cuando esa separación mejore pruebas y razonamiento. - Reducir anidamiento mediante retornos tempranos cuando mejoren la legibilidad. - No utilizar retornos tempranos que oculten invariantes o hagan difícil seguir el flujo. - No crear parámetros, opciones o extensibilidad que ningún consumidor necesite actualmente. ### Dependencias - Antes de añadir una dependencia, comprobar si el proyecto o la plataforma ya resuelven el problema. - No instalar una biblioteca por una única función trivial que pueda implementarse claramente con APIs estándar. - No sustituir una dependencia ya establecida por otra equivalente sin una razón explícita. - Comprobar compatibilidad con las versiones reales de Node.js, React, TypeScript y demás dependencias del repositorio. - No asumir APIs de una versión distinta a la instalada. ### Errores y observabilidad - Los errores deben conservar causa y contexto suficiente para poder diagnosticarlos. - No registrar el mismo error repetidamente en todas las capas. - Registrar el error en la capa responsable de observarlo o resolverlo. - No registrar tokens, contraseñas, cookies, cuerpos sensibles ni credenciales. - No utilizar `console.log` como mecanismo permanente de observabilidad cuando exista infraestructura de logging. - Los mensajes de error internos deben ser útiles para diagnóstico; los destinados al usuario no deben revelar detalles internos. ### Pruebas - Probar comportamiento observable y contratos, no detalles internos de implementación. - No escribir tests cuyo único objetivo sea aumentar cobertura. - Evitar mocks cuando pueda utilizarse una dependencia real rápida y determinista o un fake sencillo. - Mockear fronteras externas, no cada función interna. - Mantener los tests legibles y con preparación mínima. - Cada corrección de un bug reproducible debe considerar una prueba que falle antes de la corrección y pase después. - Para lógica con estados cerrados, cubrir los casos relevantes y los límites, no solamente el camino feliz. ### Criterio antes de escribir código Antes de implementar: 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. - Ejecutar las validaciones apropiadas para el alcance real del cambio.