23 KiB
R2.3 — Catálogo global de aplicaciones
Fecha: 24 de octubre de 2026. Implementación sobre las decisiones ratificadas de R2.0 y las preparaciones R2.1a, R2.1b y R2.2. Este documento describe esta fase; los informes anteriores conservan su estado histórico.
1. Estado de implementación
Backend y cliente del catálogo implementados dentro de Resources. La definición sigue siendo
un Resource con scope=global, companyId=NULL, UUID propio, valores e historial relacionales.
El catálogo está deshabilitado por defecto. La habilitación de prueba se realiza exclusivamente
en instalaciones desechables con metis_desk_test, después de migración y preflight.
Los gates de aceptación de esta fase se satisfacen con las validaciones de §3 y §18–22. Los checks globales conservan los fallos ajenos descritos en §22. No se ha habilitado ni migrado la base habitual.
2. Prerrequisitos comprobados
HEAD inicial 34b8f915381de02f9e13334634455074a5dc55ee; Node 24.20.0 y PNPM 11.25.0,
coincidente con packageManager. Sequelize 6.37.8; MariaDB 11.8 mediante la imagen fijada del
lanzador. El árbol inicial incluía las fases anteriores y cambios concurrentes de interfaz,
listados y puestos de trabajo. Se conservaron sus archivos y se guardaron copias/huellas antes
de implementar para distinguir las contribuciones de esta fase.
Se consultaron los contratos 01–13 de esta serie y las fuentes de capas, composición, HTTP, identificadores y pruebas, además de AGENTS, DESIGN, estructura web, patrón de listados, diálogos, matriz de skills y procedimiento Playwright. Se contrastaron las APIs instaladas.
Skills consultadas: rdx-solid para dominio, fronteras y pruebas; ux-tables como principal del listado; frontend-design, ux-dashboard, ux-microcopy, ux-empty-states, shadcn, vercel-composition-patterns y vercel-react-best-practices según el encargo. También ux-inputs-and-forms y ux-navigation para formulario y navegación. Influyeron en jerarquía, reutilización de primitivas, mensajes recuperables, estados separados, componentes por responsabilidad y ausencia de estado remoto duplicado. No se introdujo un dashboard de métricas. No se instalaron dependencias, skills ni auditores especializados de seguridad/SQL/accesibilidad. Las comprobaciones de navegador descritas aquí utilizan Playwright MCP, sin atribuirles una auditoría especializada externa.
3. Gates abiertos y cerrados
| Gate | Estado de esta entrega |
|---|---|
| G1 backend | Implementado: comandos, persistencia, UUID, historial e idempotencia; integración propia validada. |
| G2 autorización | Identity real y CSRF; matriz de seguridad validada. |
| G3 contratos | DTO mínimo/administrativo separados, validación estricta y compatibilidad local verificadas. |
| G4 UI | Validado con Playwright MCP, 23 comprobaciones y revisión de capturas en seis anchos. |
| G5 consumidores | Cumplido: las puertas de selección mixta, instalaciones globales y configuraciones globales permanecen cerradas; regresiones locales verificadas. |
| G6 validación | Pruebas propias, regresiones, typecheck dirigido, Biome/ESLint, build e inspección visual correctos. Deuda global ajena declarada en §22. |
Las puertas de catálogo y administración son independientes de las de consumidores. Solo
RESOURCE_GLOBAL_CATALOG_ENABLED=true abre altas/edición/reactivación tras preflight.
RESOURCE_GLOBAL_SOFTWARE_ENABLED y RESOURCE_GLOBAL_PROFILE_ENABLED siguen rechazando true;
mixedSelection continúa siendo literalmente false. La capacidad de administrar se deriva de
Identity, nunca de un flag recibido del navegador.
4. Archivos principales
modules/resources/src/api/application/global-resource.*: puerto, captura de especificación y comandos.modules/resources/src/api/domain/resource.aggregate.ts: operaciones globales explícitas.modules/resources/src/api/infrastructure/persistence/sequelize-global-resource.repository.ts: transacciones, administración, historial y replay persistido.- Los mappers, escritura relacional, especificaciones de revisión y propagación de plantilla de Resources.
modules/resources/src/api/infrastructure/http/global-resource.routes.ts,module.tsy DTO comunes.apps/server/src/config/database.tseinfrastructure/http/identity-runtime.ts: configuración y composición.modules/resources/src/web/global-applications/{list,detail,shared}/: cliente del catálogo.- Navegación, rutas y catálogo general de Companies; locales es/en de ambos módulos.
packages/rdx-ui/src/components/cursor-entity-table.tsx: composición genérica de listado remoto.apps/server/src/global-applications.integration.test.ts, pruebas de dominio, contratos, representación web y rutas; actualización de expectativas R2.2.scripts/resource-scope-test.py,scripts/visual-audit.py,scripts/global-applications-ui-audit.pyy script raíztest:global-applications.- Este informe y precisiones normativas de componentes, listado remoto y rutas del cliente.
No se modifica implementación vendor, archive ni auditoría histórica por esta fase.
5. Servicios y endpoints
Base /api/resource-global-applications, autenticada y con el CSRF existente para POST.
| Método / ruta | Contrato y autorización |
|---|---|
GET / |
Resumen mínimo paginado; sesión ordinaria suficiente. |
GET /:id |
Detalle mínimo de producto global elegible; local/ausente devuelve 404. |
GET /capabilities |
Usuario y authVersion de la sesión, capacidad derivada, estado del catálogo y posibilidad actual de crear. |
GET /template |
Especificación global de serie disponible para alta; exige administrador inicial. |
GET /:id/admin |
Valores y especificación actuales con revisionId; administrador inicial. |
GET /:id/history |
Revisiones administrativas por cursor; administrador inicial. |
POST / |
Alta 201 o replay 200 con creationId UUIDv7. |
POST /:id/update |
Edición con plantilla fija; revisión esperada opcional en HTTP y enviada por la interfaz. |
POST /:id/status |
Desactivación/reactivación, sin borrado físico. |
No existe endpoint de borrado global. Los errores esperables mantienen 400/401/403/404/409. La infraestructura produce respuesta saneada y diagnóstico con requestId; no devuelve SQL ni mensajes internos. Las rutas de candidatos/referencias de R2.2 conservan su política propia.
6. Autorización Identity
Se reutiliza canManageGlobalResources y requireGlobalResourceManager: usuario activo,
authVersion vigente, isInitialAdmin y coincidencia con el marcador bootstrap persistido.
Se revalida en servidor por petición. Sin permisos delegables ni tablas de grants/revokes.
Las mutaciones globales de plantilla y campos conservan el mismo guard, incluido scope codificado.
El cliente usa capacidades para presentar acciones; todos los POST vuelven a comprobar Identity.
7. Contratos DTO
Summary/detail mínimos solo contienen identidad, plantilla, nombre, ámbito, actividad y los campos aprobados de producto: proveedor y, en detalle, URL/descripción. Nunca notas, actores, especificaciones completas, revisiones, consumidores o instalaciones.
El DTO administrativo separado añade values, specification y revisionId. El historial tiene su propia respuesta paginada con snapshots y actor. Los schemas Zod son fuente de tipos y validación en ambos extremos. Scope, companyId, actor e integración extra se rechazan en los payloads. Los DTO legacy locales permanecen con compañía obligatoria y sin scope.
8. Modelo Resource y plantilla
Solo se permite la definición de serie real stockKey=template:application, propietario global,
kind template, campos globales válidos y sin integración. Un nombre traducido o renombrado no
cambia la elegibilidad. Una copia local, usage application o plantilla de otro tipo no la adquiere.
La raíz conserva templateId. createGlobal, updateGlobal y
synchronizeGlobalSpecification son explícitas; las operaciones locales siguen rechazando globales.
9. Persistencia y preflight
No hay tabla nueva, JSON de persistencia, compañía ficticia ni índice UNIQUE por nombre global. Se reutilizan raíces, características, revisiones y especificaciones relacionales de Resource. El arranque habilitado exige el esquema scoped de R2.1b y CHECK/FKs activos tanto en sesión como globalmente; rechaza esquema legacy o parcial y plantilla incompatible. No ejecuta expansión de ámbito automáticamente.
La escritura adquiere primero catálogo Resources global y después raíz; la propagación mantiene el orden catálogo → compañías y prepara todos los cambios antes de escribirlos en una transacción. No se generan proyecciones SMB, directorio, Access ni soporte para un producto global. Las revisiones previas no se sobrescriben. Administración carga la revisión actual; historial limita primero identidades en SQL y lee solo los snapshots de esa página, con actores en lote.
10. Ciclo de vida e idempotencia
creationId es el UUID v7 de la raíz. Bajo bloqueo transaccional, el primer POST crea una sola revisión. La repetición del mismo actor y valores originales normalizados reutiliza la raíz y responde 200 con su estado actual, incluso después de edición o reinicio. Otro payload/actor o colisión con raíz local falla con RESOURCE_CREATION_CONFLICT. Sin tabla de idempotencia adicional.
La edición y el estado comparan expectedRevisionId cuando se aporta: una actualización concurrente produce RESOURCE_UPDATE_CONFLICT. El formulario lo envía y conserva su borrador ante conflicto. Los cambios semánticamente iguales no añaden revisión. Desactivar conserva UUID, valores e historia; reactivar exige de nuevo validez de obligatorios. Si una plantilla añade un obligatorio, puede desactivarse el producto incompleto, pero no reactivarse sin completarlo. Con el catálogo cerrado se permiten lectura, historial, replay conocido y desactivación de seguridad; se impiden alta nueva, edición y reactivación.
La propagación de estructura alcanza globales y locales atómicamente y conserva los snapshots anteriores. No modifica asociaciones operativas ni adopta recursos locales.
11. Navegación
Entrada en Catálogos globales y acceso desde su navegación lateral. URLs reales
/catalogs/global-applications y /catalogs/global-applications/:UUID. Breadcrumb dentro del
contexto global; retorno con criterios de listado. No hay selector ni compañía ficticia.
Un UUID particular no puede consultarse como producto global. Las acciones de formulario son
modales independientes; los destinos de ficha son enlaces.
12. Listado
Mantiene cabecera, búsqueda/filtros, estado/configuración, tabla y pie del patrón oficial.
Acción de alta a la derecha, solo para gestor y disponible únicamente si catálogo/plantilla lo
permiten. Columnas reales: nombre, proveedor, estado y acciones. Celdas ausentes muestran —.
La tabla reutiliza contenido, configuración, acciones y estados RDX, con primitivas shadcn.
La API no ofrece total: se informa rango visible y Página N, sin inventar un total de resultados.
Paginación por cursor en servidor, tamaños 10/25/50/100 en UI y orden por nombre.
13. Detalle
La ficha mínima sirve al usuario ordinario, incluidos inactivos. El administrador obtiene características y notas de su endpoint, acciones y revisiones paginadas. Los valores de características se presentan mediante un componente propio compartido entre ficha e historial, reutilizando FieldSections y DetailField; no se copia íntegramente la ficha local ni se introduce información de instalaciones, licencias, usuarios consumidores o utilización transversal.
14. Formularios y coincidencias
FormDialog, FormBody/FormActions, FormSection, FieldSections, FormLabel, ContextHelp y controles existentes de características de Resource. Plantilla fija, nombre obligatorio y campos reales; sin selección de compañía o plantilla alternativa. Ayuda al final antes del pie. Formulario RHF con schema existente; obligatorios dinámicos y errores de operación. Pending deshabilita controles y cancelación; el diálogo no se cierra durante guardado. Guardar cierra solo después del éxito.
En alta, búsqueda indicativa limitada a diez resultados y enlace al catálogo paginado para revisión. El administrador confirma expresamente que ha revisado el catálogo y se trata de un producto distinto. Las coincidencias de recuperación no se califican como equivalencia ni como aviso fuerte D07. No se implementa similitud difusa, consolidación, detección global/local ni fusión de R2.6. Los homónimos siguen siendo válidos por UUID; una equivalencia confirmada se reutiliza abriendo la ficha existente, sin POST nuevo. No se promete detección exhaustiva ni unicidad semántica automática.
15. Búsqueda y paginación
Se reutiliza la normalización compartida aprobada en R2.2: mayúsculas/tildes y substring literal
en nombre/proveedor; conserva ñ, números y signos según el helper. % y _ no son comodines SQL.
Filtros, búsqueda y orden determinista preceden al límite, con UUID como desempate.
El servidor lee metadatos mínimos, sin inventario/historial por fila; no se afirma coste constante
ni índice de substring. El cliente carga páginas, no todo el catálogo.
q, active, sort, direction, pageSize, cursor y page permanecen en URL. Cambio de criterios reinicia cursor/pila. La ficha conserva contexto. Una URL recargada no conserva la pila anterior de cursores: Anterior vuelve a inicio. Una página posterior vacía ofrece volver a primera página. Las ediciones concurrentes pueden desplazar resultados entre páginas; no se promete snapshot de toda la búsqueda.
16. Caché e invalidación
Raíz resource-global-applications, separada de inventarios locales. Lista por criterios y detalle
mínimo por UUID. Administración e historial incluyen actorId y authVersion; plantilla administrativa
también. Un guard de fallos retira cachés administrativas ante 401/403. El logout existente elimina
consultas ajenas a la sesión. Las vistas administrativas no presentan datos previos tras error de permiso.
Mutaciones invalidan listas globales, detalle del UUID y administración/historial de ese UUID, más las claves de candidatos/referencias previstas, sin invalidar inventarios de todas las compañías. Los listados conservan useListQuery y su refresco de 60 segundos. El borrador del formulario es una copia de edición explícita con revisión esperada; no se sobrescribe por un refetch.
17. Privacidad
La lectura pública autenticada no reutiliza DTO ni caché administrativos. Las pruebas comprueban la ausencia de notas, historia, especificación completa, actores, compañías e instalaciones. Los campos de producto se proyectan mediante whitelist. No aparecen claves de licencia ni referencias operativas en la pantalla. Los datos de pruebas y capturas son sintéticos.
18. Pruebas HTTP y de seguridad
Suite R2.3 con runtime de producción, bootstrap/login real, cookies en memoria y CSRF real. Incluye ordinario rechazado, administración separada, origen/token incorrectos, campos prohibidos, plantilla incorrecta, UUID local/inexistente, no borrado y respuestas saneadas. Se vuelve a ejecutar R2.2 para flag administrativo falso, marcador bootstrap, authVersion, guard de catálogo y resolución.
19. MariaDB y regresiones locales
python3 scripts/resource-scope-test.py --catalog --regressions: 11/11 pruebas R2.3 y 50/50
regresiones HTTP/MariaDB, sin omitidas. Incluye dos POST concurrentes para una identidad, edición
concurrente, replay posterior, historia paginada, final de cursor vacío, homónimos, rollback por
fallo tras INSERT, recuperación, propagación y reinicio del runtime con puerta cerrada.
Volumen propio SQL: 15 compañías; principal con 50 personas, activos, perfiles, puestos y aplicaciones locales; las otras catorce con aplicación local. Se crean 31 aplicaciones globales en los escenarios. Las pruebas verifican que el inventario local sigue devolviendo solo sus 50 recursos y que los POST de software/perfil rechazan globales. SMB, Access, remoto, licencias, directorio y proyecciones se cubren mediante las 50 regresiones existentes, vueltas a ejecutar en esta fase.
La primera ejecución de regresiones detectó un campo obligatorio que la propia fixture R2.3 dejaba
en la plantilla compartida. Se corrigió la restauración de la plantilla antes de las demás suites;
la repetición pasa. El fallo inicial de historial por tokenización SQL <:position se corrigió y
su consulta y extremo vacío quedaron cubiertos. Una comprobación adicional detectó que la
reactivación reconstruía los valores sin exigir nuevos obligatorios; se corrigió para validar
con ResourceValues.create al activar y conservar reconstruct solo al desactivar. La prueba HTTP
que fallaba antes queda incluida en la repetición. No se atribuyen esos fallos a deuda anterior.
20. Playwright MCP
Recorrido completado e inspeccionado: 23 comprobaciones, 15 capturas. Procedimiento: bash scripts/setup-playwright-wsl.sh y
python3 scripts/visual-audit.py --global-applications --check. Reutiliza Docker/MCP stdio fijado,
login real y limpieza del procedimiento existente, sin instalar Chromium en WSL ni modificar la
configuración global. El modo R2.3 migra con escritores detenidos y reinicia solo su API temporal
con el catálogo habilitado. Sirve una copia del build mediante Vite preview para que cambios HMR
concurrentes del workspace no reinicien el formulario durante la prueba.
La preparación de navegador usa HTTP público sobre instalación vacía: 15 compañías; principal con 50 personas/perfiles/puestos, 51 activos y al menos 50 aplicaciones locales; 51 aplicaciones globales tras el alta interactiva. No ejecuta fixtures destructivas de integración ni toca una BD existente. Credenciales aleatorias solo en memoria; no se conservan cookies, storageState ni contraseñas.
El recorrido verifica catálogo vacío, alta y nombre obligatorio, ficha e historial, edición, desactivación/reactivación, filtro de inactivos, búsqueda, siguiente/anterior, contexto de retorno, Escape, devolución de foco y Tab. El usuario ordinario consulta sin alta, edición, menú vacío ni historial. Se comprobaron 1600, 1440, 1280, 1024, 768 y 390 px, sin desbordamiento horizontal de la página. La tabla conserva su desplazamiento horizontal interno y acciones fijadas en móvil.
Los estados de error y carga se indujeron reteniendo/fallando exclusivamente el GET de listado; se verificó Reintentar y recuperación. Las altas, edición, estado e historial anteriores utilizan HTTP y SQL reales. Las capturas de esos estados y las de formulario, escritorio, móvil y lectura ordinaria fueron inspeccionadas. No se probaron lectores de pantalla ni todos los atajos de un navegador; se declara la verificación concreta de teclado/foco, no una certificación WCAG.
Los intentos iniciales detectaron selectores de prueba por etiqueta que incluían el indicador de obligatorio y una recarga HMR concurrente; se corrigió el selector por rol/nombre accesible y se usó el build inmutable. La ampliación de carga requirió liberar la petición retenida antes de retirar su handler. Los recorridos finales completados sustituyen esos intentos de diagnóstico.
21. Capturas
Evidencias portables, exclusivamente sintéticas, junto a este informe:
- Listado 1600 px, 1440, 1280, 1024, 768, 390.
- Catálogo vacío, alta, detalle administrativo, inactivo, detalle móvil.
- Sin coincidencias, error recuperable, carga, detalle ordinario.
- Manifest de comprobaciones y SHA-256.
Las capturas de intentos interrumpidos permanecen solo como diagnóstico local. No contienen credenciales y no se presentan como validación completada.
22. Validación técnica y deuda anterior
- Resources: 98/98 pruebas, incluidas nuevas de dominio y representación mínima es/en.
- Identity: 11/11 casos en la ejecución del monorepo de esta fase.
- R2.2 seguridad: 11/11, más 50/50 regresiones.
- Typecheck dirigido a los once paquetes con script, excluyendo únicamente vendor: correcto.
- Build servidor/web: correcto; aviso existente de bundle mayor de 500 kB.
- Biome sobre los 54 archivos de código/contratos de esta fase: correcto, sin errores ni avisos.
- ESLint sobre sus TypeScript/TSX: correcto, incluido espaciado.
- Persistencia R2.1b: 13/13 pruebas repetidas, sin omitidas, con retirada del contenedor.
git diff --checky compilación Python de los lanzadores: correctos.
Se ejecutaron además comprobaciones globales. pnpm typecheck sigue fallando en vendor
calendar.tsx:165 (TS2375 locale de DayPicker). No se cambia vendor. Las suites amplias ejecutadas
con credenciales del contenedor, sin cargar .env, mantienen dos fallos anteriores de representación
en Companies y el fallo de Identity HTTP que espera 404 en una ruta archivada y recibe 401 en
identity.test.ts:82. El equivalente seguro usa --monorepo; no se ejecuta el script global que carga
.env contra la base habitual. pnpm lint global devuelve 15 errores, un aviso y cinco informaciones; pnpm check
devuelve 192 errores, un aviso y cinco informaciones en el árbol compartido. El conjunto propio
comprobado por separado queda limpio. Se conserva la deuda y las modificaciones concurrentes
ajenas; no se atribuyen todos los cambios del árbol a esta fase.
Estas limitaciones no se ocultan ni se reparan ampliando el alcance.
23. Limitaciones
Sin concesión delegable, ACL operador–compañía, adopción, equivalencia automática, unicidad semántica, importación masiva, productos sembrados ni licencias globales. No hay total exacto de búsqueda ni snapshot entre páginas. La comprobación visual no sustituye auditoría especializada de accesibilidad ni pruebas de lectores de pantalla. No se establece un SLA de rendimiento con esta fixture.
La operación sobre una instalación habitual requiere backup/preflight y despliegue coordinado de R2.1b conforme a su runbook. Esta entrega no ejecuta esa activación.
24. Condiciones de R2.4/R2.5
Mantener las tres puertas de consumidores cerradas hasta sus fases. Para abrir instalaciones o configuraciones globales se necesita implementar el comando correspondiente y su transacción de asociación, validar propósito/actividad/pertenencia bajo bloqueo de catálogo y compañía, preservar resolución histórica y estados locales, mantener licencias locales y añadir pruebas de concurrencia, reintentos, aislamiento y selector mixto. Que una aplicación pueda leerse en el catálogo o resolverse mínimamente no autoriza asociarla. La consolidación de duplicados requiere la fase y autorización propias de R2.6; no deriva de la apertura de este catálogo.