# R2.2 — Autorización, contratos y resolución segura Fecha: 24 de octubre de 2026. Alcance autorizado por el encargo R2.2, sobre R2.0 ratificado y R2.1a/R2.1b. No se autoriza activar R2.3–R2.5 ni modificar bases habituales. ## 1. Estado y alcance Se incorpora autorización explícita en Identity, contratos estrictos de producto/candidatos/referencias, resolución por propósito y un puerto ligado a la transacción del consumidor. El inventario y los consumidores existentes continúan siendo locales. Las rutas administrativas de producto están registradas con autenticación, autorización y gate cerrado; no tienen comandos de escritura global conectados. El cierre de validación y gates figura en §19–22. Las pruebas de R2.1b son antecedentes; se vuelven a ejecutar los recorridos afectados y se añaden pruebas de esta fase. ## 2. Inspección inicial y fuentes HEAD inicial: `34b8f915381de02f9e13334634455074a5dc55ee`. Node 24.20.0 y PNPM 11.25.0. El árbol ya incluía R2.1a/R2.1b sin commit y cambios concurrentes de frontend, listados, puestos y documentación. Se guardaron estado y huellas en los artefactos; no se revierten cambios ajenos. Se consultaron íntegramente los documentos 01–12 de esta serie y la normativa de capas, composición y HTTP. Se contrastaron sesión/CSRF, bootstrap, repositorios de Identity, catálogo e inventario Resources y las transacciones de software/perfiles. No existe registro genérico de permisos; sí existen `users.isInitialAdmin`, `authVersion` e `identity_state.initialAdminId`. Skill principal: **rdx-solid**, con referencias aplicables de arquitectura, backend, contratos y testing. Influyó en la separación de puertos, proyecciones y pruebas sobre fronteras reales. No hay skills específicas de seguridad/Sequelize instaladas; no se instalaron skills ni bibliotecas externas. Se consultaron DESIGN y la matriz visual por el traslado del helper puro de búsqueda; no se modifican componentes ni interacción visual y no se ejecutó Playwright. ## 3. Decisiones ratificadas aplicadas | Decisión | Aplicación | | --- | --- | | D01/D02 | Pares discriminados company/UUID y global/null; únicamente serie exacta `template:application`, plantilla/campos globales y sin integración. | | D03 | `resources.global.manage` únicamente para la identidad inicial persistida y sesión vigente; guard también en mutaciones globales de campos/plantillas. | | D04/D04-R | Soporte compartido conservado, contexto de compañía explícito y lectores por propósito; ningún bypass administrativo de pertenencia. | | D05/D06 | Proyecciones mínimas separadas, inactivos legibles, nuevas asociaciones requieren actividad. | | D07/D08 | Sin equivalencias por nombre, copias por compañía, transferencia ni reescritura histórica. | | D09/D10 | Routers/schemas nuevos, DTO legacy intacto y puertas independientes cerradas. Puerto transaccional sobre catálogo Resources antes de compañía. | ## 4. Capacidad Identity y administrador inicial `IdentityUseCases.canManageGlobalResources` devuelve Result: 401 conceptual si no hay sesión vigente y 403 si el actor carece de capacidad. Relee usuario activo y `authVersion`, exige `isInitialAdmin` y comprueba que su UUID coincide con `identity_state.initialAdminId`, el marcador persistido por bootstrap bajo transacción. La comprobación adicional del marcador acredita la identidad inicial aun si hubiera otro flag administrativo inconsistente. No depende de correo, nombre, orden de consultas ni UUID fijado en código. No añade grants ni migración de permisos y no cambia usuarios. No hay delegación/revocación por interfaz. La API pública de Identity expone `services.authorization.canManageGlobalResources` y el middleware `requireGlobalResourceManager`. Resources no importa repositorios ni el método privado de administración de Identity. No se cachea autorización entre peticiones; el criterio sigue siendo el inicio de la operación, sin cancelación retroactiva de transacciones ya autorizadas. ## 5. Guard de plantillas y campos `/api/resource-catalog/:scope` mantiene su lectura y contratos. Todos sus POST con scope global requieren el guard de Identity, incluidos alta, update y status de plantillas y campos. El guard usa el parámetro decodificado por Express: `%67lobal` no elude la protección. Las composiciones que omitan el guard fallan cerrado con 403, nunca permiten escrituras globales implícitamente. Overrides y definiciones de compañía conservan su semántica. Un operador ordinario puede establecer/restaurar disponibilidad local; no puede editar una definición global por el path de compañía. La propagación que escribiría un Resource global continúa rechazándose con `RESOURCE_SCOPE_UNSUPPORTED`, incluso después de superar autorización; R2.3 debe completar su implementación antes de abrir producto global. ## 6. Puertos por propósito | Puerto/superficie | Comportamiento R2.2 | | --- | --- | | `IResourceInventoryService.list(company)` | Sin cambios: inventario exclusivamente local; no se añaden scope ni owner nullable al DTO legacy. | | `IResourceReferenceReader.candidates` | Compañía existente y propósito tipado; activos locales application, y OS local solo para software. Globales excluidos por gates. | | `IResourceReferenceReader.resolve` | Lote de identidades mínimas, incluidas aplicaciones globales elegibles e inactivas; no concede capacidad de asociación. | | `IGlobalApplicationReader` | Lectura interna de producto mínimo, lista paginada y detalle con whitelist; no expuesta como catálogo funcional. | | `IResourceReferenceSession` | Resolución y validación de nueva asociación dentro de la Transaction aportada por Infrastructure del consumidor. | No se reutiliza `ResourceReferenceDTO` de Access para aplicaciones. Los propósitos cerrados son `software-installation` y `application-configuration`; remoto, SMB, dominio, certificado, soporte, mail, Access y licencias no adquieren propósito global. ## 7. Matriz implementada de resolución | Referencia en operación A | Lectura software | Lectura perfil | Nueva asociación mediante puerto | | --- | --- | --- | --- | | Serie Aplicación global activa | Resuelta | Resuelta | `RESOURCE_GLOBAL_FEATURE_DISABLED` en R2.2. | | Serie Aplicación global inactiva | Resuelta | Resuelta | `RESOURCE_REFERENCE_UNAVAILABLE`. | | Aplicación local A activa, serie o usage válido | Resuelta | Resuelta | Válida estructuralmente; consumidor conserva sus invariantes. | | Aplicación local A inactiva | Resuelta | Resuelta | `RESOURCE_REFERENCE_UNAVAILABLE`. | | Aplicación local B | Unavailable | Unavailable | `RESOURCE_REFERENCE_NOT_FOUND`. | | OS local A | Resuelto | Unavailable | Solo propósito software, sujeto a actividad. | | Licencia u otro tipo local A | Unavailable como producto | Unavailable | No es una referencia de aplicación/software. | | Global de otra plantilla, copia o campos locales | Inconsistencia interna | Inconsistencia interna | Falla sin ocultar corrupción. | Un UUID ausente, ajeno o de tipo incorrecto produce la misma forma unavailable, sin nombre ni propietario. Una inconsistencia global persistida o fallo técnico no se convierte en unavailable ni en colección vacía. La actividad de la plantilla no sustituye la del Resource para resolver un producto ya existente. ## 8. Contratos HTTP preparados Los schemas nuevos están en `modules/resources/src/common/dto/{shared,request,response}/`, exportados por `./common`, con tipos inferidos. Separan referencias por propósito, candidatos activos, summary/detail de producto y administración/historial. Se rechazan owner/scope, actor, privilegios e integración no declarados. `creationId` exige UUIDv7. El update global conserva templateId en values por el contrato ratificado; el futuro comando deberá contrastarlo con la raíz fija. No existe comando actual que pueda alterarlo. | Operación | Estado público R2.2 | | --- | --- | | GET `/api/resource-global-applications` y `/:id` | Sesión requerida; schema validado y 409 `RESOURCE_GLOBAL_FEATURE_DISABLED`. | | GET `/:id/admin`, `/:id/history` | Sesión y manage; ordinario 403, manager 409 por gate. Sin lectura administrativa conectada. | | POST producto, `/:id/update`, `/:id/status` | CSRF/origen, sesión, manage y schema; manager 409 por gate. Ningún global creado/editado/desactivado públicamente. | | GET `/api/resource-application-candidates/:companyId` | Disponible con sesión; candidatos locales paginados, purpose explícito y validado. | | POST `/api/resource-application-references/:companyId/resolve` | Disponible con sesión y CSRF/origen; resolución mínima por lotes. | `X-Metis-Resources-Contract: 2` se valida si se envía a las dos rutas por propósito. Un valor distinto no concede permisos y se rechaza. La negociación operativa legacy/upgrade de software y perfiles sigue siendo gate de R2.4/R2.5, antes de que exista cualquier asociación global; no se afirma completada en consumidores que permanecen locales. Precisión de fase: los comportamientos finales de G03/G06 con catálogo apagado —lectura administrativa y desactivación de seguridad— se implementarán con los comandos de R2.3. R2.2 cierra toda administración pública de producto por petición expresa. Por ello devuelve el mismo 409 aun para un UUID local o inexistente tras superar manage, sin consultar ni revelar esa raíz. No se sustituye la política final de R2.0. ## 9. Gates de activación `resourceGlobalGates` es inmutable y validado: catálogo, administración, selección mixta, software y perfil son cinco puertas diferenciadas, todas false. La selección requiere su propia puerta y la del consumidor; una asociación global requiere catálogo y consumidor. La validación del puerto aplica esas puertas; la resolución de lectura no las consulta para ocultar usados. La configuración validada del servidor admite las tres variables previstas `RESOURCE_GLOBAL_CATALOG_ENABLED`, `RESOURCE_GLOBAL_SOFTWARE_ENABLED` y `RESOURCE_GLOBAL_PROFILE_ENABLED` únicamente ausentes/false en esta versión. true o cualquier valor inválido impiden arrancar. No hay variable del navegador ni booleano HTTP que active funciones. No hay rutas de conversión, transferencia, delete global ni bypass exportado de escritura global. ## 10. Proyecciones y privacidad El lector consulta metadatos del producto y clasificación; no carga notas, actores ni revisiones. Summary/detail global no usa el DTO scoped completo de R2.1a. Solo publica provider/url/description de campos globales identificados por stockKey y ligados a la plantilla; si faltan, null. Candidatos contienen provider; referencias no incluyen siquiera esos campos de presentación. Las respuestas por propósito son uniones discriminadas: OS no puede aparecer en la rama de perfil. Las proyecciones no contienen instalaciones, personas, activos, licencias, configuraciones, compañías usuarias ni métricas transversales. Los schemas administrativos preparados no incluyen usos de compañías y separan historia paginada de raíz. ## 11. Búsqueda y paginación Se traslada sin cambiar su implementación `normalizeSearchText/createTextMatcher` a `rdx-utils`; `rdx-ui/lib/text-search` conserva su API mediante reexport. Se añade únicamente la relación interna entre esos dos paquetes ya existentes y su enlace en lockfile; PNPM offline no descargó paquetes ni ejecutó scripts. Se conservan trim, minúsculas, acentos vocálicos, ñ, puntuación y espacios interiores. `%` y `_` son texto literal. El servidor carga en una consulta los metadatos autorizados; filtra, ordena por clave normalizada y UUID y después pagina. No hay LIMIT anterior al filtro ni una petición SQL por producto. Cursor acotado y validado contra contexto, propósito, búsqueda/filtros/orden; no acredita acceso. La consulta original permanece en el cliente, sin alteración de sus formularios ni estado. No se promete snapshot entre páginas bajo renombrado concurrente. La primera estrategia recorre metadatos elegibles y exige medición antes de escalar; no se añaden índices por intuición. ## 12. Integración y pertenencia La composición real del servidor inyecta el guard público de Identity a Resources. Resources exporta el puerto de referencia mediante `./services` y una factoría en `services.applicationReferences(transaction, company)`; no exporta modelos/repositorios. Infrastructure del futuro consumidor aportará Transaction; Application/Domain no la importan. No hay dependencia circular nueva entre Identity, Companies, Assets y Resources. Los repositorios actuales de SoftwareInstallation/ApplicationConfiguration conservan su inventario local. Un manager no obtiene otro conjunto de referencias que un operador dentro de A: las pruebas HTTP intentan locales B, globales, inactivos y licencias en ambos consumidores con ambos sujetos. No se introduce ACL operador–compañía; abrir B expresamente sigue siendo una operación de B bajo el soporte compartido. ## 13. Consistencia transaccional y límites TOCTOU La factoría debe ser la primera operación del consumidor dentro de su transacción: adquiere la raíz existente de catálogo Resources con FOR UPDATE y después compañía con FOR UPDATE. La resolución ligada usa lectura bloqueante actual de metadatos y mantiene la transacción del caller; no inicia otra ni hace commit. La serialización es la misma que utilizan las mutaciones de Resource/plantilla existentes. Si interviene Access, el adaptador futuro debe integrar su orden completo catálogo Resources → Access global → compañías → workspace → Resource → consumidor, antes de activar. Se verifica con dos transacciones: la segunda no adquiere catálogo mientras la primera valida y escribe una asociación local de fixture, y obtiene el timeout de lock acotado del motor. No hay sleep para suponer bloqueo. Tras commit y desactivación, una validación posterior rechaza; la resolución de lectura conserva la identidad inactiva. Esta prueba acredita la unidad ligada y su serialización, no una garantía global de todos los adaptadores actuales. **Gate obligatorio R2.4/R2.5:** integrar el puerto en los repositorios reales de consumidores, antes de tomar locks propios y dentro de la misma escritura, incluyendo licencia y estado operativo. Hoy sus llamadas a `inventory.list` siguen abriendo otra transacción. No se declara reparado ese TOCTOU local heredado ni habilitada una asociación global. Al estar prohibidas todas las escrituras globales, esa dependencia no abre una nueva ruta insegura en R2.2. Medir contención y ensayar ambos órdenes con los adaptadores finales antes de activar. ## 14. Inmutabilidad y conservación Scope/owner no entran en nuevos payloads globales; permanecen fijos en agregado y escritor relacional de R2.1b. Plantilla y destino de consumidor no se cambian mediante conversión. No hay DDL nuevo, cambios de UUID, backfill, revisiones automáticas, fusión por nombre, globalización de licencias ni cambios de ámbito operativo. Los globales se crean únicamente mediante fixture SQL interna con identidades/revisiones independientes en el contenedor desechable. ## 15. Pruebas unitarias Identity prueba capacidad con sesión ausente/inválida, identidad bootstrap, flag aislado, usuario inactivo y cambio de authVersion sin caché. Resources añade elegibilidad de serie/campos, schemas correlacionados por propósito, pares inválidos, proyecciones privadas rechazadas, límites de lotes y búsqueda/cursor literal/contextual. Las suites existentes de dominio/lectores/UI local y el helper compartido siguen siendo pruebas de compatibilidad; sus resultados concretos se registran en §19. ## 16. MariaDB y volumen Lanzador reproducible: `pnpm test:resource-security`, equivalente a `python3 scripts/resource-scope-test.py --security --regressions`. Crea MariaDB 11.8 por digest fijado, credenciales aleatorias en archivo privado efímero, puerto localhost y exclusivamente `metis_desk_test`. No carga `.env` ni conecta una instancia existente; retira contenedor y archivos privados al acabar. No deshabilita CHECK/FKs. La suite R2.2 prepara 15 compañías nuevas, principal con 50 personas, 50 activos, 50 perfiles, 50 puestos y 53 recursos locales (50 aplicaciones, OS, licencia y SMB); las otras 14 aportan aplicación local. Se clonan 30 globales sintéticos válidos, activos e inactivos, con UUID e historia propios. Son datos de laboratorio, no importaciones reales. La infraestructura de prueba aplica la migración R2.1b explícitamente sobre ese esquema. ## 17. Pruebas HTTP y seguridad La suite usa módulos y middleware reales con sesión ordinaria de fixture y administrador creado por el bootstrap HTTP real. Verifica 401/403, CSRF/origen, permiso por identidad, ausencia de elevación por body y flag persistido ajeno al marcador, authVersion invalidada, guard de plantillas/URL codificada, override local, gates cerrados, referencias cruzadas también para admin y consumidores locales. Se prueban lotes de 100 y límites inválidos, orden y unavailable uniforme; candidatos locales con búsqueda acentuada y puntuación, paginación sin repetidos y cursor ajeno rechazado; whitelist de producto y exclusión de notas/historia; global con plantilla inválida y fallo SQL real tratados como error. La fixture instala un manejador de errores saneado para ese último caso; formato/requestId y diagnóstico común de producción se cubren además en las regresiones HTTP existentes. No se declara un pentest ni cobertura visual. ## 18. Consultas y rendimiento observado `reference-query-evidence.json` conserva una medición de candidatos, bytes de respuesta, número de consultas de metadatos y EXPLAIN del SQL real instrumentado. La consulta única incluye subconsultas correlacionadas indexables para campos aprobados; no equivale a afirmar coste constante por catálogo. La cifra de tiempo corresponde a una ejecución pequeña y no fija un SLA ni mejora porcentual. No se crean índices ni columnas de búsqueda derivadas. La consulta combina los conjuntos disjuntos propietario A y propietario NULL mediante OR en una sola lectura de raíces, en lugar de materializar un UNION ALL: mantiene el filtro de pertenencia anterior a la proyección/paginación y no duplica UUID. La discriminación de ámbito y la elegibilidad se validan antes de construir cada referencia. Es una precisión de implementación SQL, sin ampliación del conjunto autorizado por R2.0. ## 19. Resultados de validación | Comando / ámbito ejecutado | Resultado | | --- | --- | | `python3 scripts/resource-scope-test.py --security --regressions` | **11/11 casos de R2.2 y 50/50 regresiones** MariaDB/HTTP, cero omitidos. La ejecución ampliada anterior tenía 10 casos; se añadió después la prueba SQL de campos locales y copia de plantilla. | | `python3 scripts/resource-scope-test.py` | **13/13 pruebas de persistencia R2.1b** vueltas a ejecutar en esta fase: preservación, cuatro interrupciones, CHECK/FKs y restauración integral. | | `pnpm --filter @metis/identity test` y la misma lista con `node --test --test-isolation=none` | Correcto; ejecución detallada **11/11 casos**. | | `pnpm --filter @metis/resources test` y la misma lista sin aislamiento por fichero | Correcto; ejecución detallada **95/95 casos**. El recuento incluye pruebas concurrentes presentes en el árbol, sin atribuirlas a esta fase. | | `pnpm -r --workspace-concurrency=3 --filter='!@repo/shadcn-ui' typecheck` | Correcto en los **11 paquetes con script**, incluidos Identity, Resources, Companies, Assets, servidor y web. | | `pnpm exec biome check` sobre los 35 archivos TypeScript/package de esta fase | Correcto, cero errores/avisos. | | `pnpm exec eslint` sobre los 31 TypeScript de esta fase | Correcto, incluido espaciado. | | `pnpm build` | Correcto, servidor y web; aviso habitual de bundle web mayor de 500 kB. | | `git diff --check` dirigido y compilación Python del lanzador | Correctos. | | `pnpm typecheck` global | Falla en vendor, `calendar.tsx:165`, TS2375 de locale/DayPicker con exactOptionalPropertyTypes; vendor conserva las huellas iniciales. | | `pnpm lint` global | 15 errores, 1 warning y 5 infos fuera del cambio dirigido. | | `pnpm check` global, ejecución de cierre registrada | 170 errores, 1 warning y 5 infos; incluye formato ajeno. No se aplica formato global. | | `python3 scripts/resource-scope-test.py --security --regressions --monorepo` | Seguridad y 50 regresiones correctas; auditoría ampliada devuelve error por los dos ámbitos siguientes. | | `pnpm exec turbo run test --filter=!@metis/server` dentro del contenedor | Pasa Identity 11, Resources 95, Assets 146, rdx-ui 78, Core 11, DDD 3 y Relationship Contracts 13. Companies pasa **239/241** y falla en las dos assertions de render ya documentadas; no concluye la ejecución global de todas las apps. | | Suite amplia `apps/server/src/identity.test.ts`, con credenciales desechables explícitas | Falla en la comprobación inicial de rutas archivadas de línea 82: esperaba 404 y recibe 401. No alcanza el resto de su recorrido. | El script literal global `pnpm test` no se ejecuta porque su script de servidor carga `.env` y ejecuta preparación persistente. Se separan los tests de paquetes y servidor con credenciales explícitas desechables, sin ese archivo. No se presenta el monorepo como globalmente correcto. Las dos assertions de Companies son «Personas (1)» y «Esta persona no tiene acceso a Metis Desk», en el archivo de prueba intacto respecto al baseline de esta fase. El fallo inicial de la suite amplia de Identity ya se reprodujo en R2.1b y se ha vuelto a observar. Se conservan estos fallos ajenos, sin adaptar expectativas ni corregir rutas retiradas. No se declara ejecutado su recorrido posterior, incluido cualquier flujo HTTP de mail/remoto que dependa de él; sus contratos no se han ampliado y la evidencia de compatibilidad se limita a las suites efectivamente ejecutadas. Incidencias nuevas corregidas durante el desarrollo: el enlace interno necesario para el helper compartido, el acceso no público `Sequelize.options` de la instrumentación de pruebas (sustituido por logging del constructor público) y las reglas de formato/espaciado. La primera ejecución Resources dentro del sandbox no pudo completar su prueba HTTP; la repetición autorizada con puerto efímero pasó. No se omite ninguna prueba propia para sortear restricciones. ## 20. Archivos principales y evidencias | Área | Archivos relativos al repositorio | | --- | --- | | Identity | `identity.use-cases.ts`, `identity-ports.ts`, `sequelize-user.repository.ts`, `require-global-resource-manager.ts`, module y nueva prueba de autorización bajo `modules/identity/src/api`. | | Contratos Resources | `modules/resources/src/common/dto/{shared,request,response}/` e índice. | | Políticas/puertos | `global-application-template-policy.ts`, `resource-global-gates.ts`, `resource-reference-service.ts`, `resource-reference-pagination.ts` y `resource-reference-contracts.test.ts` bajo Resources Application. | | Adaptadores | `resource-reference-metadata.ts`, `resource-reference-reader.ts`, `resource-application.routes.ts`, guard en `resource-catalog.routes.ts`, module/services de Resources. | | Composición/configuración | `apps/server/src/config/database.ts`, `infrastructure/http/identity-runtime.ts`. | | Pruebas | `resource-authorization.integration.test.ts`, `resource-global.fixture.ts`, fixtures compartidas de pertenencias/HTTP y ajuste explícito de expectativa 403 de R2.1b ante el nuevo guard. | | Búsqueda | `packages/rdx-utils/src/text-search.ts`, índices/reexport y enlace interno de rdx-ui/lockfile. | | Comandos | package.json raíz, scripts de prueba de Identity/Resources y opción `--security` del lanzador existente. | Evidencias locales excluidas de Git en `.artifacts/resources-global-scope/r2-2/`: estado/huellas iniciales, integration/regressions, resultados de seguridad, consulta/EXPLAIN, comandos y logs de calidad. Sin cookies, contraseñas, sesiones ni backups de datos. No se modifican vendor, archive, los doce documentos históricos de auditoría ni las decisiones ratificadas. ## 21. Limitaciones y trabajo diferido No hay UI nueva, administración funcional, alta/edición/status/historial público global, selectores mixtos ni escrituras globales en software/perfiles. La lectura pública del catálogo también queda cerrada hasta R2.3; solo existe el lector interno mínimo y la resolución contextual. No se habilita `needsReview` nuevo ni delegación de permisos. R2.3 deberá implementar comandos globales, validación dinámica de plantilla/campos bajo lock, propagación autorizada, idempotencia de creationId y lectura administrativa/historial, con desactivación de seguridad disponible aun con catálogo off según el contrato final. Antes de abrir catálogo debe superar esos gates; la autorización de esta fase no autoriza activación. R2.4/R2.5 requieren además las integraciones transaccionales y negociación/lectores secundarios descritos en §13. No se extrapola el ensayo a una base habitual ni al volumen de producción. ## 22. Gates de aceptación | Gate | Estado y evidencia | | --- | --- | | G1 — Identity | Cumplido: capacidad explícita por identidad bootstrap y sesión vigente, sin delegación; 11 casos unitarios y pruebas HTTP de denegación/invalidación. | | G2 — Resolución | Cumplido para el puerto de R2.2: ámbitos/tipos/plantilla/campos/actividad/contexto comprobados, histórico mínimo conservado y unidad transaccional ensayada. Integración de consumidores finales obligatoria antes de R2.4/R2.5. | | G3 — Contratos | Cumplido: schemas estrictos y correlacionados, DTO mínimos y legacy exacto; APIs de producto preparadas y cerradas, candidatos/referencias contextuales probados. | | G4 — Activación | Cumplido: administración, creación, selección mixta y asociaciones globales cerradas; configuración true rechazada y ningún writer público global conectado. | | G5 — Compatibilidad | Cumplido para el cambio: inventario/CRUD local y software/perfiles locales comprobados, persistencia histórica conservada en las 13 pruebas reejecutadas y 50 regresiones dirigidas correctas. No se atribuye cobertura HTTP no alcanzada por la suite amplia fallida. | | G6 — Pruebas | Cumplido en el alcance R2.2: suites propias, typecheck dirigido, calidad dirigida y build correctos. Los fallos globales ajenos están descritos en §19, sin presentarlos como checks verdes. | **R2.2 queda implementada y validada en su alcance de preparación segura, con activación funcional cerrada.** No autoriza desplegar/migrar una instalación habitual ni habilitar R2.3. La integración transaccional completa de software/perfiles, administración funcional y lectores/clientes de esas fases mantienen sus gates posteriores explícitos.