metis-desk/docs/architecture/resources-global-scope/10-resumen-para-aprobacion.md

235 lines
30 KiB
Markdown
Raw Normal View History

2026-10-24 23:19:17 +00:00
# R2.0 — Resumen para aprobación
> **Ratificación posterior — 24 de octubre de 2026:** D01–D10 y D04-R ratificadas en los términos recomendados de R2.0; variante A, Resource único con `scope` explícito. Solo se autoriza implementar R2.1a. El registro vigente de alcance y evidencias está en [11 — Lectores compatibles](11-r2-1a-lectores-compatibles.md). Los estados «propuesto», «recomendado» y «pendiente» del cuerpo se conservan como evidencia de la versión documental inicial y quedan actualizados por este registro; ratificación conceptual no equivale a ejecución de R2.1b–R2.6.
**Estado: contrato completo propuesto, pendiente de ratificación. No aprobado automáticamente.** R2.0 termina con diez documentos; no implementa ni autoriza R2.1–R2.6. Los principios expresos del usuario se marcan aprobados como restricciones, no como aprobación del diseño técnico completo.
## 1. Decisiones recomendadas
| Decisión | Recomendación para ratificar | Consecuencia principal |
| --- | --- | --- |
| D01 | Resource único con scope explícito global/company y owner NULL/UUID, CHECK, propiedad/plantilla inmutables | Cambia raíz/persistencia y nuevos DTO; legacy local queda intacto |
| D02 | Solo serie Aplicación stockKey template:application, campos globales, sin integración | Regla inicial **ya establecida por el usuario**; otras plantillas excluidas |
| D03 | Capacidad conceptual resources.global.manage solo para administrador inicial, comprobada desde Identity en cada operación | Restricción nueva para producto e igualmente para mutaciones globales de plantillas/campos Resources; sin grants delegables |
| D04 | Mantener explícitamente el modelo de soporte compartido actual, sin simular ACL operador–compañía | Contexto de operación A nunca acepta local B, aunque el operador pueda abrir B por separado |
| D04-R | Mantener list/CRUD locales y añadir lecturas por propósito para catálogo/candidatos/referencias | Separación **ya requerida**; firmas y proyecciones concretas recomendadas |
| D05 | Producto mínimo para soporte, notes/especificación completa/historia de actores solo para manager | No exponer compañías usuarias, instalaciones, perfiles, licencias o datos operativos ajenos |
| D06 | Solo active existente; inactivo resoluble, fuera de nuevas asociaciones; plantilla inactiva solo bloquea alta Resource | needsReview derivado en instalación/perfil abiertos, sin campo universal ni DML masivo |
| D07 | UUID como identidad, homónimos permitidos global/local, avisos de coincidencia sin equivalencia; replay por creationId UUIDv7 | No unique global por nombre; reglas locales legacy intactas; sin tabla extra de idempotencia |
| D09 | Routers/DTO nuevos del mismo módulo; legacy exacto; proyección mínima/lotes; negociación explícita de cliente y keys por contexto | Paths, schemas, header y límites detallados en 04 son propuestas, no funcionalidades existentes |
| D10 | Lectores duales antes de DDL, expansión ensayada y gates por catálogo/software/perfil | No primer global con backend incompatible ni downgrade tras uso |
Convivencia/no fusión automática, licencias locales, UUID estable, validación en servidor, no compañía ficticia y preservación de referencias históricas son decisiones de partida aprobadas por el usuario. Los detalles técnicos de esta tabla siguen pendientes de ratificación salvo los alcances expresamente marcados. No se pregunta a mitad del trabajo por estas opciones: el contrato revisable permite decidirlas después.
## 2. Decisiones bloqueadas y condiciones de bloqueo
No se ha encontrado una incompatibilidad que impida **proponer** Resources global sobre el modelo actual. Quedan bloqueadas las siguientes ejecuciones, sin ocultarlas como «detalle de implementación»:
- **Inicio R2.1:** falta ratificación explícita de D01 y autorización del trabajo de implementación; antes de R2.1b además falta ensayo del esquema efectivo/version patch de MariaDB 11.8, FKs y CHECK. La preferencia por scope explícito no es aprobación.
- **Activación global:** no existe hoy permiso manage ni está aprobada su asignación. D03/D04/D05 deben ratificarse e implementarse/probarse antes de G3b.
- **Variante con administradores delegables:** BLOQUEADA si se exige desde primera versión, porque Identity no dispone de contrato de grant/revoke ni persistencia de permisos. La propuesta admin-inicial-only evita añadir ese alcance; no lo simula.
- **Variante de ACL estricta operador–compañía:** BLOQUEADA para activación si ese es el requisito de negocio. La aplicación documenta soporte compartido, no esa ACL; debe diseñarse por separado antes de afirmar aislamiento de operador.
- **Consolidación que modifique vínculos:** BLOQUEADA hasta ADR de sucesión y auditoría/reversión. El destino operativo fijo impide usar update actual para sustituir resourceId.
La política admin inicial, el modelo compartido y la privacidad son decisiones humanas de negocio. Las condiciones de SQL y pruebas son evidencias técnicas futuras que no se sustituyen por aprobación documental.
## 3. Decisiones diferidas
D08 consolidación con mutación; grants/revokes delegables; ocultación de un producto global por compañía; otras plantillas globales; campos de categoría/familia/identificador externo; historial público de producto; borrado físico global; vista transversal de uso de compañías. Se puede completar primera versión y R2.6 de coexistencia sin estas extensiones. No son funcionalidades prometidas ni migraciones ocultas de R2.1.
## 4. Riesgos críticos
- Ensanchar indiscriminadamente IResourceInventoryService.list permitiría destinos globales en remoto y afectaría consumidores operativos que no deben compartirlos. La matriz 06 fija cada propósito, incluido los que siguen solo locales.
- **Crítico condicionado:** confundir autenticación del espacio compartido con ACL por compañía. Si el despliegue requiere operadores aislados, el contrato de soporte compartido no satisface ese requisito.
- Autorizar al administrador global como bypass de pertenencia rompería integridad: incluso manager no puede vincular local B a instalación/perfil de A.
## 5. Riesgos altos
Mutaciones globales de plantillas hoy no restringidas; DTO/mapper no nullable y strict legacy; FKs compuestas/DDL con commit implícito; propagación que presupone companyId; actividad validada en otra transacción; exposición de notas/actores; clientes/caché desactualizados; vuelta a lectores que no entienden globales. Se exige serialización común conservadora, proyecciones distintas y activación controlada; no se atribuye probabilidad numérica ni se afirma vulnerabilidad explotada.
## 6. Dependencias y orden
R2.1a prepara lectores sin DDL → R2.1b expande esquema/backfill → R2.2 aporta seguridad/contratos/resolución en transacción → R2.3 catálogo → R2.4 software y R2.5 perfiles con lectores secundarios → R2.6 coexistencia/avisos. Activación de software y perfil puede separarse; compartir composición/contratos no fusiona agregados. El registro de relaciones tipadas y consumidores SMB/directorio/Access no adquiere ámbito global.
## 7. Condiciones para comenzar R2.1
Ratificar D01 y constatar D02 restringida, aceptar subetapas de lectores/DDL y alcance excluido, autorizar expresamente la implementación posterior y preparar baseline/test aislado. R2.1a puede prepararse sin cerrar grants ni UI; R2.1b necesita preflight SQL documentado y recuperación ensayada. No se necesita poblar producción ni tener catálogo global abierto.
## 8. Condiciones para habilitar globales
Todos los lectores/propagaciones/backend compatibles; esquema completo con CHECK/FKs ON y backfill íntegro; autorización global y política operador/contexto aprobadas; proyecciones/paths y privacidad aceptadas; pruebas de seguridad, serialización, idempotencia y recuperación pasadas; catálogo y UI con estados/teclado comprobados. Para asociaciones, además lectores operativos/históricos y clientes compatibles y flags específicos. El primer global ya impide downgrade trivial; las referencias globales obligan a mantener resolución aunque se apaguen flags.
## 9. Fuera del alcance de R2.0
Cualquier código funcional, schema/DTO existente modificado, migración/DDL, permiso concedido, componente/hook/i18n/dependencia/configuración de despliegue alterada, pruebas HTTP/BD, siembra, catálogo implementado, transferencia de recursos, licencias globales o consolidación ejecutada. Tampoco se ejecuta el plan de pruebas futuro ni se valida una UI inexistente en Playwright.
## 10. Ratificación humana necesaria
Ratificar los detalles de D01, D03, D04, D05, D06, D07, D09 y D10 según registro 02; D02 inicial y vetos de integridad ya están establecidos. D08 no requiere aprobar mutaciones para lanzar coexistencia: permanece diferida. Una aprobación posterior debe identificar versión del contrato, decisiones aceptadas y variantes rechazadas. No se registra aprobación por silencio, por tiempo transcurrido ni porque el asistente recomiende una opción.
## Índice de los diez entregables
1. [Contrato arquitectónico e invariantes](01-contrato-arquitectonico.md).
2. [Decisiones D01–D10 y correspondencia D04](02-decisiones-d01-d10.md).
3. [Seguridad, privacidad y escenarios S01–S12](03-contrato-seguridad.md).
4. [API, DTO, errores, búsqueda y UX](04-contratos-api-dto.md).
5. [Persistencia, MariaDB y unidad de consistencia](05-contrato-persistencia.md).
6. [Matrices completas de consumidores](06-matriz-consumidores.md).
7. [Despliegue, compatibilidad y rollback](07-plan-despliegue.md).
8. [Pruebas e invariantes/riesgos trazados](08-plan-pruebas.md).
9. [Implementación R2.1–R2.6, alcance y aceptación](09-plan-implementacion-r2.md).
10. Este resumen para revisar/ratificar.
## Evidencia y comprobación R2.0
Se leyeron íntegramente los doce documentos de auditoría y se contrastaron las decisiones con código actual. HEAD fa9d95fa9e227a66a45e278d98e2895d8a9995ad; Node v24.20.0 y PNPM 11.25.0 coinciden con package.json. Las 99 huellas de fuentes citadas por auditoría 12 coincidían al inicio; durante el trabajo se detectaron cambios concurrentes, con 97 iguales y dos fuentes web modificadas en la revisión de cierre. Estas dos modificaciones afectan búsqueda local (createTextMatcher), no ámbito/permisos/backend. Se releyeron y las anclas se recalcularon sobre el contenido actual; discrepancia C09 y contrato de búsqueda revisado en 01/04. No se atribuyen esos cambios a R2.0 ni se modifican. Se ampliaron lecturas de Identity/User/module, ambos builders/mappers, SQL/constraints, proyecciones y componentes/patrones compactos. Los conflictos documentales y diferencias temporales C01–C09 están resueltos expresamente en 01; no se sobrescribió la auditoría histórica.
Esta tarea solo escribe diez Markdown nuevos bajo esta carpeta y conserva íntegros los doce de auditoría. Otros cambios de código/documentación aparecidos en el árbol compartido son concurrentes y ajenos a esta entrega; se inspeccionan cuando afectan evidencia, sin editarlos ni revertirlos. Scripts de comprobación temporales en /tmp. No se ejecutaron pnpm typecheck/lint/test/check/build ni suites funcionales: no hay código cambiado y los resultados anteriores no se reutilizan como validación actual. No se conectó BD ni se ejecutaron migraciones, datos sintéticos o Playwright. Diseño/UI se apoya en normas y skills ya consultadas, sin auditores especializados ni certificación visual.
La comprobación documental debe validar enlaces/archivo/ancla de fuente, D01–D10, INV-01–INV-26 y T01–T26, escenarios y propósitos de matrices, operaciones de API, gates y alcance permitido de cada fase. Se conservan evidencias de símbolos en el apéndice siguiente para repetir la comprobación contra el árbol actual. Las referencias futuras de tipos/paths propuestas no se confunden con símbolos/rutas ya existentes.
## Evidencias de código contrastadas
Todas las entradas son código existente; los nombres propuestos en 04 no se añaden a esta lista.
| Clave | Archivo/línea | Expresión anclada |
| --- | --- | --- |
| E-accessFK | [modules/companies/src/api/infrastructure/persistence/access-relational.ts](../../../modules/companies/src/api/infrastructure/persistence/access-relational.ts#L32) | `export const accessColumns` |
| E-admin | [modules/identity/src/api/application/identity.use-cases.ts](../../../modules/identity/src/api/application/identity.use-cases.ts#L75) | `private async administrator` |
| E-appResponse | [modules/companies/src/api/application/application-configuration-response.builder.ts](../../../modules/companies/src/api/application/application-configuration-response.builder.ts#L7) | `export function buildApplicationResponse` |
| E-applicationDTO | [modules/companies/src/common/application-configurations.ts](../../../modules/companies/src/common/application-configurations.ts#L20) | `export const ApplicationConfigurationSchema` |
| E-applicationFK | [modules/companies/src/api/infrastructure/persistence/configuration-snapshots.ts](../../../modules/companies/src/api/infrastructure/persistence/configuration-snapshots.ts#L57) | `export const applicationSnapshot` |
| E-applicationMapper | [modules/companies/src/api/application/application-configuration-input.mapper.ts](../../../modules/companies/src/api/application/application-configuration-input.mapper.ts#L11) | `export function mapApplicationInput` |
| E-applicationPolicy | [modules/companies/src/api/application/application-configuration-policy.ts](../../../modules/companies/src/api/application/application-configuration-policy.ts#L5) | `export function applicationReferences` |
| E-applicationRead | [modules/companies/src/api/infrastructure/persistence/sequelize-application-configuration.repository.ts](../../../modules/companies/src/api/infrastructure/persistence/sequelize-application-configuration.repository.ts#L34) | `private async load` |
| E-auth | [modules/identity/src/api/infrastructure/http/require-authenticated.ts](../../../modules/identity/src/api/infrastructure/http/require-authenticated.ts#L6) | `export function requireAuthenticated` |
| E-capture | [modules/resources/src/api/application/resource-input.mapper.ts](../../../modules/resources/src/api/application/resource-input.mapper.ts#L69) | `export function captureResourceSpecification` |
| E-catalogDTO | [modules/resources/src/common/index.ts](../../../modules/resources/src/common/index.ts#L19) | `export const CatalogItemSchema` |
| E-catalogRoutes | [modules/resources/src/api/infrastructure/http/resource-catalog.routes.ts](../../../modules/resources/src/api/infrastructure/http/resource-catalog.routes.ts#L65) | `export function registerResourceCatalogRoutes` |
| E-catalogSQL | [modules/resources/src/api/infrastructure/persistence/resource-catalog.model.ts](../../../modules/resources/src/api/infrastructure/persistence/resource-catalog.model.ts#L48) | `export async function migrateResourceCatalog` |
| E-certificateFK | [modules/companies/src/api/infrastructure/persistence/configuration-snapshots.ts](../../../modules/companies/src/api/infrastructure/persistence/configuration-snapshots.ts#L80) | `export const certificateSnapshot` |
| E-compact | [packages/rdx-ui/src/components/compact-table.tsx](../../../packages/rdx-ui/src/components/compact-table.tsx#L26) | `export function CompactTable` |
| E-config | [apps/server/src/config/database.ts](../../../apps/server/src/config/database.ts#L15) | `export function readDatabaseEnvironment` |
| E-csrf | [modules/identity/src/api/infrastructure/http/identity.routes.ts](../../../modules/identity/src/api/infrastructure/http/identity.routes.ts#L105) | `request.get("origin") !== origin` |
| E-dbVersion | [compose.yaml](../../../compose.yaml#L4) | `image: mariadb:11.8` |
| E-directoryRead | [modules/resources/src/api/infrastructure/persistence/directory-domain-reader.ts](../../../modules/resources/src/api/infrastructure/persistence/directory-domain-reader.ts#L19) | `export class DirectoryDomainReader` |
| E-directorySQL | [modules/resources/src/api/infrastructure/persistence/directory-domain-migration.ts](../../../modules/resources/src/api/infrastructure/persistence/directory-domain-migration.ts#L5) | `export async function migrateDirectoryDomains` |
| E-dto | [modules/resources/src/common/inventory.ts](../../../modules/resources/src/common/inventory.ts#L35) | `export const ResourceSchema` |
| E-folderPolicy | [modules/companies/src/api/application/folder-mapping-policy.ts](../../../modules/companies/src/api/application/folder-mapping-policy.ts#L7) | `export function folderReferences` |
| E-history | [modules/resources/src/api/infrastructure/persistence/resource-revision-specifications.ts](../../../modules/resources/src/api/infrastructure/persistence/resource-revision-specifications.ts#L39) | `export async function migrateResourceRevisionSpecifications` |
| E-identityModule | [modules/identity/src/api/module.ts](../../../modules/identity/src/api/module.ts#L19) | `export async function createIdentityModule` |
| E-integrationApplication | [apps/server/src/applications.integration.ts](../../../apps/server/src/applications.integration.ts#L18) | `export async function` |
| E-integrationResource | [apps/server/src/resources.integration.ts](../../../apps/server/src/resources.integration.ts#L19) | `export async function` |
| E-integrationSoftware | [apps/server/src/software-installations.integration.ts](../../../apps/server/src/software-installations.integration.ts#L17) | `export async function` |
| E-invalidation | [modules/resources/src/web/resources/shared/api/resource-related-query-keys.ts](../../../modules/resources/src/web/resources/shared/api/resource-related-query-keys.ts#L4) | `export function resourceRelatedQueryKeys` |
| E-inventoryAPI | [modules/resources/src/web/resources/shared/api/inventory-api.ts](../../../modules/resources/src/web/resources/shared/api/inventory-api.ts#L11) | `export const inventoryKeys` |
| E-mapper | [modules/resources/src/api/infrastructure/persistence/resource-persistence.mapper.ts](../../../modules/resources/src/api/infrastructure/persistence/resource-persistence.mapper.ts#L24) | `export class ResourcePersistenceMapper` |
| E-module | [modules/resources/src/api/module.ts](../../../modules/resources/src/api/module.ts#L39) | `export async function createResourceModule` |
| E-name | [packages/rdx-ddd/src/value-objects/name.vo.ts](../../../packages/rdx-ddd/src/value-objects/name.vo.ts#L5) | `export class Name` |
| E-owner | [modules/resources/src/api/domain/resource.aggregate.ts](../../../modules/resources/src/api/domain/resource.aggregate.ts#L11) | `companyId: UniqueID;` |
| E-picker | [packages/rdx-ui/src/components/option-picker-list.tsx](../../../packages/rdx-ui/src/components/option-picker-list.tsx#L7) | `export function OptionPickerList` |
| E-preview | [packages/rdx-ui/src/components/collection-preview.tsx](../../../packages/rdx-ui/src/components/collection-preview.tsx#L6) | `export function CollectionPreview` |
| E-processRead | [modules/companies/src/api/infrastructure/persistence/sequelize-process.repository.ts](../../../modules/companies/src/api/infrastructure/persistence/sequelize-process.repository.ts#L109) | `await this.resources.list(company)` |
| E-projection | [modules/resources/src/api/infrastructure/persistence/resource-compatibility.ts](../../../modules/resources/src/api/infrastructure/persistence/resource-compatibility.ts#L13) | `export async function projectResource` |
| E-propagation | [modules/resources/src/api/infrastructure/persistence/prepare-resource-template-updates.ts](../../../modules/resources/src/api/infrastructure/persistence/prepare-resource-template-updates.ts#L15) | `export async function prepareResourceTemplateUpdates` |
| E-read | [modules/resources/src/api/infrastructure/persistence/resource-inventory-relational.ts](../../../modules/resources/src/api/infrastructure/persistence/resource-inventory-relational.ts#L195) | `export async function readInventory` |
| E-relationshipAuth | [apps/server/src/infrastructure/http/relationship-read-access.ts](../../../apps/server/src/infrastructure/http/relationship-read-access.ts#L7) | `export class SessionRelationshipReadAccess` |
| E-remoteFK | [modules/companies/src/api/infrastructure/persistence/mail-remote-snapshots.ts](../../../modules/companies/src/api/infrastructure/persistence/mail-remote-snapshots.ts#L83) | `export const remoteSnapshot` |
| E-remotePolicy | [modules/companies/src/api/application/remote-connection-policy.ts](../../../modules/companies/src/api/application/remote-connection-policy.ts#L6) | `export function remoteReferences` |
| E-resourceDetail | [modules/resources/src/web/resources/detail/ui/resource-detail.tsx](../../../modules/resources/src/web/resources/detail/ui/resource-detail.tsx#L11) | `export function ResourceDetail` |
| E-routes | [modules/resources/src/api/infrastructure/http/resource.routes.ts](../../../modules/resources/src/api/infrastructure/http/resource.routes.ts#L54) | `export function registerResourceRoutes` |
| E-runtime | [apps/server/src/infrastructure/http/identity-runtime.ts](../../../apps/server/src/infrastructure/http/identity-runtime.ts#L17) | `export async function createIdentityRuntime` |
| E-save | [modules/resources/src/api/application/resource.use-cases.ts](../../../modules/resources/src/api/application/resource.use-cases.ts#L64) | ` save(` |
| E-seed | [modules/resources/src/api/domain/resource-catalog-seed.ts](../../../modules/resources/src/api/domain/resource-catalog-seed.ts#L140) | `const TEMPLATES` |
| E-select | [packages/rdx-ui/src/components/select-with-action.tsx](../../../packages/rdx-ui/src/components/select-with-action.tsx#L19) | `export function SelectWithAction` |
| E-service | [modules/resources/src/api/application/resource-inventory.service.ts](../../../modules/resources/src/api/application/resource-inventory.service.ts#L7) | `export interface IResourceInventoryService` |
| E-shareRead | [modules/resources/src/api/infrastructure/persistence/network-share-reader.ts](../../../modules/resources/src/api/infrastructure/persistence/network-share-reader.ts#L22) | `export class NetworkShareReader` |
| E-shareSQL | [modules/resources/src/api/infrastructure/persistence/network-share-migration.ts](../../../modules/resources/src/api/infrastructure/persistence/network-share-migration.ts#L4) | `export async function migrateNetworkShares` |
| E-softwareDTO | [modules/companies/src/common/software-installations.ts](../../../modules/companies/src/common/software-installations.ts#L19) | `export const SoftwareInstallationSchema` |
| E-softwareFK | [modules/companies/src/api/infrastructure/persistence/software-installation-snapshot.ts](../../../modules/companies/src/api/infrastructure/persistence/software-installation-snapshot.ts#L7) | `export const softwareSnapshot` |
| E-softwareInput | [modules/companies/src/api/domain/software-installation-values.ts](../../../modules/companies/src/api/domain/software-installation-values.ts#L5) | `export const SoftwareInstallationInputSchema` |
| E-softwareMapper | [modules/companies/src/api/application/software-installation-input.mapper.ts](../../../modules/companies/src/api/application/software-installation-input.mapper.ts#L15) | `export function mapSoftwareInput` |
| E-softwarePolicy | [modules/companies/src/api/application/software-installation-policy.ts](../../../modules/companies/src/api/application/software-installation-policy.ts#L5) | `export function softwareReferences` |
| E-softwareRead | [modules/companies/src/api/infrastructure/persistence/sequelize-software-installation.repository.ts](../../../modules/companies/src/api/infrastructure/persistence/sequelize-software-installation.repository.ts#L27) | `private async load` |
| E-softwareResponse | [modules/companies/src/api/application/software-installation-response.builder.ts](../../../modules/companies/src/api/application/software-installation-response.builder.ts#L7) | `export function buildSoftwareResponse` |
| E-sql | [modules/resources/src/api/infrastructure/persistence/resource.model.ts](../../../modules/resources/src/api/infrastructure/persistence/resource.model.ts#L19) | `export async function migrateInventory` |
| E-supportRead | [modules/assets/src/api/infrastructure/persistence/sequelize-support.repository.ts](../../../modules/assets/src/api/infrastructure/persistence/sequelize-support.repository.ts#L21) | `SELECT r.id,r.name` |
| E-supportSQL | [modules/assets/src/api/infrastructure/persistence/support-migration.ts](../../../modules/assets/src/api/infrastructure/persistence/support-migration.ts#L86) | `support_resource_company` |
| E-templateStatus | [modules/resources/src/api/domain/resource-catalog.aggregate.ts](../../../modules/resources/src/api/domain/resource-catalog.aggregate.ts#L49) | ` active(` |
| E-textSearch | [packages/rdx-ui/src/lib/text-search.ts](../../../packages/rdx-ui/src/lib/text-search.ts#L2) | `export function normalizeSearchText` |
| E-unitApplication | [modules/companies/src/api/domain/application-configuration.test.ts](../../../modules/companies/src/api/domain/application-configuration.test.ts#L75) | `test(` |
| E-unitLifecycle | [modules/resources/src/api/application/resource-lifecycle.test.ts](../../../modules/resources/src/api/application/resource-lifecycle.test.ts#L94) | `test(` |
| E-unitResource | [modules/resources/src/api/domain/resource.test.ts](../../../modules/resources/src/api/domain/resource.test.ts#L38) | `test(` |
| E-unitSoftware | [modules/companies/src/api/domain/software-installation.test.ts](../../../modules/companies/src/api/domain/software-installation.test.ts#L52) | `test(` |
| E-usage | [modules/resources/src/api/application/resource-inventory.service.ts](../../../modules/resources/src/api/application/resource-inventory.service.ts#L54) | `function resourceUsage` |
| E-user | [modules/identity/src/api/domain/user.aggregate.ts](../../../modules/identity/src/api/domain/user.aggregate.ts#L8) | `export type UserProps` |
## Fuentes documentales completas de la auditoría
| Fuente | SHA-256 al formalizar |
| --- | --- |
| [01-modelo-resource.md](../../auditorias/resources-global-scope/01-modelo-resource.md) | `d8dda2ac4e22351eb3be203da7331c9e1ade5f0975bd51ea94bc10b983b6f78b` |
| [02-plantillas-aplicacion.md](../../auditorias/resources-global-scope/02-plantillas-aplicacion.md) | `4be8cb2dba0fa8e54fdda7bff0ac83027b98f6d29aef5eadf27208eeb54d4e1c` |
| [03-sequelize-mariadb.md](../../auditorias/resources-global-scope/03-sequelize-mariadb.md) | `2805772bc224be908cf82df775d93c907926573ca7d1d7cbd67b82cc70fb15f9` |
| [04-aislamiento-autorizacion.md](../../auditorias/resources-global-scope/04-aislamiento-autorizacion.md) | `562771079ce0fa35e798a93e43e7f6189381e12cd10b03ccbfd889a8f68f6053` |
| [05-consumidores-resource.md](../../auditorias/resources-global-scope/05-consumidores-resource.md) | `9deba084c334c2cbb79584a3f083bce313284641a9a737c6b42a952ebf3531be` |
| [06-software-configuraciones.md](../../auditorias/resources-global-scope/06-software-configuraciones.md) | `a900e206f30827829731973b427770019ad411e26afa19013386b39d76b3efe3` |
| [07-contratos-api.md](../../auditorias/resources-global-scope/07-contratos-api.md) | `5148d7494bee890ed519c07947d28533778e098a1cf65679c65a95537707df5d` |
| [08-propuesta-ui-ux.md](../../auditorias/resources-global-scope/08-propuesta-ui-ux.md) | `d4162d5c2ceef6183658a901a31018e4070ea712df650de60ad05ef83f9ec5a9` |
| [09-alternativas-arquitectonicas.md](../../auditorias/resources-global-scope/09-alternativas-arquitectonicas.md) | `cf29fc96d801d658b31189b6379bca22afbaff610d528e09aaec7ec9ff09c11c` |
| [10-plan-implementacion.md](../../auditorias/resources-global-scope/10-plan-implementacion.md) | `532387597f375c5daec91ffec4e4badb6b57c78b2e2eb4ae74327d390734a93d` |
| [11-resumen-ejecutivo.md](../../auditorias/resources-global-scope/11-resumen-ejecutivo.md) | `7df1095dd62e65ce654f685a924796cca0baaa4ef786e34ec0d34b27291f0bdc` |
| [12-matriz-trazabilidad.md](../../auditorias/resources-global-scope/12-matriz-trazabilidad.md) | `6030853b658bc9cf404e59f4e3e3471eb8bd6ffbd5678814b11e8a3597efae70` |
## Resultado de comprobación documental
Correctos: **10 entregables, 224 enlaces locales (181 con línea), 69 anclas de símbolos, D01–D10 y los 26 invariantes con sus T01–T26**. Los doce documentos de auditoría conservan sus huellas. Se ejecutaron comprobadores Python de existencia/anclas/cobertura y whitespace individual con git diff --no-index --check; git diff --check no reporta errores. La comparación con el baseline detectó cambios concurrentes en 27 archivos ya existentes y otros nuevos ajenos a esta tarea, conservados sin editarlos ni revertirlos. Esa comparación no se presenta como ausencia de cambios del resto del repositorio.
El siguiente comprobador es reproducible desde la raíz, solo lectura; no sustituye la revisión semántica de matrices/decisiones ni ejecuta tests futuros:
```bash
python3 - <<'PY'
from pathlib import Path
import hashlib
import re
import subprocess
base = Path('docs/architecture/resources-global-scope')
files = sorted(base.glob('*.md'))
assert len(files) == 10
links = 0
for file in files:
body = file.read_text()
assert chr(123) * 2 not in body and chr(125) * 2 not in body
assert body.count(chr(96) * 3) % 2 == 0
for target in re.findall(r'\]\(([^)]+)\)', body):
if target.startswith(('https://', 'http://')):
continue
name, _, anchor = target.partition('#')
source = (file.parent / name).resolve()
assert source.is_file(), target
if anchor.startswith('L'):
assert 1 <= int(anchor[1:]) <= len(source.read_text().splitlines())
links += 1
result = subprocess.run(['git', 'diff', '--no-index', '--check', '/dev/null', str(file)], capture_output=True, text=True)
assert result.returncode in (0, 1) and not result.stdout and not result.stderr
contract = (base / '01-contrato-arquitectonico.md').read_text()
tests = (base / '08-plan-pruebas.md').read_text()
decisions = (base / '02-decisiones-d01-d10.md').read_text()
for number in range(1, 27):
assert f'| INV-{number:02} |' in contract
assert f'| T{number:02} / INV-{number:02} |' in tests
for number in range(1, 11):
assert f'| D{number:02} |' in decisions
summary = (base / '10-resumen-para-aprobacion.md').read_text()
anchors = re.findall(r'^\| E-([^| ]+) \| \[([^]]+)\]\([^#]+#L(\d+)\) \| `([^`]+)` \|$', summary, re.M)
for key, name, line, token in anchors:
assert token.replace(' / ', '|') in Path(name).read_text().splitlines()[int(line)-1], key
sources = re.findall(r'^\| \[([^]]+\.md)\]\(../../auditorias/resources-global-scope/[^)]+\) \| `([a-f0-9]{64})` \|$', summary, re.M)
assert len(sources) == 12
for name, digest in sources:
assert hashlib.sha256((Path('docs/auditorias/resources-global-scope') / name).read_bytes()).hexdigest() == digest
print(f'OK: {len(files)} documentos, {links} enlaces, {len(anchors)} símbolos, 26 invariantes con pruebas y 12 fuentes intactas.')
PY
```