| title | Registro de Decisiones Arquitectónicas Locales |
|---|---|
| type | hub |
| classification | Product ADR |
| owner | Evolith Tracker Team |
Bilingual Navigation: English (this document) · Versión en Español
Este documento sirve como el Hub de Decisiones Arquitectónicas Locales para Evolith Tracker. Documenta las decisiones y desviaciones específicas de este producto satélite frente al Core.
Nota: Las decisiones universales se heredan del Upstream Base (evolith_arch32).
- Upstream base:
https://github.com/beyondnetcode/evolith_arch32 - Última clasificación:
2026-06-28 - Convención de IDs de ADR upstream: calificados por categoría —
core/,nodejs/,dotnet/,android/(p. ej.core/0074,nodejs/0075). - ADRs locales (satélite): este repositorio es un satélite gobernado — ver
evolith.yaml. Los ADRs locales conservan su namespaceT-{NNN}(distinto delADR-{NNNN}del Core, per T-011), siguen la plantilla canónica de 6 secciones del Core, llevan el tagEvolithSatelliteen su frontmatter y están registrados enevolith.yaml → spec.compliance.adrRegistry(regla federada CoreINH-04). - Fuente única de verdad (CD-18): el fichero
docs/adrs/T-{NNN}-<slug>.md(+ su par.es.md) es el ADR; los demás registros son índices derivados que deben mantenerse en sincronía con él y con el disco: (1) este hub (DECISIONS.md/.es.md), (2)evolith.yaml → spec.compliance.adrRegistry, y (3) el catálogo servido porGET /api/architecture/adrs(AdrRegistryCatalog). Al añadir o cambiar un ADR se actualizan los tres, y todo fichero dedocs/adrs/debe tener par bilingüe — comprobado porcheck-bilingual-parity.mjs(cobertura ADR).
| ID | Título | Operación | Ref Upstream | ADR Local | Notas |
|---|---|---|---|---|---|
| T-001 | Orquestación de Monorepo con Nx | Adoptar | ADR-0001 | T-001 | Corregido 2026-08-01 (GAP-015): la entrada anterior decía «npm workspaces con Nx», y ningún package.json del repositorio declara workspaces — nunca se usaron. Nx orquesta por GRAFO DE PROYECTOS: project.json más los plugins de inferencia @nx/vite, @nx/webpack, @nx/eslint y @nx/jest declarados en src/nx.json. La razón que decidió es que el repositorio no es homogéneo: tracker-api es .NET, y npm workspaces enlaza node_modules entre paquetes npm — habría cubierto tres proyectos de cuatro dejando fuera al mayor. |
| T-002 | Adopción de Microfrontends en Fase 1 | Sobrescribir | N/A | T-002 | Desviación de topología para escalabilidad de UI. |
| T-003 | Arquitectura Hexagonal (Ports & Adapters) | Adoptar | ADR-0002 | — | Capa de dominio pura sin dependencias externas. |
| T-004 | TypeScript estricto como lenguaje primario | Adoptar | ADR-0003 | — | strict: true habilitado. |
| T-005 | TypeORM como ORM (Data Mapper pattern) | Adoptar | ADR-0043 | — | Data Mapper elegido sobre Active Record. |
| T-006 | React con Vite como base del frontend | Adoptar | ADR-0044 | T-006 | Topología microfrontends. |
| T-007 | Zustand + TanStack Query (State Management) | Adoptar | ADR-0045 | — | Zustand (cliente) + TanStack Query (servidor). |
| T-008 | Convención de nombres de schema PostgreSQL | Definir | N/A | T-008 | Canónico: tracker_discovery, tracker_design... |
| T-009 | REST + OpenAPI 3.0 como única API en Fase 1 | Definir | N/A | T-009 | GraphQL fuera de alcance en Fase 1. |
| T-010 | Framework de agentes configurable por tenant | Extender | N/A | T-010 | Core usa BMAD internamente; el Tracker es framework-agnóstico. El tenant configura su harness agéntico por fase (bmad, spec-kit, custom). |
| T-011 | Estándar de numeración para Iniciativas, ADRs y Specs | Definir | N/A | T-011 | IDs de gobernanza/diseño: Iniciativas INIT-{NNN}, ADRs locales T-{NNN}, ADRs upstream ADR-{NNNN}. La descomposición de ejecución queda fuera de alcance. |
| T-012 | Contratos de eventos de dominio en libs/shared/ | Definir | N/A | — | Eventos compartidos (DriftDetectedEvent, ExternalCheckpointRegisteredEvent) se definen como contratos TypeScript en libs/shared/src/domain/events/. Pact tests pendientes (Phase 0). |
| T-013 | Value Object canónico ExternalReference en Shared Kernel | Definir | N/A | — | Unifica shapes dispares en 6 contextos. ExternalReference con system, externalId, url, type, linkedAt, label, metadata. Ubicado en libs/shared/src/domain/external-reference.vo.ts. |
| T-014 | UC-005 dividido en UC-005a y UC-005b | Definir | N/A | — | Plan Release y Authorize Deployment son operaciones distintas con distintos actores y precondiciones. UC-005a (Planning), UC-005b (Execution). |
| T-015 | Core BFF Gateway como único canal de comunicación | Adoptar | core/0074, nodejs/0075, core/0080 | — | El Tracker se comunica exclusivamente con Evolith Core a través del BFF Gateway (NestJS). Se prohíben llamadas directas a servicios internos de Core. Actualizado 2026-06-28 (CR-26): la nota previa "B2B API Gateway (API key)" queda obsoleta — core/0075 (api-key) fue superado por core/0080: Core no autentica; el BFF es el único perímetro (UMS Bearer + grafo de autorización) y las llamadas a Core usan repositoryRef+workspaceRef, sin API key. |
| T-016 | Capa Anti-Corrupción para Integración PPM (Funnel 0) | Definir | N/A | — | Se implementa una ACL (PpmIntakeACL) en el módulo tracker_intake para mapear y normalizar los esquemas de herramientas PPM externas (ej. Meisterplan) antes de que toquen el dominio del Tracker. Esto evita la contaminación de modelos financieros externos en el core del Tracker. |
| T-017 | Core API Exposure Layer (REST-only) | Adoptar | core/0074 | — | (CR-01/CR-02) Core-API es REST-only bajo /api/v1 (sin GraphQL/SSE) + gateway MCP. Salidas con envelope ADR-0073; errores RFC 9457. Reemplaza supuestos de transporte previos. |
| T-018 | Contrato de Referencia de Repositorio Remoto | Adoptar | core/0080 | — | (CR-26) Supera core/0075 (api-key). Core no autentica; el BFF es el único perímetro (UMS Bearer). Llamadas con contenido: repositoryRef {url, revision} + workspaceRef opaco + operationId. |
| T-019 | Separación Dominio/Financiero | Adoptar | core/0078 | — | (CR-17) ROI/finanzas fuera del dominio de gobernanza. Revisar Discovery Canvas / Business Case (hoy embeben ROI). |
| T-020 | Corpus Multi-Topología Componible | Adoptar | core/0079 | — | (CR-08/CR-09) Topología = 5 dimensiones componibles (progressive-axis/execution/integration/data/ai), no un ladder mono→micro. F1/F2/F3 = madurez de progressive-axis (modular-monolith/distributed-modules/microservices), no fases SDLC. |
| T-021 | PhaseId Canónico Semántico | Adoptar | core (GT-343) | — | (CR-28) Ids canónicos discovery|design|construction|qa|release; f1..f5 son alias deprecados solo de entrada; el namespace F# es madurez de topología. |
| T-022 | Contrato de Evaluación de Satélite | Adoptar | core/0073, core/0074 | — | (CR-03) POST /api/v1/evaluate → EvaluationVerdict (OPA real vía SatelliteEvaluationPipeline). El verdict es evidencia técnica, no un GateDecision canónico: el Tracker decide el gate. |
| T-023 | Distribución OPA-wasm Agnóstica | Adoptar | core/0085 | — | (CR-03) Evaluación de reglas vía rulesets/opa/policy.wasm; resultado por regla passed|failed|skipped (skipped si falta el wasm — manejar con gracia). |
| T-024 | Colisión de Nombre GateDecision |
Definir | Accepted | T-024 | (CR-12/CR-13) Core ya define un VO GateDecision (estrecho). El GateDecision canónico del Tracker (rico: status/snapshots/approvals/exceptions) se renombra/namespacea (p. ej. TrackerGateDecision) y mapea el VO de Core como entrada de TechnicalEvaluation. |
| T-025 | Gobernanza de IA Agéntica (cluster) | Referenciar | core/0081–0083, 0086–0089 | — | (CR-21/CR-27) Sandbox isolation, trust boundary, action-authorization audit, telemetry/cost, ABAC tool execution, sovereign identity, event-driven workflows. Base para la superficie "AI Governance" del Tracker. |
| T-026 | Redis solo para soporte operacional | Definir | N/A | — | Redis es cache/locks/jobs/idempotency. Todo estado crítico va a PostgreSQL. Redis degrada gracefully cuando no está disponible. No es system of record. |
| T-027 | Circuit breaker en cliente Core API | Definir | N/A | — | Estado machine CLOSED→OPEN→HALF_OPEN. Umbral: 5 fallos. Recuperación: 30s. Previene cascade failures cuando Core no está disponible. |
| T-028 | Estrategia PostgreSQL schema-per-context | Superado por T-047 | T-008 | — | Declaraba 10 schemas. El código creó 4 y nadie lo escribió, así que el registro decía una cosa y la base otra. T-047 ratifica la topología consolidada. |
| T-029 | OpenTelemetry Collector como sink único | Superado por T-049 | N/A | — | Desacopla la app del backend de observability (Prometheus, Grafana, Loki). Un solo endpoint OTLP. |
| T-030 | Superseded por T-043 | N/A | — | CD-29): la implementación eligió Helm y nunca hubo Kustomize — existen tres charts (product/infra/helm/evolith-tracker-{api,web,postgres}/Chart.yaml) y cero ficheros kustomization* en el repo. La decisión llevaba meses tomada en el código sin registro escrito; ver T-043. |
|
| T-031 | Criterios de evaluación de compuertas configurables por tenant sobre campos de artefactos | Definir | Accepted | T-031 | Base de artefactos autoritativa del Core (requeridos inmutables + opcionales) + overlay del tenant por compuerta: criterios por campo/valor, campos custom, config obligatoria (fail-closed con UNCONFIGURED), 5 estados de resultado (MET/NOT_MET/PENDING/OBSERVED/NOT_APPLICABLE) y traza total por criterio. |
| T-032 | Contexto acotado Geo / Datos Maestros (regionalización, localización, ubigeo) | Definir | Accepted | T-032 | Reemplaza los campos regionales libres (TenantLocalization) por catálogos maestros validados. Contexto geo dentro del Tracker con esquema propio + puerto IMasterDataDirectory + referencia blanda (geoId+snapshot); jerarquía por adyacencia+ltree, maestros globales tenant-agnósticos + overlays por tenant, datos por catalog_release. Extraíble a un servicio compartido evolith_mms (estrangulador) cuando exista un 2º consumidor. |
| T-033 | Adopción de la Plataforma Shell del Core (Ddd/Aop/Factory/Bootstrapper) | Adoptar | Accepted | T-033 | El dominio consume BeyondNetCode.Shell.Ddd (kernel compartido) en vez de un kernel propio; stack Shell completo (aspectos, factory, bootstrapper) espejando UMS. Cierra R-10 / Core ADR-0071. |
| T-034 | Hub de Configuración vs Monitor | Definir | Active | T-034 | La configuración/mantenimiento (CRUD) vive en Tenant configuration; los monitores son solo-lectura + comandos declarados por historias. §2.1 los registros están exentos; §2.2 toda sección de config es colapsable. |
| T-035 | Estándar de info-hints de campo (ayuda en contexto) | Definir | Active | T-035 | Todo término técnico/campo avanzado lleva un ⓘ InfoHint junto al label (significado·impacto·cuándo); info prop en TextField/Select, accesible, copy bilingüe. |
| T-036 | Parametrización de gates dirigida por artefactos del Core | Definir | Active | T-036 | (Extiende T-031) Evidencia y criterios anclados a artefactos definidos por el Core por fase (no texto libre); requerido efectivo = coreRequired OR tenantOverride; sin artefactos Core ⇒ sin criterios; GateMode (SIMPLE/COMPLEX) derivado de la config, no editable. |
| T-037 | Consumir la proyección de Tenant de MMS (Tracker como consumidor downstream) | Adoptar | Proposed | T-037 | Adopta Core ADR-0106 por referencia: MMS es la única autoridad del Tenant maestro. Tracker añade mensajería (MassTransit/RabbitMQ + inbox EF), consume TenantEvent con upsert versionado idempotente y degrada el agregado Tenant local a proyección de solo lectura. |
| T-038 | Binding a los contratos del Core vía schema neutro, no copia traducida a mano | Definir | Accepted | T-038 | @beyondnet/evolith-contracts es TypeScript-only e inconsumible desde .NET. Se pide a Core publicar JSON Schema de sus formas de cara al consumidor y se generan los tipos C# desde ahí. Puente provisional: DTOs derivados a mano confinados a la frontera de adaptador + guard de conformidad que falle el CI ante deriva (hoy es un no-op) + chequeo de schemaVersion en arranque. Cierra CD-12; prerrequisitos CD-01/CD-02. |
| T-039 | El Core recomienda, el tenant decide — la autoridad de excepción vive en el Tracker | Definir | Accepted | T-039 | Todo veredicto del Core (incluido un waiver aplicado upstream) es advisory: nunca satisface por sí solo un criterio de compuerta. La autoridad de excepción es del tenant y se ejerce en el Tracker. Exception pasa a agregado de primera clase con solicitante/autorizador/alcance/caducidad/revocación; GatePolicy separa approvalAuthority de waiverAuthority (default: autorizador distinto). Corolario: un veredicto MockFallback tampoco puede satisfacer una compuerta y se marca como sintético. Cierra CD-19/CD-20/CD-21. |
| T-040 | Adoptar el contrato de identidad de UMS v1 en vez de mantener un modelo propio | Adoptar | Accepted | T-040 | Tracker delega authn/authz en UMS, que es quien posee la abstracción de IdP (UMS ADR-0020): elegir IdP no es decisión de este satélite. UMS publica en 1.0.0 Ums.Sdk.Contracts/Authorization/.AspNetCore y Tracker es .NET, así que son consumibles directamente — pero no están publicados (sin dotnet pack/nuget push, 404 en nuget.org), de modo que la publicación es dependencia del board de UMS. Prohibido vendorizar o copiar a mano. Entregable inmediato y sin dependencia: alinear el claim de tenant a tid (UMS TE-01) y decidir el tratamiento de bid/cat/idp/jti. La reconciliación del modelo de permisos va aparte en CD-25. |
| T-041 | Línea base heredada: triaje y registro de los ADR del Core core/0090–core/0113 |
Referenciar | core/0090–0113 | T-041 | (CD-16) Los 24 ADRs del rango, triados con motivo escrito en cinco cubos. Gobiernan código ya embarcado: core/0101 (Core stateless), core/0102 (agent runtime, ya consumido), core/0106 (proyección de Tenant), core/0107 (clúster único), core/0108 (topología de mensajes), core/0109 (satélites monorepo), core/0110 (pin MassTransit v8) → alta en evolith.yaml → adrRegistry. Gobierna con brecha abierta: core/0091 (rotación de tokens de workload) — el Tracker usa claves estáticas, así que se declara en governingAdrs pero no en adrRegistry, para no afirmar un cumplimiento inexistente. Gobiernan trabajo planificado: core/0098, 0100, 0103, 0104, 0105, 0111, 0113. No aplicables (9, con motivo): core/0090, 0092–0094, 0095–0097, 0099, 0112. Heredar no es autoría: todos siguen siendo decisiones del Core. |
| T-042 | Guarda de build para el pin de licencia de MassTransit v8 | Definir | core/0110 | T-042 | (CD-16) MassTransit v9 es comercial y no sublicenciable; core/0110 fija la suite en v8 (Apache-2.0) y el Tracker cumplía por accidente con versiones exactas 8.3.1. Decisión local: rango acotado [8.3.1,9.0.0) en los tres PackageReference + guarda MSBuild solución-wide (src/apps/tracker-api/Directory.Build.targets, error EVOLITH0110) que falla el build si alguien reescribe la versión a mano. El modo de fallo que cierra no es un build roto, sino un build en verde embarcando licencia no redistribuible. Ampliar el rango exige superseder antes core/0110. |
| T-043 | Helm supersede a Kustomize como formato de orquestación K8s | Definir | Accepted | T-043 | Supersede T-030. Ratifica una decisión ya tomada en el código y jamás registrada: tres charts Helm (product/infra/helm/) son los únicos artefactos desplegables y no existe ningún kustomization*. Alcance limitado al formato de empaquetado; no decide GitOps, gestión de secretos ni destino de despliegue. Cierra la premisa falsa que ARCHITECTURE_REVIEW.md (:155, :696, :714) inyectó en el board vía AR-03 (CD-29). |
| T-044 | Un solo modelo de aislamiento entre tenants, con TenantScope como autoridad |
Definir | Accepted | T-044 | Hay TRES implementaciones de la misma regla (aspecto, TenantScope, filtro EF). El fallo-abierto que las hacía peligrosas se cerró en SEC-01; lo que queda es incoherencia funcional. Requiere ratificación del PO: alinear el aspecto con TenantScope amplía lo que un operador de plataforma alcanza en tenant-intelligence, y eso es decisión de producto, no técnica. |
| T-045 | Frontera de conectores: el Core posee la forma canónica; el Tracker posee conexión, credencial, fetch y sync | Definir | Accepted | T-045 | Verificado en el repositorio del Core: sus tres formas canónicas se declaran PURE y excluyen el fetch como paso de conector, y authorizesPhaseTransition: false está horneado en el tipo. Sin esta frontera el Tracker re-derivaría en .NET un mapeo ya canonizado, y el día que divergieran ganaría la copia local. Reduce el alcance de EAG-18/19/20/22 a la vez. |
| T-046 | El Tracker habla REST con el Core mediante cliente propio tipado; el SDK publicado es sólo Node | Definir | Accepted | T-046 | @evolith/sdk-client nunca existió —verificado contra npm— y el SDK real es una librería Node que un backend .NET no puede consumir en proceso. El cliente propio no es un apaño: es el binding correcto. Cierra CR-05. |
| T-047 | Topología de esquemas consolidada: 4 schemas, una convención, una excepción declarada | Adoptar | Accepted | T-047 | Supera a T-028. Ratifica los 4 schemas reales, renombra geo→tracker_geo, y declara masterdata como excepción CON MOTIVO: no es un contexto del Tracker sino una réplica de MMS. Cierra COH-015. |
| T-048 | Contrato de evidencia de señal de calidad: mapeo, semántica y quién la produce | Adoptar | Accepted | T-048 | Cierra CD-07, CD-08 y CD-09 como una sola conversación. dimension y determinism pasan a columna; metrics/findings[] se proyectan. La señal es advisory en el cable y exigible por GatePolicy. El Tracker NO ejecuta productores — eso es del agent-runtime (ADR-0111). |
| T-049 | Transporte de observabilidad: scrape Prometheus para métricas, push OTLP para trazas | Adoptar | Accepted | T-049 | Supera a T-029, que estaba en Definir SIN ADR — un boceto sin ratificar, no una decisión incumplida. Las trazas no tienen alternativa a push; las métricas siguen tirándose para no atar la disponibilidad del producto a la de un sumidero de telemetría. Exportador OTLP apagado por defecto. Cierra AR-14. |
| T-050 | Taxonomía del dominio: cinco fases, una maquinaria, una frontera, un resultado y unos cimientos | Adoptar | Accepted | T-050 | Sustituye el listado de «9 bounded contexts pares», que nunca describió este producto: Gobernanza sola tiene 8 agregados frente a 3 de las cinco fases juntas, y cinco contextos construidos (Tenancy, Products, Intake, Audit, Geo) no figuraban en él. Integración es FRONTERA bidireccional, no resultado; Sdlc (la vía) y Governance (el árbitro) siguen separados porque T-031 explota esa distinción. Parte AR-05 en vocabulario de fase (mejora sobre lo que ya funciona en genérico) y capa de resultado (Metrics, inexistente). |
| T-051 | El gateway MCP se enlaza al BFF del tracker-api, no al Core | Definir | Superseded | T-051 | Superado por T-052. Corrigió que las 6 tools MCP del Tracker tiraran de rutas imaginadas del Core, enlazándolas al BFF; su sujeto (el servidor MCP del Tracker) se eliminó después. |
| T-052 | Un solo MCP del ecosistema (el del Core); el gateway consume, no compite | Definir | Accepted | T-052 | Elimina el servidor MCP del Tracker; el gateway es BFF agregador que consume core-api (REST) y evolith-mcp (cliente). Supera a T-051. Verificado en vivo. |
| T-053 | Consumir la identidad UMS: JWKS/OIDC preferido, simétrico como interino | Definir | Proposed | T-053 | El tracker-api valida OIDC/JWKS pero el UMS no lo expone. Preferir que el UMS publique JWKS (aguas arriba); interino simétrico implementado y verificado con mock. |
| T-054 | Gate de frontera en tiempo de edición: adoptar acotado, diferido a EAG-11 | Definir | Accepted | T-054 | Adopta el edit-gate del Core pero DIFERIDO: .claude/ está en .gitignore, EAG-11 aún no da la fuente única de reglas, y el matcher puede misfirear. Activación condicionada a EAG-11 + versionar .claude/settings.json + acotar rutas + vía de escape. |
| T-055 | El Core deposita sus veredictos; el Tracker posee el ledger y deriva el tenant de la clave | Definir | Accepted | T-055 | Segunda dirección del tráfico de evidencia: POST /core-evaluation-transactions autenticado por clave de máquina atada al esquema POR NOMBRE, permiso propio :ingest SIN :read, tenant derivado de QUÉ CLAVE encajó (un tenantId en el cuerpo se rechaza con 400, no se ignora), idempotencia por (tenant, correlationId) respaldada por índice único, motor de cada regla VERBATIM (vocabulario abierto) y los dos responsables —quien pidió y quien debe arreglar— en columnas distintas. Estado ingested, distinto de completed. El DTO derivado a mano cumple T-038 con guarda de deriva por fixture. Sigue siendo advisory (T-039). Cierra GT-604. |
| T-056 | El sellado no lleva lógica, la validación de contenido es del tenant, y la IA propone pero nunca decide | Definir | Accepted | T-056 | Tres capas con frontera dura, ratificadas al cablear GT-588. (1) El sellado no mira el contenido: payload es opaco y lo único que se fija es la regla de ESCRITURA (claves ordenadas en toda profundidad, comparación ordinal para que el idioma de la máquina no las reordene, una codificación). Por eso la interoperabilidad C#/TypeScript no crea un problema de estandarización por tenant — un notario sella documentos distintos con un procedimiento invariable. (2) La validación de contenido la configura el tenant: en cuanto la semántica de un cliente llega al motor como código, el motor pasa a ser un catálogo de casos particulares y cada cliente nuevo es una release. Misma frontera que T-039 aplicada al contenido. (3) La IA propone, nunca decide: un decisor probabilístico vuelve la garantía incomprobable, porque dos ejecuciones podrían diferir y nadie sabría cuál vale; detrás tiene que haber un verificador determinista y, si la propuesta es probabilística, su confianza se muestra a quien la ratifica (GT-590, GT-584). |
| T-057 | The tenant signs what it asserts, the platform attests that it was recorded | Definir | Accepted | T-057 | Two signing identities, and only one belongs to the tenant. The ISSUER key is the tenant's and lives in tenant configuration, alongside the gate matrix and the artifact overlay — it is the tenant's own claim, so it is the tenant's key. Per-tenant keys make custody harder, not easier (N to store and rotate), and that cost is accepted because it is what makes each tenant independent. The TRANSPARENCY SERVICE key stays with the platform: RFC 9943 puts that authority in a SEPARATE entity, and MerkleTransparencyService already says why — a statement and a receipt signed with the same key are a self-assertion with ceremony, so a tenant signing its own receipt could rewrite its history and re-sign it. Neither key is what makes the trail immutable: that is the append-only audit table (MakeAuditEntriesAppendOnly); the signature adds attributable provenance, not immutability. AUD-TRANSP-04 already refuses to call it non-repudiation when both identities resolve to the same key. |
Plantilla para nuevos ADRs: Al crear un nuevo documento para "ADR Local", utilice el esquema de Frontmatter definido en los estándares de Evolith Core y ubíquelo en la carpeta de gobernanza correspondiente.