# R2.4 — Aplicaciones globales en instalaciones de software Fecha: 24 de octubre de 2026. Implementación integrada en el árbol de trabajo; catálogo y asociaciones globales **desactivados por defecto**. La validación funcional de esta fase pasa en instalaciones desechables. La validación global del repositorio conserva fallos ajenos descritos en §23: **no se declara aceptación completa de G6 ni se activa una base habitual**. ## 1. Objetivo y alcance Una `SoftwareInstallation` particular puede referenciar por UUID una aplicación global elegible de R2.3. Dos compañías comparten el producto, conservando instalaciones, activos, versiones, estados, licencias e historial independientes. No se copia el producto ni se modifica el comportamiento de `ApplicationConfiguration`. El catálogo de R2.3 y el resolver por propósito de R2.2 siguen siendo los responsables de la definición y elegibilidad de Resource. Companies incorpora su resultado mínimo exclusivamente al contexto interno de instalaciones. `IResourceInventoryService.list(company)` conserva su semántica particular. ## 2. Prerrequisitos verificados Se aplican los contratos R2.0 [01](01-contrato-arquitectonico.md), [02](02-decisiones-d01-d10.md), [03](03-contrato-seguridad.md), [04](04-contratos-api-dto.md), [05](05-contrato-persistencia.md), [06](06-matriz-consumidores.md), [07](07-plan-despliegue.md), [08](08-plan-pruebas.md), [09](09-plan-implementacion-r2.md) y [10](10-resumen-para-aprobacion.md), junto a las implementaciones [R2.1a](11-r2-1a-lectores-compatibles.md), [R2.1b](12-r2-1b-persistencia.md), [R2.2](13-r2-2-autorizacion-contratos.md) y [R2.3](14-r2-3-catalogo-global.md). Se reutilizan sus lecturas completas de esta sesión y se contrasta el comportamiento actual de lectores, writers, rutas y preflight. Los informes históricos permanecen intactos. Entorno: WSL Ubuntu, Node 24.20.0, PNPM 11.25.0, Sequelize v6 y MariaDB 11.8.x desechable. El arranque con catálogo habilitado mantiene el preflight de esquema scoped, CHECK y FKs. La migración R2.1b se ejecuta explícitamente en la preparación temporal, con writers detenidos. No se conecta ni se migra la base habitual; no se modifica ningún `.env`. ## 3. Archivos principales | Área | Archivos y responsabilidad | | --- | --- | | Composición | `apps/server/src/config/database.ts`, `infrastructure/http/identity-runtime.ts`; módulos de Resources, Assets y Companies: gates y servicios ligados a transacción. | | Resolver | `modules/resources/src/api/infrastructure/persistence/resource-reference-reader.ts`, `resource-application.routes.ts`, contratos de referencia y capabilities. | | Persistencia | `modules/companies/src/api/infrastructure/persistence/sequelize-software-installation.repository.ts`, `software-installation-dependencies.ts`; lecturas transaccionales de repositorios de Assets y Resources. | | Aplicación y HTTP | Policy, mapper, puerto y casos de uso de SoftwareInstallation; `software-installation.routes.ts`. | | Selector | `modules/resources/src/web/application-references/`: API, query keys, resolución, feedback, selector y picker. | | Vistas | Panel, formulario, editor e historial de instalaciones; `workstations/detail/ui/workstation-software.tsx`; API de software y cabecera opcional del borrado compartido. | | Caché e idioma | Invalidación global de Resources, controlador de catálogo, locales es/en de Resources y Companies. | | Biblioteca | `packages/rdx-ui/src/components/remote-option-picker.tsx`: selección inicial opcional; documentación de componentes. Vendor sin cambios de esta fase. | | Pruebas | `apps/server/src/global-software.integration.test.ts`, cliente HTTP compartido y fixtures de scope; regresiones R2.2/R2.3; pruebas de dominio, representación y caché. | | Procedimientos | `scripts/resource-scope-test.py`, `visual-audit.py`, `global-applications-ui-audit.py`, nuevo `global-software-ui-audit.py`; comando `pnpm test:global-software`. | El árbol contenía trabajo previo y cambios concurrentes. La revisión de esta fase usa una instantánea anterior y una lista explícita de 54 ficheros TS/TSX/JSON, además de scripts y documentación; `git diff` contra HEAD incluye trabajo adicional que no debe atribuirse a R2.4. ## 4. Contratos aplicados La instalación conserva `companyId`, `assetId`, `resourceId`, `licenseResourceId` opcional, identidad, versión, estado, notas y revisiones. El producto y el activo continúan inmutables en edición. El ámbito se resuelve desde UUID; no se acepta un `scope` ni un `globalResourceId` aportado por el cliente. No se modifica el DTO común de SoftwareInstallation ni sus tablas. El conjunto de UUID globales es interno al contexto del repositorio y permite negociar el contrato HTTP sin añadir campos al DTO. Domain y Application no importan Sequelize; Infrastructure y composición aportan `Transaction` al puerto público existente de Resources. ## 5. Matriz de elegibilidad | Referencia | Selección / alta o reconfiguración dependiente | Lectura e historial existentes | | --- | --- | --- | | Global activo, plantilla stock real `template:application` | Sí, con gate efectivo de asociación | Sí, incluso con gates cerrados | | Global inactivo | Excluido de candidatos; rechazo de nueva dependencia | Se conserva; notas y retirada permitidas | | Aplicación particular propia activa | Sí | Sí | | Sistema operativo particular propio elegible | Contrato existente | Sí | | Aplicación/SO de otra compañía | Rechazo | No se amplía el acceso ajeno | | SO global, licencia global, SMB, certificado o plantilla incompatible como producto | Rechazo | No se reinterpretan como aplicación | | Licencia particular propia | Solo en el campo independiente de licencia | Según contrato local existente | | Licencia ajena, global o producto usado como licencia | Rechazo | No se habilita acceso cruzado | | UUID inexistente | Rechazo controlado | No se inventa un producto | El resolver exige la plantilla stock y metadatos compatibles; el nombre no determina elegibilidad. La policy distingue alta, cambio de versión/estado hacia `pending` o `configured`, y edición sin nueva dependencia. Toda alta requiere disponibilidad; notas, no-op y retirada de una instalación existente no exigen que el producto siga activo. ## 6. Integración transaccional y precisión del contrato de locks `save` inicia la transacción Sequelize, obtiene primero la sesión del resolver, carga instalaciones y revisiones, inventarios locales y metadatos globales dentro de esa misma transacción, ejecuta dominio/policies y vuelve a validar la asociación por propósito antes de escribir. Los servicios ligados a transacción no abren un `get` independiente. Las licencias y los activos se leen con la compañía bloqueada por los mismos writers que los modifican. `remove` respeta el mismo orden; `get` usa una sesión de lectura sin locks de escritura. **Precisión explícita de R2.0 §6 / R2.2:** el punto de coordinación sigue siendo la raíz existente de catálogo, pero la asociación toma `LOCK IN SHARE MODE`, mientras que las mutaciones de producto/plantilla conservan su lock exclusivo. Se conserva el orden catálogo → compañía → recursos por UUID → raíces operativas. Los metadatos de los UUID solicitados se releen mediante current reads protegidas. No se bloquea la tabla ni se adquiere un lock exclusivo sobre todas las filas del catálogo. Esta precisión evita serializar exclusivamente las instalaciones de compañías distintas sin retirar la coordinación con los writers de catálogo. Es una resolución documentada de la tensión entre el coste conservador de R2.0 y la restricción de concurrencia de R2.4; no una promesa de throughput. Una mutación de catálogo puede esperar a asociaciones que mantienen el lock compartido; los cambios de plantilla siguen teniendo el alcance de propagación ya aprobado. Tres pruebas reales con barreras `afterQuery`, HTTP de producción y MariaDB demuestran: 1. **Instalación primero:** INSERT detenido antes de commit; desactivación observada esperando en `INNODB_LOCK_WAITS`; se libera la instalación, ambos commits terminan y la lectura deriva `needsReview`. 2. **Desactivación primero:** cambio de producto detenido antes de commit; instalación observada esperando; al liberar la desactivación, la instalación relee inactivo, devuelve 409 y no crea raíz ni revisiones. 3. **Dos compañías:** ambos INSERT alcanzan sus barreras antes de permitir cualquiera de los commits; los dos terminan 201. No hay exclusión mutua de catálogo entre asociaciones. No se usan esperas arbitrarias para afirmar el orden. Los timeouts son límites de fallo. La observación de locks usa exclusivamente el contenedor desechable propiedad del runner; no amplía privilegios de la cuenta de aplicación. ## 7. Persistencia No se añaden migraciones, columnas, tablas, JSON ni nuevas FKs. El ensayo consulta las FKs reales de `software_installations` y `software_installation_revisions`: producto y licencia conservan referencias UUID simples a Resource; activo y compañía mantienen sus relaciones; revisiones conservan actores y referencias. Una FK simple permite el UUID global con dueño NULL, pero **no demuestra aislamiento**: lo aplica la validación transaccional por compañía/propósito, con negativos HTTP/SQL. Se prueban producto global con `resources.companyId IS NULL`, instalación con dueño concreto, licencia local y licencia ausente NULL. El mismo UUID global aparece en instalaciones de dos compañías con una sola fila de producto. La evidencia de FKs está en `software-foreign-keys.json` del ensayo. ## 8. Licencias La lista y policy de licencias siguen siendo particulares. Se exige compañía propia, uso de licencia correcto y disponibilidad/vigencia cuando se incorpora o cambia la referencia. Una licencia global nunca entra en ese inventario; un producto global no puede ocupar su campo. Se conserva la posibilidad de desvincularla y su historial. La asociación es informativa: no reserva plazas ni calcula derechos, cobertura o compatibilidad comercial del fabricante. El formulario conserva esta explicación y aclara que las altas rápidas crean recursos particulares. ## 9. API y negociación Se mantienen GET/POST, actualización y borrado de `/api/software-installations/:companyId` y sus rutas existentes. Sesión, autorización ordinaria y CSRF siguen delante de las operaciones; ser administrador global no permite saltarse la pertenencia del activo/licencia/producto local. `X-Metis-Resources-Contract: 2` anuncia soporte de referencias globales; se emite `Vary`. Sin cabecera, los CRUD particulares siguen funcionando. Una respuesta que contiene instalaciones globales, o una mutación de una instalación global, devuelve 409 `RESOURCE_CLIENT_UPGRADE_REQUIRED` para clientes antiguos, sin ocultar filas. Cabecera desconocida: 400. Se prueba también UUID en mayúsculas normalizado, sin bypass de negociación. Se reutilizan los endpoints R2.2 de candidatos y resolución mínima. Se añade GET autenticado `/api/resource-application-candidates/:companyId/capabilities`, con parámetros, query vacía y cabecera validados. Expone únicamente `softwareSelection` y `softwareAssociation`; no identidad administrativa ni información privada. Globales en candidatos también requieren negociación v2. Errores relevantes: referencia inexistente/ajena/incompatible 404, no disponible o gate cerrado 409, entrada inválida 400, falta de sesión 401 y CSRF rechazado 403. No se envían SQL, stacks ni datos privados de otras compañías. ## 10. Selector mixto `SoftwareApplicationSelect` consume las capabilities del servidor. Con selección mixta cerrada conserva `SelectWithAction` y sus altas particulares. Abierta, usa `RemoteOptionPicker`/`RemoteOptionList`: página remota de 20, búsqueda por nombre/proveedor, procedencia visible, UUID de selección y cursor opaco ligado a compañía/propósito. No se descarga el catálogo global completo para filtrar en React. Se conservan nombre y UUID seleccionados al cambiar búsqueda/página. Confirmar usa la identidad provisional, aunque ya no esté en la página actual. El servidor vuelve a validar vigencia y elegibilidad al guardar. Si selección está abierta pero asociación cerrada, las filas globales están deshabilitadas. La referencia global existente se resuelve también con gates cerrados; en edición el producto permanece fijo. Carga, consulta pendiente, cero resultados, error parcial y reintento tienen mensajes traducidos. La búsqueda diferida cancela su temporizador; las peticiones reciben `AbortSignal` y timeout. No se crea un resolver paralelo en Companies ni se usan nombres como identidad. ## 11. Integración en Assets El panel conserva CRUD e historial y resuelve en lotes los UUID ausentes del inventario particular **del activo mostrado**, sin consultar uno por fila. Muestra nombre, tipo y badge Global, con enlace `/catalogs/global-applications/:id`; las licencias mantienen sus enlaces de compañía. Los valores ausentes de celdas siguen mostrando `—`. Un fallo de metadatos se presenta como feedback independiente con reintento; no convierte toda la tabla particular válida en un error global. Durante carga/error no se inventa un nombre. El historial incorpora el enlace del producto además de versión, estado, actor, fecha y licencia. ## 12. Integración en Workstation Se reutiliza el mismo lector de referencias para las instalaciones del ordenador principal actual, conservando las tarjetas, acciones y expansión existentes. Se muestran nombre global, versión, estado y licencia particular. No se cambia la selección del ordenador ni la semántica de Workstation. El recorrido MCP cambia realmente el ordenador principal mediante la API de la instalación sintética: el nuevo ordenador aparece sin instalaciones, el registro original conserva `assetId` y UUID del producto, y se restaura el puesto. No se trasladan instalaciones. ## 13. Lectura histórica El resolver de lectura no exige actividad ni gates abiertos para una referencia existente. Revisiones, UUID, versiones, estados, actores, fechas y licencias no se reescriben al renombrar/desactivar el producto. Los nombres se resuelven actualmente por UUID; el historial de la instalación no se convierte en historial administrativo de Resource. Los tests comparan snapshots de revisiones anteriores y la independencia entre compañías. Una edición semánticamente idéntica no añade revisión; el guardado solo persiste las revisiones nuevas del agregado. ## 14. Desactivación global Desactivar excluye de candidatos y bloquea altas/reconfiguraciones dependientes posteriores. Las instalaciones existentes permanecen con su estado y licencia; notas y retirada siguen sus reglas. `needsReview` se deriva en la proyección existente, sin columna persistida ni escritura masiva en compañías. El renombrado posterior se resuelve con el mismo UUID; no crea un producto nuevo ni cambia el historial operativo. El apagado de flags no borra datos ni deja de resolver referencias existentes. ## 15. Caché e invalidación Se separan las familias de candidatos/capabilities por compañía y propósito, resolución por compañía/propósito/conjunto ordenado de UUID, catálogo global, instalaciones por compañía e inventario particular. No se usa una compañía ficticia NULL. Guardar una instalación invalida sus instalaciones y candidatos/referencias de software de esa compañía. Borrado conserva la invalidación de instalaciones: no cambia producto ni elegibilidad. Renombrado/estado global invalidan las referencias cuyo conjunto contiene ese UUID y las instalaciones ya cacheadas que lo utilizan; las compañías sin ese producto y sus inventarios no se invalidan. Candidatos se invalidan porque pueden ganar/perder elegibilidad; cambios del catálogo invalidan candidatos y referencias. La prueba de caché comprueba el alcance selectivo. Los cambios externos se observan al recargar, recuperar foco/reconectar o vencer el periodo de refresco de los lectores existentes. No se implementa push. La caché administrativa privada de R2.3 sigue separada de la proyección mínima ordinaria. ## 16. Flags y gates | Gate | Configuración / condición efectiva | Valor predeterminado | | --- | --- | --- | | Catálogo | `RESOURCE_GLOBAL_CATALOG_ENABLED` | false | | Administración | Policy independiente R2.3: bootstrap inicial activo, sesión/authVersion y estado Identity válidos; exige catálogo para escribir | Sin delegación nueva | | Candidatos mixtos | catálogo AND `RESOURCE_GLOBAL_SOFTWARE_CANDIDATES_ENABLED` | false | | Asociación SoftwareInstallation | catálogo AND `RESOURCE_GLOBAL_SOFTWARE_ENABLED` | false | | Asociación ApplicationConfiguration | `RESOURCE_GLOBAL_PROFILE_ENABLED` solo admite false | false | Selección y asociación se prueban en ambas combinaciones independientes. Con catálogo cerrado, incluso flags subordinados true quedan efectivamente cerrados. La lectura global existente y el CRUD local siguen operativos tras reinicio con gates cerrados. La apertura exige esquema scoped completo y preflight válido; aquí solo se abre en contenedores sintéticos y no cambia los valores predeterminados. ## 17. Pruebas de dominio, representación y caché Pruebas dirigidas SoftwareInstallation: **7/7**, incluyendo elegibilidad/inactividad, recurso fijo, versión, licencia, idempotencia, historial, representación es/en y producto global sin `ResourceDTO` particular. Se verifica que la licencia mantiene su enlace local. Resources: **100/100**, incluyendo flags independientes, contrato de perfiles cerrado e invalidación por UUID. Los casos de fronteras/seguridad y concurrencia se completan con integración real, no se simulan con un mero schema. Los comandos y sus límites se registran en §22–23. ## 18. MariaDB `python3 scripts/resource-scope-test.py --software --regressions`: **13/13** en la suite R2.4 (incluye el test padre) y **50/50** regresiones. Esquema R2.1b, FKs y NULL verificados. Tres carreras con orden observable y relectura final, sin duplicar el producto ni producir una instalación tras el commit de desactivación. Volumen SQL: 15 compañías; principal con al menos 50 personas, 50 activos, 50 perfiles, 50 puestos, 50 aplicaciones particulares y 50 instalaciones adicionales mediante HTTP. Otras compañías verifican aislamiento. La paginación de candidatos recorre 75 UUID distintos en páginas de siete, con búsqueda por tildes, homónimos y ambos ámbitos, sin notas privadas ni información operativa ajena. Artefactos locales ignorados: [resultados](../../../.artifacts/resources-global-scope/r2-4/software-results.json), [FKs](../../../.artifacts/resources-global-scope/r2-4/software-foreign-keys.json), [integración](../../../.artifacts/resources-global-scope/r2-4/integration.log) y [regresiones](../../../.artifacts/resources-global-scope/r2-4/regressions.log). Los contenedores temporales se retiran al terminar. ## 19. HTTP real Se usa el runtime de producción con bootstrap, sesiones reales, cookies/CSRF y usuario ordinario. Se prueban CRUD particular/global, historial, negociación v2, cabecera inválida, autenticación, CSRF, campos extra rechazados, activos/productos/licencias ajenos, global inactivo, tipos incompatibles, asociación cerrada, independencia de gates y lectura/retirada tras apagado. El mismo producto global se instala en A y B con versiones, estados y licencias particulares distintas. ApplicationConfiguration no acepta ese UUID global y sus candidatos siguen siendo particulares. No se añaden rutas administrativas alternativas para instalar. ## 20. Playwright MCP Comando: `python3 scripts/visual-audit.py --global-software --check`. Se usa el MCP Docker/stdio fijado por el proyecto y una instalación temporal preparada por el procedimiento existente. Login por formulario real con usuario ordinario; el administrador temporal solo prepara productos/desactivación y fixtures. La única simulación de transporte es un GET de candidatos 500 para verificar recuperación; todas las escrituras son reales sobre `metis_desk_test`. S01–S11 cubren mixto, búsqueda con tildes, selección por UUID conservada tras cambiar la búsqueda, licencia local, guardado, reapertura con producto fijo, lectura tras desactivación, candidatos sin inactivos, vacío/recuperación, error/reintento, gate cerrado con alta particular, software del puesto y alta densidad. Se comprueban Escape y retorno de foco del picker, ausencia de desbordamiento de página y viewports 1600/1440/1280/1024/768/390; el picker se abre también a 390. El manifiesto registra 25 comprobaciones y 14 capturas, revisadas visualmente. Esto no equivale a certificación WCAG ni a una prueba con lector de pantalla. Volumen visual: 15 compañías, principal con 50 personas, 51 activos, 50 perfiles, 50 puestos y al menos 50 aplicaciones particulares; 50 productos globales y **52 instalaciones en el ordenador principal** al terminar. El volumen se crea por procedimientos reproducibles y no se reutiliza una base existente. Se eliminan sesión, credenciales y servicios temporales. Skills aplicadas: `rdx-solid` para límites de capas, transacción y pruebas; `ux-search` para consulta remota, vacío y reintento; `ux-inputs-and-forms` para conservar el formulario/validación; `shadcn` para integrar APIs reales de Base UI/RDX sin tocar vendor. Se respetan DESIGN y los patrones normativos. No se instala ninguna skill, dependencia ni auditor especializado de Sequelize, seguridad o accesibilidad; se utilizan las verificaciones disponibles. ## 21. Capturas y manifiesto Evidencias sintéticas y hashes SHA-256 se conservan localmente fuera de Git, de acuerdo con el procedimiento visual: [manifiesto final](../../../.artifacts/playwright/r2-4/final/r24-report.json). La carpeta no contiene cookies, passwords ni `storageState`. Las capturas de densidad enfocan el bloque de software mediante scroll programático y no contienen toda la página ni todas las filas. En algunas, el shell queda recortado y aparece una franja exterior vacía al desplazar la raíz; no se retocan las imágenes para ocultarlo. A 390 px la tabla conserva desplazamiento horizontal y el contador ocupa varias líneas, comportamiento compartido del listado que no se rediseña en esta fase. La evidencia del picker y del puesto muestra por separado sus controles y productos; no se declara resuelto el diseño móvil general de los listados. | Evidencia | Captura | | --- | --- | | Búsqueda mixta / procedencia | [Selector](../../../.artifacts/playwright/r2-4/final/r24-01-mixed-search.png) | | Versión y licencia particular | [Formulario](../../../.artifacts/playwright/r2-4/final/r24-02-installation-form.png) | | Software global del puesto | [Workstation](../../../.artifacts/playwright/r2-4/final/r24-03-workstation.png) | | Sin resultados / error recuperable | [Vacío](../../../.artifacts/playwright/r2-4/final/r24-04-no-results.png), [error](../../../.artifacts/playwright/r2-4/final/r24-05-candidate-error.png) | | Activo, seis anchos | [1600](../../../.artifacts/playwright/r2-4/final/r24-asset-1600.png), [1440](../../../.artifacts/playwright/r2-4/final/r24-asset-1440.png), [1280](../../../.artifacts/playwright/r2-4/final/r24-asset-1280.png), [1024](../../../.artifacts/playwright/r2-4/final/r24-asset-1024.png), [768](../../../.artifacts/playwright/r2-4/final/r24-asset-768.png), [390](../../../.artifacts/playwright/r2-4/final/r24-asset-390.png) | | Móvil | [Picker](../../../.artifacts/playwright/r2-4/final/r24-mobile-picker.png), [puesto](../../../.artifacts/playwright/r2-4/final/r24-workstation-390.png) | | Gate cerrado | [Alta particular](../../../.artifacts/playwright/r2-4/final/r24-06-gate-closed-local.png) | ## 22. Regresiones y comandos | Comprobación ejecutada | Resultado | | --- | --- | | `python3 scripts/resource-scope-test.py --software --regressions` | 13/13 + 50/50 | | `python3 scripts/resource-scope-test.py --catalog` | R2.3: 11/11 | | `python3 scripts/resource-scope-test.py --security` | R2.2: 11/11 | | `pnpm --filter @metis/resources test` | 100/100 | | Importación explícita de las dos suites SoftwareInstallation con `node --import tsx -e` | 7/7; evita dar por suficientes los dos resultados agregados por fichero del runner aislado | | `pnpm --filter '!@repo/shadcn-ui' -r typecheck` | Paquetes propios correctos, incluidos Companies, Resources, Assets, Web y Server | | `pnpm exec biome check <54 archivos de la fase>` | Sin errores ni warnings | | `pnpm exec eslint ` | Correcto | | `pnpm build` | Correcto; aviso previo de tamaño del bundle web | | Compilación Python de los cuatro scripts y revisión de diff | Verificación dirigida de los procedimientos y cambios propios | | `python3 scripts/visual-audit.py --global-software --check` | Recorrido real S01–S11, gates cerrados, cambio de ordenador y seis anchos | | `python3 scripts/resource-scope-test.py --software --regressions --monorepo` | Ensayo amplio aislado: conserva fallos descritos en §23 | | `pnpm typecheck`, `pnpm lint`, `pnpm check` | Ejecutados; fallos globales en §23 | Las 50 regresiones SQL incluyen instalaciones/perfiles particulares, licencias, Assets, relaciones de puesto, SMB/directorio, Access, remoto e historial. El ensayo amplio de paquetes registró Identity 11/11 y Assets 147/147. Se vuelve a comprobar el catálogo/autorización global mediante R2.2/R2.3. No se ejecuta un `pnpm test` que pueda consumir configuración habitual: su recorrido de paquetes se ejecuta dentro del entorno SQL desechable del runner `--monorepo`. ## 23. Fallos conocidos y límite de aceptación El árbol completo no está verde. En el ensayo amplio aislado, Companies registró 245/247: dos expectativas de render de ficha/contactos y formulario de persona ajenas a este flujo. La repetición del comando de Companies también señala el fichero agregado `companies-page.test.tsx`; las siete pruebas de software se ejecutan explícitamente y pasan. La integración legacy de Identity se detiene en `identity.integration.test.ts:82` (404 esperado frente a 401 actual de una ruta archivada). Estos fallos ya están documentados en R2.3; no se modifica el código ajeno para ocultarlos. El typecheck global sigue fallando en `packages/shadcn-ui/src/components/calendar.tsx:165`, TS2375 sobre `locale`, vendor sin cambios propios. `pnpm lint` y `pnpm check` también registran diagnósticos en el árbol previo/concurrente; el análisis dirigido de los 54 archivos de esta fase está limpio. No se etiqueta automáticamente cada diagnóstico concurrente como anterior a HEAD ni se declara el monorepo validado. Los logs globales y el ensayo amplio se conservan junto a las evidencias locales. | Gate | Estado observado | | --- | --- | | G1 — Dominio | Pasa en las pruebas de esta fase: dueño local, UUID compartido, licencias/activos aislados, tipos y estados, historial. | | G2 — Transacciones | Pasa: unidad transaccional, ambos órdenes de commits y compañías paralelas probados en MariaDB. | | G3 — Contratos | Pasa: DTO estable, negociación, proyección mínima y selector mixto. | | G4 — UI/UX | Recorrido MCP y revisión visual ejecutados, con teclado/foco y responsive; alcance de accesibilidad indicado en §20. | | G5 — Activación | Pasa como implementación: flags separados, preflight y defaults cerrados, perfil cerrado. No se activa una base habitual. | | G6 — Validación | Pruebas dirigidas, SQL/HTTP, build y navegador pasan; **aceptación global pendiente** por los fallos ajenos anteriores. | Por esta limitación, R2.4 se entrega implementada con evidencias dirigidas, **sin declararla totalmente aceptada ni abrir los flags habituales**. ## 24. Riesgos residuales La coordinación compartida del catálogo puede hacer esperar sus mutaciones; la compañía sigue serializando operaciones propias. Las pruebas demuestran las carreras descritas, no un benchmark de carga ni ausencia universal de deadlocks. Se conservan las restricciones y el orden de locks aprobado. Se reutiliza la estrategia R2.2 de metadata/paginación; no se afirma coste O(pageSize) ni se incorpora un índice nuevo sin evidencia. El inventario particular completo sigue siendo necesario para licencias y el selector antiguo; el catálogo global no se precarga completo en el cliente. Los lotes de resolución evitan N+1 por fila, aunque un conjunto grande requiere varios lotes de 100. No hay cálculo de derechos de licencia, push entre operadores ni certificación de accesibilidad. Un fallo de metadata puede dejar temporalmente un `—` con feedback y reintento, sin eliminar el registro. Los artefactos visuales ignorados deben conservarse aparte si se necesita una revisión en otra máquina. ## 25. Prerrequisitos de R2.5 Resolver la deuda de validación global y aceptar expresamente el siguiente alcance. Revisar los consumidores y policies de `ApplicationConfiguration` y su propia unidad transaccional, manteniendo sus restricciones locales hasta esa fase. Repetir las pruebas de seguridad, concurrencia, lectura histórica y compatibilidad que correspondan a perfiles; no inferir autorización desde el gate de software. R2.4 no activa perfiles globales, consolidación de duplicados, licencias/SO globales, importación, fabricantes, ACL nueva, delegación ni migración automática de referencias.