# Decisión aprobada — SO actual en activos y puestos Fecha: 25 de octubre de 2026. Decisión funcional aprobada por el usuario para esta intervención. Complementa la [auditoría arquitectónica](auditoria-sistemas-operativos-activos-y-puestos.md), cuyos hallazgos originales se conservan. ## Decisión y alcance El SO de fábrica permanece en las características de Assets. El producto SO sigue siendo un recurso particular `os` de Resources; su instalación y revisiones pertenecen a `SoftwareInstallation` de Companies. La ficha de activo y la del puesto muestran una proyección compartida de esas instalaciones. No se añaden persistencia, entidades, migraciones, estados, derechos de licencia, requisitos de perfil ni catálogo global de SO. La selección exige simultáneamente compañía y activo correctos, estado `configured` y referencia pública resuelta de tipo `os`. Se excluyen `pending`, `removal_pending`, `removed` y `never_configured`. Un producto inactivo que el contrato permite resolver conserva su información y el aviso de revisión del registro; no se relajan permisos ni pertenencia. Se admiten todos los productos configurados diferentes que permite la unicidad existente por pareja activo/recurso. El orden es por nombre con locale explícito `en` y UUID como desempate. Se muestra la versión de instalación salvo cuando el texto completo ya figura en el nombre, delimitado por espacios o signos y comparado sin diferencias de mayúsculas, espacios o representación Unicode. No se confunde `1` con `11` ni `24.04` con `24X04`. La comparación afecta solo a la presentación: no cambia nombres, ediciones ni versiones guardadas y no incorpora reglas por fabricante. ## Presentación - Activo: tarjeta **Sistema operativo actual** después de sus características técnicas, antes de notas e información adicional. Los campos originales de identificación y características conservan su orden. Companies aporta la tarjeta al slot `technicalSummary` de la API web pública de Assets, que no importa Companies. - Puesto: la misma tarjeta antes del bloque de gestión de software, calculada a partir del principal validado `computerAssetId`. Cambiarlo selecciona otras instalaciones sin mover registros, licencias o historial. - Varios productos: título plural y lista completa con enlaces; una o dos columnas según capacidad del contenedor. No se reduce a una etiqueta que oculte productos. - Sin SO configurado: **No registrado / Not recorded**. No significa ausencia física de SO. Los campos `factory_os` y `factory_preloaded_os` no participan en esta lectura. - Carga inicial o resolución necesaria pendiente: skeleton con estado accesible. Error de instalaciones o de resolución necesaria: mensaje con Reintentar, incluso si hay caché previa; no se presenta como vacío ni como dato vigente confirmado. - Referencia configurada no resoluble o sin nombre utilizable: aviso de referencia de software no disponible, conservando los SO que sí se resuelven. Sin metadatos no se puede saber si la referencia desconocida era SO o aplicación: se evita afirmar «No registrado» en ese caso. El criterio es independiente de la plantilla del activo. Funciona en portátil, sobremesa, servidor sin puesto y plantilla propia; cualquier otro activo puede mostrar un SO si existe una instalación explícita `os`. No se infiere SO a partir del firmware, el nombre o la categoría. Para activos sin registros se aplica el mismo estado «No registrado», sin una nueva taxonomía de elegibilidad. La gestión conserva el panel y formulario de instalaciones de la ficha del activo y el diálogo existente del puesto, incluyendo estados, versión, licencia e historial. No se incorpora otro formulario. El mapa sigue mostrando software abierto con su estado, incluidas configuraciones pendientes: **no es una lista de SO actuales**. Comparte la selección de ámbito/activo y conserva una tarjeta por instalación; la lista completa de SO actuales permanece en el resumen. ## Código y fronteras | Área | Archivos y responsabilidad | | --- | --- | | Selección pura | `modules/companies/src/web/software-installations/shared/helpers/current-operating-systems.ts`: ámbito, activo, estados, tipo, referencia no disponible, orden y versión visible. | | Lectura compartida | `software-installations/shared/queries/use-asset-software.ts`: `softwareKeys.company`, `softwareApi.get` y `useSoftwareReferences` público de Resources. No copia estado remoto ni usa efectos de sincronización. | | Presentación | `software-installations/detail/ui/current-operating-systems.tsx`: tarjeta común con DetailPanel, Skeleton, Button y BrowserLink ya instalados; traducciones es/en y tokens semánticos. | | Integración de activo | `assets/detail/ui/asset-operating-systems.tsx` y `companies/detail/ui/company-inventory.tsx` en Companies; slot ReactNode de `AssetDetailPage`/`AssetDetailContent`/`AssetDetail` en Assets. | | Integración de puesto | `use-workstation-software.ts`, `workstation-detail-content.tsx` y `use-workplace-map-model.ts`; el mapa resuelve los nombres con las mismas referencias públicas y reutiliza `getAssetSoftwareInstallations` en `workplace-map-model.ts`. | | Pruebas | `current-operating-systems.test.tsx`, regresiones de ficha de activo/puesto y `scripts/current-operating-systems-ui.test.py`. La suite nueva se añade al comando de Companies. | Las rutas de servidor, DTO HTTP, dominio y repositorios permanecen sin cambios de esta intervención. Se consume el resolver por propósito `software-installation`, que admite SO particulares y aplicaciones según sus garantías vigentes. El árbol ya contenía trabajo concurrente en Resources y continuó cambiando durante la tarea; esos cambios no se atribuyen a esta decisión ni se revierten. La caché se comparte con la gestión existente. Guardar una instalación invalida la query de compañía y las referencias por propósito mediante el formulario existente; ambos resúmenes se recalculan durante render. El cambio de principal filtra inmediatamente por el nuevo UUID y resuelve sus productos. Los identificadores de instalación, licencia y revisiones no cambian por ese cambio de contexto. ## Validación técnica | Comprobación ejecutada | Resultado | | --- | --- | | Selección dirigida con `node --import tsx --test --test-isolation=none` | 75/75: nueva suite SO, panel/formulario software, ficha de puesto, mapa/layout, ficha de activo y dominio SoftwareInstallation. | | `pnpm --filter @metis/assets test` | 27 archivos correctos; el runner agrupa por archivo. | | `pnpm --filter @metis/companies test` | 49/50 archivos correctos. El archivo `companies-page.test.tsx` falla en expectativas de contador de personas y ayuda de formulario de persona, fuera de este cambio. Su ejecución individual confirma esas dos expectativas; no se modifican para ocultarlas. | | `python3 scripts/resource-scope-test.py --software --regressions` | 13/13 de software HTTP/MariaDB y 50/50 regresiones; contenedor propio `metis_desk_test` retirado. | | Typechecks dirigidos de Assets, Companies y Web | Correctos. | | Biome y ESLint dirigidos a archivos propios | Correctos. | | `pnpm build` | Correcto; conserva aviso de bundle web mayor de 500 kB. | | `pnpm typecheck` global | Falla en vendor `packages/shadcn-ui/src/components/calendar.tsx:165`, TS2375 por `locale`; vendor sin cambios propios. | | `pnpm lint` y `pnpm check` globales | Mantienen diagnósticos fuera de los archivos propios, incluidas convenciones/skills y código previo/concurrente. No se declara el árbol global validado. | No se ejecuta un `pnpm test` global que pudiera consumir configuración de BD habitual. La integración SQL usa el runner desechable; las suites web/dominio dirigidas no usan BD. Los resultados históricos de la auditoría anterior no se reutilizan como pruebas nuevas. ## Evidencia interactiva y reproducción Entorno preparado con `bash scripts/setup-playwright-wsl.sh` y `python3 scripts/visual-audit.py`, siguiendo [Playwright MCP](../playwright-mcp.md). Se usa el servidor MCP Docker fijado en `.codex/config.toml` y el cliente stdio existente; no se instala Chromium ni se crea configuración global. Origen Vite temporal verificado: `http://localhost:36037`. Se inicia sesión por el formulario real como operador ordinario y solo se persiste sobre la BD desechable `metis_desk_test`. La preparación reproducible utiliza `visual-audit-volume.py`, `workstation-dashboard-fixtures.py` y después `current-operating-systems-ui.test.py`, pasando `METIS_PLAYWRIGHT_STORAGE_STATE` únicamente como indica el procedimiento. Esta última comprueba ruta privada y origen del preparador, conserva fixtures solo dentro de ese entorno y no permite elegir una BD existente. Las credenciales/cookies no se incluyen en documentación, capturas ni Git. Volumen preparado: 15 compañías, 59 personas, al menos 143 activos y 76 recursos en la principal; puesto de densidad con 80 miembros y 60 perfiles. Los casos adicionales de SO incluyen portátil con FreeDOS original, sobremesa Windows, servidor sin puesto, plantilla de activo propia, activo sin SO de fábrica, fábrica sin instalación actual, pendiente, retirada, dos SO, aplicación convencional y un segundo ordenador. La regresión MCP del mapa (`scripts/workplace-visual-map.test.py`) pasa **7 recorridos**, incluyendo seis anchos, densidad, navegación/gestión, errores, principal, refresco, contraste y movimiento reducido. Sus evidencias se conservan bajo el directorio de la ejecución desechable; no se confunde su software abierto con el resumen nuevo. Tras compartir también los nombres del resolver, se repitieron estados parciales y refresco; el último caso pasa con la fixture coherente actualizada. El recorrido nuevo completo pasa **7/7 pruebas MCP** (`ui-final.log`), con las siguientes evidencias: | Comprobación | Evidencia observada | | --- | --- | | OSUI-01–07, 12–14, 17–18 | Origen FreeDOS/Windows independiente, configurado frente a pendiente/retirado, dos SO sin aplicaciones, servidor sin puesto y plantilla propia, leídos por API real y comprobados en la ficha. | | OSUI-08 | Sustitución del principal: el resumen pasa al otro SO y las instalaciones/licencias/revisiones antes y después son idénticas por HTTP. | | OSUI-09/15 | Guardado en el formulario existente: aparece `Versión 25H2` en el resumen sin recargar. | | OSUI-10/11 | Respuesta de referencia `unavailable` y HTTP 500 interceptados por MCP: aviso/error con reintento, sin «No registrado»; al retirar la interceptación se recupera. | | Responsive y temas | Activo y puesto: 320, 390, 768, 1024, 1280 y 1600 px, en claro y oscuro; 24 combinaciones con dos productos y nombres largos, sin overflow de la tarjeta. | | Contraste local | Mínimo medido de texto/enlaces: 4,648:1 en claro y 4,749:1 en oscuro, sobre fondos efectivos de la tarjeta. No es una certificación de toda la aplicación. | | Teclado/foco | Los enlaces del resumen reciben foco y Tab recorre ambos, con outline visible. | | Carga | Skeleton con nombre accesible, capturado durante el reintento inicial de un HTTP 503 sintético; después se retira la interceptación y se resuelve la lectura real. | | OSUI-16 | Selector real de idioma: tarjeta traducida a inglés; además, render es/en en las pruebas dirigidas. | | Zoom | Reflow con CSS `zoom: 2` y sin overflow de la tarjeta. No se ha controlado ni se declara probado el zoom nativo de la interfaz del navegador. | Directorio local excluido de Git: `.artifacts/playwright/authenticated-2jopd8ii/current-operating-systems/`. `manifest.json` registra dimensiones y SHA-256 de las capturas; `measurements.json` y `ui-final.log` conservan los resultados. Muestras abiertas e inspeccionadas: `asset-light-320.png`, `asset-dark-1600.png`, `workstation-dark-320.png`, `saved-version.png`, `workstation-english.png`, `workstation-loading.png` y `asset-error.png`. Las rutas no contienen sesión: el archivo privado de autenticación vive separado y se elimina al cerrar el preparador. La regresión del mapa se amplía para que los nombres sintéticos largos coincidan tanto en inventario como en el resolver público; no se simulan dos nombres contradictorios del mismo producto. Las primeras iteraciones del procedimiento de carga fallaron por manejo de rutas MCP y una etiqueta incorrecta del selector de idioma; se corrigió el procedimiento y el recorrido completo final pasa. No se alteró código de producción para ocultar esos fallos de prueba. Las capturas nuevas corresponden a la tarjeta de SO dentro del viewport indicado, no a una auditoría de toda la página. Se abren muestras representativas para revisión visual. No se certifica accesibilidad global, comportamiento de lectores de pantalla ni cumplimiento WCAG completo. Las limitaciones generales de las fichas ajenas al resumen —por ejemplo acciones extensas de otros paneles en móvil— no se corrigen en esta intervención. ## Skills consultadas Principal: `vercel-composition-patterns`, según la matriz del proyecto, para componente compartido y composición desde Companies mediante un slot de Assets. `vercel-react-best-practices` orienta cálculo derivado durante render y reutilización de la caché TanStack existente, sin sustituirla por SWR. `shadcn` se aplica consultando las APIs locales reales de DetailPanel, Skeleton y Button; el intento `pnpm dlx shadcn@latest docs skeleton button` falla por DNS `EAI_AGAIN`, por lo que no se atribuye lectura de documentación remota. No se modifica vendor ni se instala ninguna dependencia del proyecto. `rdx-solid` mantiene la solución dentro de los agregados y contratos actuales. La consulta puntual de `ui-ux-pro-max` sobre textos largos respalda `min-w-0` y wrapping de tokens, subordinados a DESIGN. El contraste, foco y responsive se contrastan mediante navegador, no se deducen de las skills. No falta ninguna skill necesaria para la implementación; no se instalan auditores adicionales. ## Limitaciones y siguientes decisiones Se conserva la consulta completa de instalaciones de compañía y el límite de una pareja abierta por activo/producto. No se añaden instancias de arranque, procedencia/verificación persistida, ausencia confirmada, reconciliación con perfiles ni capacidad de licencias. La duplicidad de fábrica del servidor queda pendiente para otra intervención. La clasificación de una referencia inaccesible es desconocida; el aviso es deliberadamente de software y no inventa un SO. Las ediciones permanecen en el recurso y no se reconstruyen versiones comerciales desde nombres. La auditoría de historia inmutable, snapshots de producto/licencia y mejora de consultas por volumen también permanecen fuera del alcance aprobado. ## Cierre del entorno El preparador se cerró con SIGINT tras identificar exclusivamente su proceso por los archivos de esta ejecución. Confirmó la retirada de servicios, contenedor/base y sesión temporales. Las 30 capturas y los logs de evidencia permanecen sin credenciales. El código de salida del preparador fue cero; no se detuvo ni modificó la base habitual. ## Revalidación posterior del árbol de trabajo El 25 de octubre de 2026 se revisó de nuevo la integración conservada en el árbol de trabajo. No se volvió a implementar el resumen ni se modificó código de producción. La selección de compañía/activo, `configured` y recurso `os`, los estados de interfaz y la composición de activo/puesto siguen presentes. Se ejecutaron con `node --import tsx --test --test-isolation=none` las suites `current-operating-systems.test.tsx`, `software.test.tsx`, `asset-detail.test.tsx` y `software-installation.test.ts`: **19/19 casos correctos**. Se verificaron también los hashes de las 30 capturas conservadas y la ausencia del archivo privado de sesión; no se repitió el recorrido de navegador ni la integración SQL en esta revalidación. `pnpm --filter @metis/companies typecheck` registra ahora un fallo en el trabajo concurrente de asociación de activos: `modules/assets/src/web/workstations/associate-assets/ui/workstation-asset-add-dialog.tsx:90`, TS2322. La llamada a `ContextHelp` aporta `children`, pero su contrato requiere `label`, `title` y `description`. Ese archivo no pertenece a la implementación del resumen y no se modifica en esta revisión. El resultado anterior de los typechecks dirigidos corresponde a su ejecución previa; no acredita que el árbol concurrente actual esté libre de errores.