metis-desk/docs/architecture/resources-global-scope/17-r2-6-coexistencia-duplicados.md
2026-10-25 01:19:17 +02:00

264 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# R2.6 — Coexistencia y posibles duplicados de aplicaciones
Fecha: 25 de octubre de 2026. Implementación funcional y validación dirigida completadas; la aceptación global del proyecto sigue condicionada por la deuda registrada en §25. Las sugerencias no acreditan equivalencia. D08 continúa sin aprobar ni implementar.
## 1. Objetivo y alcance
Desde la ficha de una aplicación particular se pueden consultar productos globales con nombre compatible, conocer por qué aparecen y abrir su ficha. La consulta no cambia recursos, UUID, propietario, scope, referencias, versiones, licencias, estados ni historial. Aplicaciones particulares y globales siguen coexistiendo como productos independientes.
Se incluye una consulta opcional del uso particular en instalaciones y configuraciones de la compañía atendida. No hay consolidación, adopción, migración, eliminación ni búsqueda de uso entre compañías.
## 2. Contratos R2.0 aplicados
Se mantienen las decisiones de [R2.0](01-contrato-arquitectonico.md), [D01–D10](02-decisiones-d01-d10.md), [seguridad](03-contrato-seguridad.md), [HTTP](04-contratos-api-dto.md) y [consumidores](06-matriz-consumidores.md). Los documentos 01–16 se consultaron completos durante esta secuencia de trabajo; R2.6 no los modifica ni reinterpreta.
La identidad continúa siendo el UUID. Una coincidencia de atributos no crea una relación de dominio. La aplicación particular conserva compañía; el producto global conserva owner nulo. D07 mantiene tildes en la comparación de identidad. D08 se analiza exclusivamente en §29. Los gates de catálogo, software, candidatos y perfiles conservan su independencia y su valor predeterminado `false`.
## 3. Archivos modificados
El árbol de partida ya contenía R2.1–R2.5 y otros trabajos de interfaz. El diff completo contra HEAD incluye esos trabajos; el inventario de esta fase se obtiene contra la copia anterior a cada edición, sin revertir cambios ajenos.
| Área | Archivos principales |
| --- | --- |
| Contrato | `modules/resources/src/common/dto/application-coincidences.dto.ts`, índice común y traducciones es/en. |
| Aplicación | `modules/resources/src/api/application/application-coincidence.ts` y su prueba. |
| Infrastructure | `application-coincidence-reader.ts`, `application-coincidence.routes.ts`, `resource-reference-metadata.ts`, composición de Resources. |
| Interfaz Resources | `web/application-coincidences/{shared,review/ui}`, ResourcesPage, ResourceDetailDialog y ResourceSelectionPanel. |
| Caché | Invalidaciones en formulario particular, catálogo de definiciones y mutaciones de productos globales. |
| Interfaz Companies | `application-resource-usage.tsx`, CompanyResourceRelatedContent y traducciones es/en. |
| Pruebas | `apps/server/src/application-coincidences.integration.test.ts`, scripts de pruebas SQL, visual-audit y verificador MCP R2.6; comandos en package.json. |
| Documentación | Este informe y `docs/componentes-de-interfaz.md`. |
No se añaden dependencias, tablas, columnas, migraciones, flags ni escrituras de detección. Las huellas iniciales verifican sin cambios 66 archivos vendor, 255 de archive, los documentos previos de Resources y sus evidencias. Las doce auditorías originales también se conservan. Ningún `.env` local se imprime, copia o modifica.
## 4. Modelo de coincidencias
Una función pura compara una fuente `{ name, provider }` con la proyección pública de un producto global. Devuelve el candidato con clasificación y motivos, o ausencia. El lector SQL resuelve elegibilidad y pertenencia, limita el trabajo y aplica esa única función. El navegador presenta resultados; no descarga ni compara todo el catálogo.
Los atributos disponibles son nombre comercial y proveedor textual del campo existente. No hay un identificador canónico de fabricante, familia, edición o producto externo que permita demostrar equivalencia. URL y descripción no se convierten en pruebas de identidad. Las versiones de instalación y configuración son operativas y no intervienen.
## 5. Normalización
`normalizeApplicationIdentityText`: Unicode NFC, trim, colapso de espacios y minúsculas. Conserva acentos, puntuación, abreviaturas, números, ediciones y denominaciones. `CAFE` y `CAFÉ` no coinciden; `CAFE\u0301` y `CAFÉ` sí comparten la representación NFC cuando las demás letras coinciden.
La búsqueda del diálogo reutiliza el normalizador amplio de búsqueda, que sí permite encontrar texto sin tildes. Es un filtro posterior entre coincidencias ya admitidas; nunca amplía la equivalencia. No hay fuzzy matching, stemming, diccionario de sinónimos ni integración externa.
## 6. Reglas de exclusión
Solo se admite una fuente particular de la compañía indicada cuya plantilla sea la aplicación stock o declare `usage: application`. Un producto global no sirve como fuente de esta operación. El lector reutiliza las comprobaciones existentes de plantilla, scope, owner y campos globales válidos.
Se excluyen sistemas operativos, licencias, certificados, SMB, Access, remoto y las demás familias. Un UUID de B enviado bajo A devuelve 404 incluso para administrador. Los nombres distintos tras normalización y los proveedores presentes pero contradictorios no producen sugerencia.
`Office 2021`/`Microsoft 365 Apps`, `Reader`/`Pro`, `Standard`/`Professional`, `Web`/`Desktop`, números o puntuación distintos permanecen separados. No se deducen familias a partir de subcadenas.
## 7. Clasificaciones
| Código | Condición | Interpretación |
| --- | --- | --- |
| `name-provider` | Nombre normalizado igual y ambos proveedores no vacíos e iguales. | Señal por dos atributos; requiere revisión. |
| `name-only` | Nombre normalizado igual y falta uno o ambos proveedores. | Información insuficiente; no se presupone fabricante. |
| Ausencia | Nombre distinto o proveedores presentes contradictorios. | No se propone candidato. |
No hay clasificación «confirmada» ni porcentaje arbitrario. Los nombres genéricos con proveedor ausente permanecen débiles. No se implementa una categoría probable basada en familias porque faltan atributos estructurados fiables. Un global inactivo puede aparecer para revisión histórica, con advertencia explícita de que no es elegible para asociaciones nuevas.
## 8. Explicabilidad
Los motivos cerrados son `same-normalized-name`, `same-provider`, `provider-missing` e `inactive-global`, traducidos a es/en. Se muestran atributos disponibles, procedencia Global y estado junto al producto. Un dato ausente se representa con `—`. Ni una etiqueta ni un motivo declaran que dos UUID sean el mismo producto.
## 9. API
`GET /api/resource-application-coincidences/:companyId/:id`
Query estricta: `search` hasta 100 caracteres, `pageSize` entero de 1 a 50 (10 por defecto), `cursor` opcional hasta 2048 caracteres. Parámetros UUID y query se validan con Zod; campos desconocidos se rechazan. La respuesta se valida también en el cliente:
```text
{ source, items: [{ candidate, classification, reasons }],
nextCursor, complete, scanned }
```
`source` es una referencia particular mínima con proveedor; `candidate` usa GlobalApplicationSummary. No se devuelven DTO administrativos. El cursor contiene versión, contexto y último UUID examinado, codificados como transporte, sin persistencia JSON. Se comprueba compañía, fuente, nombre/proveedor, búsqueda y tamaño de página; cambiar contexto invalida el cursor. No es un token de autorización ni una garantía de snapshot entre peticiones.
Orden determinista por UUID, sin reordenar por supuesto grado de equivalencia. `complete` indica agotamiento del tramo restante del catálogo, no un total ni una conclusión de identidad. Puede haber una página vacía con cursor siguiente. Cada petición examina como máximo 200 globales y obtiene hasta 201 filas para detectar continuidad.
Estados: 200 lectura, 400 entrada/cursor inválidos, 401 sesión ausente, 404 compañía/referencia inexistente o incompatible, 409 catálogo cerrado. Los errores esperables llevan código y requestId; los inesperados pasan al middleware central. No se exponen SQL ni stacks.
## 10. Autorización
Se usa la autenticación existente antes del lector. Un usuario ordinario autenticado puede revisar una aplicación particular en el contexto de soporte autorizado existente; no se introduce un permiso de administración global para una lectura pública del producto.
R2.2 no define un tenant por usuario que limite a una sola compañía atendida: no se inventa aquí. Sí se comprueba en servidor que la fuente pertenece a la compañía del contexto. Conocer un UUID ajeno no permite leerlo bajo otra compañía, tampoco con rol administrador. La detección exige el gate del catálogo; no abre los gates de asociación de software/perfil.
## 11. Privacidad
Las consultas usan proyecciones mínimas y parámetros ligados. Ni coincidencias ni cursor incluyen notas administrativas, historial global completo, licencias, secretos o uso operativo de otras compañías. Se conserva la validación de metadatos globales existente.
La vista de uso se compone en Companies mediante APIs públicas propias. Resources no importa Companies ni incorpora un endpoint transversal de dependencias. Las sesiones, contraseñas y storageState de la auditoría fueron temporales y se retiraron con el contenedor; el manifiesto no guarda credenciales.
## 12. UI/UX
Entrada contextual «Buscar coincidencias en el catálogo global» en la ficha particular elegible, tanto panel como detalle modal. No se crea un módulo de administración de duplicados. ApplicationCoincidencesDialog vive en su propio fichero y reutiliza FormDialog estable, FormBody, Input, Button, Badge y CursorPagination.
La página conserva el estado de apertura por UUID para sobrevivir al cambio responsive entre panel y modal; la fuente se deriva de la query actual, sin copiar estado remoto. El diálogo presenta origen particular, advertencia de sugerencias, proveedor, filtro, candidatos, motivos y enlaces reales a sus fichas globales en otra pestaña. No contiene acciones de reemplazo ni selección automática.
Se aplicaron DESIGN.md, los patrones de diálogos/listados y la estructura web por entidad/operación. Skills consultadas durante el trabajo: rdx-solid para separación de responsabilidades y pruebas observables; ux-search para filtro remoto y estados parciales; ux-tables para procedencia y valores ausentes; shadcn para composición con APIs instaladas. No se instaló ningún auditor adicional ni se afirma una auditoría automática especializada de accesibilidad.
## 13. Integración con selectores
Los selectores R2.4/R2.5 mantienen UUID independientes, procedencia y proveedor. Una coincidencia no oculta, sustituye, selecciona ni reordena candidatos. Las opciones particulares siguen disponibles. Los inactivos son visibles en revisión, pero siguen excluidos de nuevas asociaciones por los contratos existentes.
El MCP abrió los dos pickers con homónimos particulares/globales y canceló los formularios. SQL verificó que las opciones particulares siguen disponibles con los gates de asociación global cerrados. No se cambia el contrato del picker para introducir equivalencias.
## 14. Vista previa de uso
ApplicationResourceUsage consulta a demanda `softwareApi` y `applicationApi`, con sus query keys existentes. Filtra por compañía y UUID particular. Muestra por separado raíces operativas devueltas, incluidos registros cerrados, estado, versión y enlaces al activo/perfil donde se consulta su historial.
Son recuentos de registros raíz actuales del conjunto completo devuelto por esas APIs de compañía, no de revisiones históricas únicas ni de referencias de todas las compañías. Si falla una fuente, su recuento es `—`, con error/reintento independiente; la otra fuente puede seguir disponible. Se explica que la vista no es un plan de migración. El coste de cargar estos conjuntos con historial solo se asume cuando se despliega la sección; véase §27.
## 15. Rendimiento
MariaDB 11.8 desechable, usuario ordinario, HTTP y sesión reales. Una petición de calentamiento y una medida por volumen, en la misma máquina de desarrollo; no son percentiles ni un SLA. El contador incluye consultas de autenticación y transacción.
| Globales en el momento medido | Tiempo HTTP | Consultas | Productos examinados | Tramo agotado |
| --- | ---: | ---: | ---: | --- |
| 10 | 7,87 ms | 9 | 10 | Sí |
| 100 | 9,79 ms | 9 | 100 | Sí |
| 1.000 | 18,04 ms | 9 | 200 | No |
| 10.000 | 93,40 ms | 9 | 200 | No |
El conjunto final contiene 10.028 globales y el recorrido completo encuentra 25 coincidencias únicas, seis inactivas, en orden determinista. Se usa rango por UUID y LIMIT ligado; el coste SQL todavía crece con el catálogo por joins y comprobaciones correlacionadas existentes. No se declara O(1), índice de nombre canónico ni benchmark concurrente. No hay producto cartesiano compañías × recursos, N consultas por candidato ni descarga completa al navegador.
## 16. Caché
Familia centralizada `resource-application-coincidences`, compañía y UUID, ampliada por revisión particular, búsqueda y cursor. TanStack Query mantiene el estado remoto. Editar la fuente invalida su prefijo; cambiar un producto global o las definiciones del catálogo invalida la familia afectada porque puede introducir o retirar candidatos en varias fuentes. No se invalida toda la caché.
La vista de uso comparte las claves operativas existentes y sus invalidaciones. No se persisten sugerencias ni decisiones de descarte. Las peticiones del diálogo propagan cancelación y timeout de 20 segundos. Cambios concurrentes entre páginas pueden requerir reiniciar el recorrido; no existe snapshot de larga duración ni token de confirmación.
## 17. Estados vacíos y errores
Carga con status; error con alert y reintento; los últimos resultados disponibles se conservan acompañados de aviso de posible desactualización. El fallo del detector no destruye la ficha ni impide consultar el inventario local.
Se distingue ausencia en el tramo/página de ausencia al agotar una búsqueda inicial completa. Una página vacía no implica que no queden globales. La búsqueda se puede cambiar y los cursores se reinician. No se sustituye una respuesta parcial por un total inventado.
## 18. Accesibilidad
Título de diálogo, etiqueta asociada al filtro, listas semánticas, estados textuales además de badges y botones nativos. Cierre por Escape y retorno de foco al disparador comprobados en navegador. Se conserva el mecanismo de foco de FormDialog; si el disparador responsive se desmonta se busca el botón equivalente por UUID.
No hay confirmaciones destructivas porque no hay acciones destructivas. No se ejecutaron pruebas con lector de pantalla, axe ni contraste instrumental; la comprobación disponible fue MCP, teclado y revisión de capturas. Los seis cambios de anchura conservaron abierto el diálogo.
## 19. Responsive
MCP ejecutado a 1600, 1440, 1280, 1024, 768 y 390 px; alturas de 1000 px y 844 px en las dos anchuras menores. Se verificó presencia del diálogo y ausencia de desbordamiento horizontal de la página. Los nombres largos se ajustan, el cuerpo desplaza verticalmente y el cierre permanece disponible en el pie.
La revisión visual detectó y permitió corregir la pérdida del diálogo al desmontarse el panel por cambio de layout. No se modificó el shell compartido. Las capturas finales de las seis anchuras se inspeccionaron individualmente.
## 20. Pruebas unitarias
Resources: **105/105**, incluyendo tres pruebas nuevas con matriz de assertions sobre nombre/proveedor, dato ausente, contradicción, activo/inactivo, NFC, acentos, espacios, ediciones, números, puntuación, abreviaturas y denominaciones web/desktop. Datos congelados y UUID conservado verifican ausencia de mutación; se incluye comparación pura sobre 10.000 candidatos.
La elegibilidad por plantilla/compañía y la paginación se comprueban en la frontera SQL/HTTP reutilizada, no mediante mocks que repitan su implementación. El comando directo con aislamiento de procesos resumió inicialmente 17/18 ficheros sin diagnóstico útil; la ejecución explícita `--test-isolation=none`, con permiso para los HTTP efímeros locales, mostró **105/105**. No se oculta aquella ejecución fallida ni se interpreta su agregado como número de pruebas.
## 21. Pruebas MariaDB
`python3 scripts/resource-scope-test.py --coincidences --regressions`: **6/6** R2.6 y **50/50** regresiones. Instalación temporal exclusivamente `metis_desk_test`, migración explícita existente y retirada al terminar.
Fixture: 15 compañías, principal con al menos 50 personas, activos, perfiles, puestos y recursos particulares; volumen global progresivo 10/100/1.000/10.000. La carga masiva clona una raíz global válida con nuevas identidades UUID v7, revisión y tablas hijas/snapshots, manteniendo restricciones y relaciones; no desactiva claves foráneas. Después se añaden casos discriminantes hasta 10.028 globales.
Se comparan hashes de recursos, revisiones, características, instalaciones/revisiones y configuraciones/revisiones antes/después de las consultas. Resultado: sin cambios, incluidas referencias, licencia particular, versión LOCAL-123 e historial. No se aplicaron fixtures sobre una base habitual.
## 22. Pruebas HTTP
Servidor real, sesiones ordinaria/administradora y cliente con CSRF para la preparación de escrituras autorizadas de prueba. Se prueban 200, 400, 401, 404 y 409; parámetros extra, pageSize inválido, cursor corrupto/cambiado de búsqueda o compañía, referencia ajena, global como fuente, OS/licencia/certificado, UUID inexistente y catálogo cerrado.
El recorrido de cursores verifica límites, unicidad, orden, seis inactivos, motivos y ausencia de notas/historial administrativos. El filtro `Pro` no convierte otra edición en coincidencia. La misma instantánea se mantiene después de errores y del cierre del gate. Las comprobaciones de CSRF generales siguen cubiertas por R2.2 y las regresiones; la detección es GET sin mutación.
## 23. Pruebas Playwright
`python3 scripts/visual-audit.py --application-coincidences --check`, usando Playwright MCP y login real ordinario: **22 comprobaciones** y **12 capturas**. Entorno desechable retirado automáticamente. Volumen visual: 15 compañías; principal con 50 personas, 51 activos, 50 perfiles, 50 puestos y al menos 50 recursos; 60 globales, 50 de nombre compatible y diez con sufijo Pro.
| Escenario solicitado | Evidencia real |
| --- | --- |
| S01 exacta / S02 probable o débil | Señales nombre+proveedor y falta de proveedor visibles, sin afirmar equivalencia. No hay heurística probable adicional. |
| S03 diferente edición | Diez productos Pro excluidos en datos visuales; matriz pura y HTTP verifican exclusión. |
| S04 inactivo | Badge y explicación visibles. |
| S05 sin candidatos | Fuente propia sin coincidencias, mensaje de vacío completo. |
| S06 múltiples | Página siguiente con primer UUID distinto, cursor y motivos. |
| S07 selectores mixtos | Ambos pickers con procedencia y homónimos; software sin selección inicial. |
| S08 instalación / S09 configuración | Uso particular visible; igualdad de DTO e historial antes/después. |
| S10 sin permiso | Navegador anónimo redirigido a login; UUID ajeno bajo otra compañía rechazado en HTTP. |
| S11 error parcial | Respuesta 500 interceptada solo en el detector, ficha y últimos datos conservados, reintento correcto. |
| S12 densidad | Fixture amplio, nombres largos y seis anchuras; 10.000 globales comprobados en SQL, no cargados de golpe en navegador. |
Las iteraciones iniciales detectaron el fallo responsive descrito y dos localizadores de auditoría que no esperaban el resultado filtrado. Se corrigieron y se ejecutó de nuevo el recorrido completo. No se presenta una ejecución interrumpida como validación final.
## 24. Capturas
Evidencias locales ignoradas por Git: `.artifacts/playwright/r2-6/final/r26-report.json` y sus 12 PNG con SHA-256. Origen final: `authenticated-szjtam4f`. Se copiaron exclusivamente manifiesto y capturas, comprobando sus hashes; no cookies ni storageState.
Capturas: coincidencias, error recuperable, diálogo en seis anchuras, uso local, vacío y los dos pickers homónimos. Todas se inspeccionaron visualmente. No se versionan binarios ni se modifican capturas de auditorías históricas.
## 25. Regresiones y validación técnica
| Comando o grupo | Resultado |
| --- | --- |
| R2.2 `--security` | 11/11 |
| R2.3 `--catalog` | 11/11 |
| R2.4 `--software` | 13/13 |
| R2.5 `--profiles` | 11/11 |
| R2.6 `--coincidences --regressions` | 6/6 y 50/50 |
| Suite Resources expandida | 105/105 |
| `pnpm -r --filter '!@repo/shadcn-ui' --filter '!metis-desk' typecheck` | Correcto en los paquetes propios seleccionados, incluidos Resources, Companies, Assets, Identity, Server y Web. |
| Biome check / ESLint en los 30 archivos propios TS/TSX/JSON | Correctos, sin supresiones nuevas. |
| `pnpm build` | Correcto, Server y Web; repetición final sin caché. Advertencia existente de chunk grande de Web. |
| `--coincidences --monorepo` | Ejecutado en base temporal; R2.6 correcto, recorrido global con fallos previos. |
| `pnpm typecheck` | Falla en vendor calendar.tsx:165, TS2375 de locale con exactOptionalPropertyTypes. Vendor no modificado. |
| `pnpm lint` / `pnpm check` | Ejecutados; 15/168 errores respectivamente, más 2 warnings y 5 infos en el árbol global. No son una validación verde. |
El ensayo amplio equivalente a `pnpm test` dentro del contenedor SQL registra Core 14/14, contratos 13/13, DDD 3/3, rdx-ui 79/79, Identity 11/11, Resources 105/105, Assets 147/147 y Companies **252/254**. Los dos fallos ya documentados en R2.5 son `companies-page.test.tsx:180` (texto Personas (1)) y `:210` (texto de acceso de persona). La prueba legacy SQL `apps/server/src/identity.test.ts:82` conserva el fallo previo 401 frente a 404 esperado en una ruta retirada.
Las 50 regresiones dirigidas incluyen Resources local, WorkProfile, Workstation, instalaciones/licencias, SMB, Access, remoto e historial. No hay nueva evidencia que resuelva la aceptación global pendiente de R2.5 ni G6 histórico de R2.4. La ejecución global se hizo sin acceder a configuración habitual; no se corrigió deuda ajena para obtener un resultado artificialmente verde.
## 26. Gates
| Gate R2.6 | Estado y límite |
| --- | --- |
| G1 detección | Verificado: datos reales, reglas conservadoras, motivos, orden y solo lectura. |
| G2 integridad | Verificado por snapshots SQL y comparación de DTO operativos en MCP. |
| G3 seguridad | Verificado para sesión, pertenencia contextual, proyección y catálogo cerrado. |
| G4 UI/UX | Verificado con MCP y capturas; límites de accesibilidad indicados en §18. |
| G5 compatibilidad | Regresiones dirigidas R2.2–R2.5 correctas, homónimos separados, flags por defecto cerrados. |
| G6 validación | Validación dirigida correcta y mediciones disponibles. Checks globales continúan fallando por deuda previa. |
R2.6 está implementada y comprobada en su alcance dirigido; no se declara aceptación global completa del proyecto. G6 de R2.4 y aceptación global de R2.5 siguen pendientes. No se modifica configuración de despliegue ni se habilitan flags en la instalación habitual. La activación requiere el procedimiento de preflight/despliegue ya aprobado, fuera de esta consulta.
## 27. Riesgos residuales
La comparación exacta produce falsos negativos deliberados cuando cambian marcas, nombres, abreviaturas o ediciones. Un nombre y proveedor iguales tampoco prueban identidad; nombres genéricos pueden seguir generando sugerencias ambiguas. El proveedor es texto comercial, no una entidad fabricante canónica.
El recorrido de catálogos grandes puede necesitar muchas páginas, incluidas vacías; se declara parcial y no se automatiza un barrido completo del navegador. Los joins y subconsultas de metadatos explican crecimiento de latencia aunque el número de filas retornadas sea acotado. No se ha medido carga concurrente, otra topología ni percentiles.
La vista opcional de uso carga las colecciones completas de compañía con historial mediante APIs existentes. Se comparte caché y se difiere la consulta hasta abrirla, pero una compañía con mucho historial puede requerir en el futuro una proyección pública paginada específica de Companies. No se introduce esa API sin un consumidor medido que la justifique.
El catálogo puede cambiar entre páginas; los cursores no congelan una sesión de revisión. Los datos obsoletos se invalidan al mutar desde esta aplicación y se comunican los fallos de actualización. No hay persistencia de revisiones/descarte de coincidencias.
## 28. Funcionalidades diferidas
Adopción particular→global, merge, cambio de referencia, eliminación/archivo automático, equivalencias persistentes, fabricante canónico, fuzzy matching, índice canónico, panel transversal de uso, descarte persistente y acciones masivas. No hay endpoint, botón, migración de datos ni compensación ejecutable para ninguna de ellas.
También quedan fuera la resolución de deuda global, la activación habitual y la aceptación histórica pendiente. No se añaden TODO vagos ni implementaciones alternativas comentadas.
## 29. Propuesta de futuro ADR D08
**Estado: propuesta para discusión, sin ratificar y sin código de adopción.** Una futura adopción debe ser una decisión explícita distinta de una sugerencia. Nunca debe inferirse del nombre ni reescribir el UUID del producto particular, su owner, scope o historial.
La fase previa tendría que mostrar un plan autorizado de una única compañía: fuente particular, destino global activo y elegible para cada propósito, raíces abiertas/cerradas, licencias, versiones, entrega, historial y conflictos. La revisión humana debe acreditar equivalencia comercial y operativa con información suficiente; proveedor ausente o una coincidencia débil no la acreditan. El servidor vuelve a validar identidad, permisos, actividad, pertenencia y concurrencia antes de confirmar. Un hash/versión del plan impediría confirmar una fotografía ya desactualizada; su contrato requiere nuevo diseño.
Los target de SoftwareInstallation y ApplicationConfiguration son inmutables en los contratos actuales. Una adopción aprobada no puede hacer UPDATE silencioso de resourceId ni reutilizar el UUID de una raíz operativa. Una opción coherente para evaluar es cerrar explícitamente cada asociación abierta mediante su operación de dominio y crear una sucesora con UUID nuevo apuntando al global. Debe definirse qué representa el cierre en cada agregado, sin inventar estados «removed» ajenos a su máquina de estados. Las raíces cerradas y las revisiones históricas se conservan referidas al recurso particular; no se trasladan versiones o licencias al producto global. Las versiones/entrega propias solo se copiarían a sucesoras si el usuario lo revisa en el plan y las policies lo permiten.
Se deben resolver previamente los conflictos de pares únicos activo/recurso y perfil/recurso, destinos ya asociados, incompatibilidades de licencia particular, actividad, concurrencia y consumidores de referencia solo local. No se fusionan configuraciones existentes ni se descarta historial. Los campos de procedencia de sucesión/auditoría e idempotencia requerirían un modelo relacional explícito y migración aparte, no notas ni blobs JSON que sustituyan relaciones.
Una futura confirmación debe ser atómica en la transacción pública de los módulos, respetando el orden de bloqueos existente, revalidación final y registro auditable de actor, operación y resultado. La clave de idempotencia debe devolver el mismo resultado ante repetición y no crear sucesoras duplicadas. Los eventos o una infraestructura adicional solo se justificarían si aparecen consumidores reales.
Antes del commit, rollback transaccional ante cualquier fallo. Después del commit, no se propone restaurar un backup ni borrar historial como «deshacer». Cualquier compensación sería otra operación explícita: comprobar que las sucesoras no han recibido cambios o dependencias nuevas, cerrarlas conforme a dominio y crear, si procede, nuevas asociaciones particulares. Si han evolucionado, exigir un nuevo plan y revisión, sin sobrescribir trabajo posterior. Deben definirse permisos, trazabilidad, errores e idempotencia de esa compensación en el ADR.
El recurso particular permanece conservado para consulta histórica y consumidores locales; su eventual archivo sería otra decisión independiente, con comprobación de dependencias. Ratificar D08, aprobar el contrato HTTP/relacional y verificar pruebas de conflictos, fallos intermedios, repetición y compensación son requisitos de una fase futura; este informe no los autoriza ni los implementa.