106 lines
13 KiB
Markdown
106 lines
13 KiB
Markdown
# Viabilidad técnica y validación
|
||
|
||
Fecha: 22 de octubre de 2026. Investigación estática, sin modificaciones de implementación. Referencias E: [inventario](01-inventario-funcional.md).
|
||
|
||
## Consultas reutilizables
|
||
|
||
| Fuente / endpoint GET | Cliente y claves existentes | Coste y uso propuesto |
|
||
| --- | --- | --- |
|
||
| `/api/companies/:companyId` | Compañía ya recibida por la ficha; `companyKeys.detail` | Personas, sedes, departamentos y asignaciones; evitar segunda copia de estado |
|
||
| `/api/assets/:companyId` | `inventoryApi.get`, `inventoryKeys.company` de Assets | Toda la compañía, activos/puestos/relaciones/historiales; fuente actual del puesto |
|
||
| `/api/asset-catalog/:scope` | `catalogApi`, `catalogKeys.scope` de Assets | Completa plantillas; usar specification del activo cuando proceda |
|
||
| `/api/work-profiles/:companyId` | `profileApi.get`, `profileKeys.company` | Todos los perfiles con revisiones; filtrar por host y compañía |
|
||
| `/api/software-installations/:companyId` | `softwareApi.get`, `softwareKeys.company` | Todas las instalaciones; filtrar por host, no pedir por fila |
|
||
| `/api/resources/:companyId` | `inventoryApi.get`, `inventoryKeys.company` de Resources | Recursos y proyecciones, datos para software/configuraciones |
|
||
| `/api/access/:scope` | `getAccess`, `accessKeys.scope` | Catálogo y accesos; cobertura/reviews. No hay filtro de puesto |
|
||
| `/api/folder-mappings/:companyId` | `folderApi`, `folderKeys.company` | Mapeos con cobertura de cuenta y revisión |
|
||
| `/api/mail-configurations/:companyId` | `mailApi`, `mailKeys.company` | Configuraciones nuevas y snapshots legacy; no unir las legacy por `profileId` inexistente |
|
||
| `/api/mailboxes/:companyId` | Contratos `MailboxesResponseSchema`; E36 | Buzones y usos temporales; validar procedencia de acceso de persona |
|
||
| `/api/application-configurations/:companyId` | `applicationApi`, `applicationKeys.company` | Aplicaciones de perfil, distintas de instalación en host |
|
||
| `/api/certificate-configurations/:companyId` | `certificateApi`, `certificateKeys.company` | Configuración, caducidad/revisión del certificado |
|
||
| `/api/printer-configurations/:companyId` | `printerApi`, `printerKeys.company` | Perfil e impresora; no convertirla en miembro exclusivo |
|
||
| `/api/telework-authorizations/:companyId` | `teleworkApi`, `teleworkKeys.company` | Contexto de persona y efectividad calculada por servidor |
|
||
| `/api/remote-connections/:companyId` | `remoteApi`, `remoteKeys.company` | Conexiones de personas; filtrar explícitamente por origen/destino/persona |
|
||
| `/api/asset-support/:companyId/:assetId` | Cliente de soporte y `supportKeys` de Assets | Consulta por equipo; evitar cargar cada miembro para un resumen inicial |
|
||
| `/api/asset-infrastructure/:companyId` | `InfrastructureResponseSchema`; E35 | Roles, interfaces y direcciones registradas; filtrar por `assetId` |
|
||
| `/api/local-identity-environments/:companyId` | Contratos `LocalIdentityQuerySchema` | Filtros por activo/entorno, paginación `nextAfterId`; 100 máximo por página |
|
||
| `/api/asset-directory-memberships/:companyId` y `/:assetId/current` | Contratos `DirectoryMembershipQuerySchema` | Paginación y pertenencia actual; contexto de directorio del host |
|
||
| `/api/asset-network-share-publications/:companyId` | Contratos de publicaciones E20 | `assetId`, `resourceId`, `includeEnded`, cursor, pageSize <=100; no total global en respuesta |
|
||
| `/api/processes/:companyId` | Process API | Procesos de personas; opcional, sin referencia directa al puesto |
|
||
|
||
Las rutas se contrastan con registradores HTTP; los identificadores de catálogos se verifican en el cliente instalado. Los GET de configuraciones citados rechazan query desconocida; no añadir `workstationId` o `profileId` a HTTP por imitar el filtro de la UI. Las rutas de navegador contextuales de perfil no implican filtros backend equivalentes.
|
||
|
||
## Reutilización y límites
|
||
|
||
**Reutilización directa**: datos básicos del puesto, cabecera/acciones, historial, enlaces, diálogos y panel de software.
|
||
|
||
**Adaptación menor**: resumen de equipo, contadores definidos, tabla de equipamiento compacta, cuenta en tabla de perfiles, vista de última revisión.
|
||
|
||
**Desarrollo nuevo de composición**: filtros de configuraciones por perfiles del host, procedencia visible, estados parciales, resúmenes y enlaces contextuales de remoto/soporte/directorio. Los datos existen; el agregado de pantalla no está implementado.
|
||
|
||
**Funcionalidad no soportada**: tickets activos, telemetría, presencia actual, puntuación de salud y comprobación real de permisos SMB. No desbloquearlas con datos inventados en el fixture.
|
||
|
||
El dashboard inicial puede construirse con consultas existentes. No hay necesidad demostrada de nuevo endpoint. Si una medición posterior detecta payload excesivo, latencia o incoherencia entre snapshots de consultas independientes, justificar una lectura agregada/paginada antes de diseñarla. Sería un contrato nuevo con autorización y pruebas propias, fuera de esta auditoría.
|
||
|
||
## Composición y dependencias
|
||
|
||
La ficha vive en `modules/companies/src/web/workstations/detail/`; el agregado y comandos, en Assets. `useEnvironmentQueries` y `buildEnvironment` de Companies ya reúnen el contexto de perfil (E30). El primero solicita once fuentes y condiciona su `data` a que todas existan: no copiarlo sin revisar la recuperación parcial. El segundo es cálculo puro y conserva `profileId`/compañía y estados abiertos. E37 muestra el registro real de módulos en `identity-runtime.ts`, llamado desde el arranque; no todas las rutas se registran directamente en `index.ts`.
|
||
|
||
Reutilizar las APIs públicas de módulos; no importar repositorios ni modelos ORM en frontend. Componentes genéricos de `@repo/rdx-ui` deben recibir datos semánticos y callbacks, sin DTO específico de puesto. No modificar vendor ni extraer una plataforma genérica de widgets sin consumidores reales.
|
||
|
||
Las composition roots son explícitas. No se confirmó un sistema dinámico de plugins que deba adaptarse para esta pantalla. Sí hay proveedores/contratos tipados de relaciones (`@metis/relationship-contracts`) y servicios entre módulos: las publicaciones SMB y directorio se componen en sus propietarios. No introducir acceso cruzado a tablas como supuesto «plugin de dashboard».
|
||
|
||
## Rendimiento y coherencia
|
||
|
||
| Riesgo | Evidencia / alcance | Decisión recomendada |
|
||
| --- | --- | --- |
|
||
| Payload por compañía | Inventario/perfiles/configuraciones incluyen revisiones; no GET individual de puesto localizado | Usar caché existente y carga por necesidad; medir antes de nuevo endpoint |
|
||
| Multiplicar queries por perfil | E30 hace 11 consultas por compañía | Compartir claves y cargar cada familia una vez; filtrar después. No montar un hook agregador por fila |
|
||
| N+1 en soporte/publicaciones | APIs admiten consulta por activo; puesto hasta 200 miembros | Consultar principal primero; detalle del resto bajo demanda. No fan-out masivo inicial |
|
||
| Coste de filtrado local | E11 hace `find` de activos/catálogos por miembro; controller filtra perfiles | Mapas/sets cuando haya recorrido repetido relevante; no `useMemo` preventivo sin necesidad |
|
||
| Conteo de página incompleta | Publicaciones y entornos locales paginados | Seguir cursor para total o etiquetar parcial; no confundir recuento cargado con total |
|
||
| Lecturas no atómicas entre APIs | Queries independientes y momentos distintos | Datos parciales y refetch dirigidos; no prometer fotografía transaccional global |
|
||
| Cambio de host | Las relaciones derivan de `computerAssetId` actual | Recalcular al cambiar dato de query; no copiar relaciones en estado ni sincronizar con efectos |
|
||
| Fallo secundario bloquea todo | E30 retorna `data: undefined` si falta una fuente | Estado por bloque; conservar datos previos al actualizar y reintento específico |
|
||
|
||
No se midieron latencias, volumen de respuesta ni planes SQL. El lector de puestos usa consultas en lote de raíces, revisiones y miembros; no hay base para afirmar un N+1 de BD por puesto en ese lector (E07). Hay recorridos locales repetidos y varios loaders de contexto; distinguir riesgo de coste real medido.
|
||
|
||
## Mutaciones e invalidación
|
||
|
||
- Guardar el puesto invalida `inventoryKeys.company(companyId)` en su controller; el host y relaciones derivadas se recalculan al refrescar.
|
||
- Renombrar relee inventario para conservar composición vigente y evita enviar valores capturados al comenzar edición; no se encontró control optimista de revisión en el contrato de puesto. No prometer resolución completa de concurrencia.
|
||
- Ciclo de vida invalida inventario; ficha solicita refetch de perfiles tras desmantelar. Esto no desactiva perfiles en backend.
|
||
- Software, perfiles y configuraciones conservan sus comandos y claves propias. La integración futura debe revisar cada consumidor antes de introducir contadores derivados; no invalidar toda la caché.
|
||
- No descartar promesas con `void` ni ocultar errores en lógica nueva. Los `.catch(() => undefined)` existentes de reintento no se corrigen durante esta documentación; TanStack Query expone el error de consulta, pero no debe suponerse que todos los callbacks futuros harán lo mismo.
|
||
|
||
## Autenticación, ámbito y permisos
|
||
|
||
**CONFIRMADO** E33: `requireAuthenticated` valida sesión y versión de autenticación; los routers de puesto y consultas relacionadas reciben ese middleware. El `actorId` se obtiene de sesión y los cuerpos/params se validan con Zod. La pertenencia de persona, sede y miembros se valida con contextos por compañía y restricciones relacionales; las escrituras de inventario usan transacciones y bloqueo de compañía.
|
||
|
||
No se localizó una ACL usuario–compañía ni un rol específico por operación de puesto en el recorrido de rutas, use cases y repositorio. El middleware autenticado no recibe `companyId` ni consulta membresías del operador. En relaciones tipadas, `SessionRelationshipReadAccess` verifica sesión y existencia de compañía, y utiliza contextos emitidos por servidor; no acredita una ACL por compañía (E34).
|
||
|
||
Por tanto, aislamiento de datos por IDs y pertenencia de entidades está confirmado en las rutas inspeccionadas; autorización granular del operador por compañía **NO ESTÁ CONFIRMADA**. No inferirla del filtrado frontend ni simular distintos roles como si ya existieran. El dashboard debe reutilizar el contrato efectivo y cualquier nueva lectura debe conservar comprobaciones en servidor. No exponer accesos de compañías distintas ni considerar la compañía atendida como tenant de la empresa de soporte.
|
||
|
||
No se inspeccionaron credenciales ni se generaron conexiones remotas reales. No incluir tokens, contraseñas o destinos sensibles en un mockup.
|
||
|
||
## Registro de investigación y validación
|
||
|
||
- Ejecutados desde WSL/bash: `pwd`, `git status --short`, `node --version`, `pnpm --version`; versiones correctas. El estado inicial contiene cambios previos ajenos a esta tarea.
|
||
- Inspección con `rg`, `rg --files`, lecturas de archivos y manifests/skills. Se revisaron fuentes activas, contratos, tests existentes, DDL y documentación; no se verificó una BD desplegada.
|
||
- MCP: `browser_tabs` respondió `about:blank`. Curl a `http://localhost:5173/` falló; `ss -ltnp` no pudo usar netlink. No se confirmó URL Vite ni sesión sintética. No se inició servidor, no se ejecutaron migraciones ni se sembraron datos.
|
||
- JSON: 16 bloques de `sources` validados contra sus schemas Zod existentes. Comprobados miembros, referencias, reserva exclusiva, unicidad de perfiles activos, coherencia de historial y contadores con `workstationRelations`/`buildEnvironment`. `projectAccess` reproduce exactamente las coberturas y revisiones del ejemplo. Son comprobaciones locales, no guardados de dominio ni consultas a la BD.
|
||
- Pruebas existentes dirigidas: los **tres archivos** finalizaron correctamente, sin fallos. Comando reproducible desde raíz:
|
||
|
||
```bash
|
||
TSX_TSCONFIG_PATH=tsconfig.json node --import tsx --test \
|
||
modules/assets/src/api/domain/workstation.test.ts \
|
||
modules/companies/src/web/workstations/detail/helpers/workstation-relations.test.ts \
|
||
modules/companies/src/web/workstations/detail/ui/workstation-detail.test.tsx
|
||
```
|
||
|
||
- Una comprobación auxiliar inicial de Access falló al importar `@repo/rdx-ddd` desde el eval de la raíz, donde no es dependencia directa. Se corrigió la comprobación usando la ruta local del VO, sin modificar configuración; la ejecución final fue satisfactoria. No es un fallo del producto ni se ocultó alterando sus tipos.
|
||
- Se comprobaron 108 enlaces locales y todas las rutas completas de código citadas entre backticks; sin destinos inexistentes. El catálogo contiene 37 grupos y 89 referencias a archivos/líneas verificadas. Se revisó el contenido completo de los ocho entregables y se comprobó el diff de cada fichero nuevo con `git diff --no-index --check`: sin diagnósticos de whitespace. En modo `--no-index`, el código 1 por archivo nuevo no se interpreta como un error de formato.
|
||
- El fixture contiene **1 compañía, 2 personas, 5 activos, 7 recursos, 4 puestos y 4 perfiles**. No se cargaron en persistencia. Las comprobaciones locales no cumplen ni sustituyen el volumen funcional/visual exigido por AGENTS.md.
|
||
|
||
No se ejecutan `pnpm typecheck`, `pnpm lint`, `pnpm test`, `pnpm check` ni `pnpm build` globales: los únicos cambios son Markdown y un fixture de diseño. Las suites de integración quedan pendientes por su persistencia y por la prohibición de migraciones/datos. No declarar una auditoría visual, de rendimiento o de accesibilidad como superada.
|