From 8aafeabe1fb1b13ea73526eddec1b2dcf000f62f Mon Sep 17 00:00:00 2001 From: aarroyo Date: Sat, 22 Aug 2026 19:38:10 -0500 Subject: [PATCH 1/4] docs(adr): the tenant master is UMS, and ADR-0106's MMS was never built MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0106 has been Accepted since 2026-07-08 naming MMS "the sole owner of the Master Tenant identity and lifecycle", with MMS publishing a projection to UMS and the Tracker. MMS was never built. On 2026-08-22 both satellites recorded the opposite: ADR-UMS-107 makes UMS the master and supersedes UMS-083, and T-059 removes the Tracker's MMS consumer, its contract, its read-model migrations and its MasterDataDb requirement. Core owns none of that code but publishes the corpus that describes it, so this repository was still the place where a reader found an accepted architecture for a system nobody will build. ADR-0129 records where the boundary sits, supersedes 0106 rather than amending it — the MMS reasoning stays legible, which is the point of a corpus — and declares implementation-status none, because Core neither emits nor consumes the snapshot and ADR-0101 keeps it that way. The sweep covers what described CURRENT behaviour: - the glossary, which defined MMS as a product and gave it the tenant - both ADR indexes - the poison-message runbook, which sent an operator to shovel two queues that do not exist; the Tracker's consumer endpoint is tracker.tenant-snapshot, verified in T-059's ConsumerDefinition - two Prometheus rules built on the same two dead queue names - two source comments listing MMS as a .NET suite product The runbook also named masterdata_projection_applied_total and its discarded twin. Neither has an emitter — not in the Tracker after T-059 and not before it — so the verify step now reads queue depth and the ums_projection_version column, which do exist. ADRs 0107, 0108, 0109 and 0110 name MMS in their historical context and are deliberately left unamended, for the same reason 0106 keeps its text. Co-Authored-By: Claude Opus 5 --- ...44-adr-implementation-status.baseline.json | 2 - .../operations/alerts/prometheus-alerts.yml | 24 +-- ...nt-response-poison-message-reprocess.es.md | 37 +++-- ...ident-response-poison-message-reprocess.md | 39 +++-- reference/core/architecture/adrs/README.es.md | 3 +- reference/core/architecture/adrs/README.md | 3 +- ...06-master-tenant-context-projections.es.md | 5 +- .../0106-master-tenant-context-projections.md | 4 +- .../core/0129-ums-is-the-tenant-master.es.md | 151 ++++++++++++++++++ .../core/0129-ums-is-the-tenant-master.md | 147 +++++++++++++++++ .../core/architecture/adrs/core/README.es.md | 3 +- .../core/architecture/adrs/core/README.md | 3 +- .../sdlc/glossary/glossary-ecosystem.es.md | 5 +- .../core/sdlc/glossary/glossary-ecosystem.md | 5 +- .../adapters/netarchtest-adapter.ts | 2 +- ...-tenant-and-context-projections.rules.json | 2 +- ...the-suite-projects-a-versioned-.rules.json | 30 ++++ .../commands/architecture/scaffold.command.ts | 2 +- 18 files changed, 409 insertions(+), 58 deletions(-) create mode 100644 reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.es.md create mode 100644 reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md create mode 100644 src/rulesets/adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json diff --git a/.harness/scripts/ci/44-adr-implementation-status.baseline.json b/.harness/scripts/ci/44-adr-implementation-status.baseline.json index d9969fdd6..d6e1420b9 100644 --- a/.harness/scripts/ci/44-adr-implementation-status.baseline.json +++ b/.harness/scripts/ci/44-adr-implementation-status.baseline.json @@ -133,8 +133,6 @@ "reference/core/architecture/adrs/core/0102-evolith-agent-runtime.md", "reference/core/architecture/adrs/core/0103-architecture-planning-gate-intake.es.md", "reference/core/architecture/adrs/core/0103-architecture-planning-gate-intake.md", - "reference/core/architecture/adrs/core/0106-master-tenant-context-projections.es.md", - "reference/core/architecture/adrs/core/0106-master-tenant-context-projections.md", "reference/core/architecture/adrs/core/0107-single-cluster-kubernetes-deployment-topology.es.md", "reference/core/architecture/adrs/core/0107-single-cluster-kubernetes-deployment-topology.md", "reference/core/architecture/adrs/core/0108-masstransit-owned-message-topology.es.md", diff --git a/product/operations/alerts/prometheus-alerts.yml b/product/operations/alerts/prometheus-alerts.yml index 5d04e42a4..891e077a4 100644 --- a/product/operations/alerts/prometheus-alerts.yml +++ b/product/operations/alerts/prometheus-alerts.yml @@ -143,7 +143,7 @@ groups: summary: "Error budget burning too fast (6h window)" description: "6h burn rate {{ $value | humanizePercentage }} exceeds 6x threshold" - # Master-data projection messaging (ADR-0108 / deployment strategy §5.3, GT-463). + # Tenant-snapshot projection messaging (ADR-0108 / ADR-0129 / deployment strategy §5.3, GT-463). # MassTransit moves a message that exhausts its retries into `_error` — it never # nacks, so the broker DLX never fires. ANY message in an `_error` queue is a poison # message that needs a human: alert on depth > 0 (not a backlog threshold). @@ -166,7 +166,7 @@ groups: # on a series nobody emits never fires, and a rule that cannot fire is worse than no rule # because it reads as coverage. # - # The bus going away is LOSSLESS, not incorrect: MMS's transactional outbox commits and + # The bus going away is LOSSLESS, not incorrect: UMS's transactional outbox commits and # drains on reconnect, consumers idle and catch up, and readiness never gates on the broker # (deployment strategy §5.4). So these are freshness alerts, and their severity says so. - alert: MessageBusDisconnected @@ -181,39 +181,41 @@ groups: service: messaging annotations: summary: "The message bus is unreachable" - description: "No `rabbitmq_queue_messages` series is being reported: the broker or its exporter is unreachable. Master-data projection is FROZEN, not lost — the producer's outbox commits and drains on reconnect. Correctness is unaffected; freshness is not." + description: "No `rabbitmq_queue_messages` series is being reported: the broker or its exporter is unreachable. Tenant-snapshot projection is FROZEN, not lost — the producer's outbox commits and drains on reconnect. Correctness is unaffected; freshness is not." runbook_url: "https://github.com/beyondnetcode/evolith/blob/main/product/operations/incident-response-poison-message-reprocess.md" - alert: TenantProjectionLag - # A projection queue in steady state is near-empty: the consumer drains it as fast as MMS + # A projection queue in steady state is near-empty: the consumer drains it as fast as UMS # publishes. Sustained depth means the consumer is not keeping up, which is the freshness # symptom this row exists for. # # The threshold is SEMANTIC, not measured: `> 0 for 10m` means "not draining", which is a # property of the queue's purpose rather than a percentile nobody has taken. The generic - # `RabbitMQQueueDepth` above only fires at 1000, and on a tenant-projection queue that + # `RabbitMQQueueDepth` above only fires at 1000, and on a tenant-snapshot queue that # number means freshness has been broken for a long time already. Re-tune this from real # traffic once there is any — and until then, an honest semantic bound beats a fabricated # numeric one. - expr: rabbitmq_queue_messages{queue=~".*\\.tenant-projection"} > 0 + # One consumer, one queue: UMS publishes the snapshot and consumes nothing of it + # (ADR-0129), so this names `tracker.tenant-snapshot` rather than matching a family. + expr: rabbitmq_queue_messages{queue="tracker.tenant-snapshot"} > 0 for: 10m labels: severity: warning service: messaging annotations: - summary: "Tenant projection is lagging on {{ $labels.queue }}" - description: "{{ $labels.queue }} has held {{ $value }} undelivered message(s) for 10 minutes. A projection queue in steady state is near-empty, so this means the consumer is not draining. Tenant master data served by this product is STALE, not wrong." + summary: "The tenant snapshot is lagging on {{ $labels.queue }}" + description: "{{ $labels.queue }} has held {{ $value }} undelivered message(s) for 10 minutes. A projection queue in steady state is near-empty, so this means the consumer is not draining. The tenant data this product serves is STALE, not wrong." runbook_url: "https://github.com/beyondnetcode/evolith/blob/main/product/operations/incident-response-poison-message-reprocess.md" # A projection consumer endpoint that never binds its queue means the pod is Ready but # the consumer is dead (the 406 redeclare failure ADR-0108 retired the CRDs to prevent). - alert: TenantProjectionQueueMissing - expr: absent(rabbitmq_queue_messages{queue="ums.tenant-projection"}) or absent(rabbitmq_queue_messages{queue="tracker.tenant-projection"}) + expr: absent(rabbitmq_queue_messages{queue="tracker.tenant-snapshot"}) for: 5m labels: severity: critical service: messaging annotations: - summary: "A tenant-projection consumer endpoint queue is absent" - description: "ums.tenant-projection and/or tracker.tenant-projection is not declared on the broker — a consumer endpoint failed to start (dead consumer behind a Ready pod)." + summary: "The tenant-snapshot consumer endpoint queue is absent" + description: "tracker.tenant-snapshot is not declared on the broker — the consumer endpoint failed to start (dead consumer behind a Ready pod)." runbook_url: "https://github.com/beyondnetcode/evolith/blob/main/product/operations/incident-response-poison-message-reprocess.md" diff --git a/product/operations/incident-response-poison-message-reprocess.es.md b/product/operations/incident-response-poison-message-reprocess.es.md index 8c094868a..7d3b7f9b9 100644 --- a/product/operations/incident-response-poison-message-reprocess.es.md +++ b/product/operations/incident-response-poison-message-reprocess.es.md @@ -2,7 +2,7 @@ > **Navegación bilingüe:** [View English version](./incident-response-poison-message-reprocess.md) -Playbook operativo para mensajes fallidos de la proyección de datos maestros. Se dispara desde la +Playbook operativo para mensajes fallidos de la proyección del retrato de tenant. Se dispara desde la alerta `MassTransitPoisonMessages` (estrategia de despliegue §5.3 · ADR-0108 · GT-463). ## Qué lo disparó @@ -10,10 +10,14 @@ alerta `MassTransitPoisonMessages` (estrategia de despliegue §5.3 · ADR-0108 MassTransit reintenta un consumo fallido (exponencial, 5 intentos). Agotados los reintentos, **mueve** el mensaje a `_error` — nunca hace `nack`, así que el dead-letter exchange del broker jamás se dispara. **Cualquier** mensaje en una cola `_error` es un mensaje veneno que un -consumidor no pudo procesar. Las colas afectadas para la proyección de tenant son: +consumidor no pudo procesar. El retrato de tenant tiene exactamente un consumidor, así que solo una +cola puede guardar veneno suyo: -- `ums.tenant-projection_error` -- `tracker.tenant-projection_error` +- `tracker.tenant-snapshot_error` + +UMS publica el retrato y no consume nada de él (ADR-0129 · ADR-UMS-107), así que no hay cola `ums.*` +de proyección que shovelear. Las `ums.tenant-projection_error` y `tracker.tenant-projection_error` +que este playbook nombró hasta el 2026-08-22 eran del consumidor de la era MMS que T-059 retiró. ## Severidad @@ -41,31 +45,34 @@ Cuando la causa sea transitoria o esté corregida, mueve los mensajes de vuelta ```bash kubectl exec -n evolith-messaging evolith-rabbitmq-server-0 -c rabbitmq -- \ - rabbitmqctl set_parameter shovel reprocess-ums-projection \ - '{"src-protocol":"amqp091","src-uri":"amqp:///","src-queue":"ums.tenant-projection_error", - "dest-protocol":"amqp091","dest-uri":"amqp:///","dest-queue":"ums.tenant-projection", + rabbitmqctl set_parameter shovel reprocess-tenant-snapshot \ + '{"src-protocol":"amqp091","src-uri":"amqp:///","src-queue":"tracker.tenant-snapshot_error", + "dest-protocol":"amqp091","dest-uri":"amqp:///","dest-queue":"tracker.tenant-snapshot", "src-delete-after":"queue-length","ack-mode":"on-confirm"}' ``` - `src-delete-after: queue-length` drena solo los mensajes presentes al iniciar el shovel (no hace bucle infinito con los que fallen después). -- Repite con `tracker.tenant-projection_error` → `tracker.tenant-projection` para Tracker. -- Borra el shovel al terminar: `rabbitmqctl clear_parameter shovel reprocess-ums-projection`. +- Borra el shovel al terminar: `rabbitmqctl clear_parameter shovel reprocess-tenant-snapshot`. ## Verificar 1. La profundidad de la cola `_error` vuelve a 0 y la alerta `MassTransitPoisonMessages` se limpia. -2. La proyección se pone al día: `masterdata_projection_applied_total` sube; sin un alza equivalente - en `masterdata_projection_discarded_total` más allá de los descartes esperados por la guarda de stale. -3. Verifica puntualmente el/los tenant(s) afectados en `masterdata.tenant_projection` (UMS + Tracker) - contra MMS. +2. La proyección se pone al día: `tracker.tenant-snapshot` drena y se queda casi vacía. No hay + contador de aplicados/descartados que leer — las métricas `masterdata_projection_*` de la era MMS + que aquí se nombraban hasta el 2026-08-22 nunca tuvieron emisor, y T-059 no introdujo + sustitutas. La señal es la profundidad de la cola. +3. Verifica puntualmente el/los tenant(s) afectados: la fila de `tenants` del Tracker lleva el + `ums_projection_version` que UMS publicó por última vez, y `code`/`name`/`status` coinciden con + UMS, que es el maestro (ADR-0129). ## Prevenir recurrencia - Veneno-por-contenido persistente → el productor emitió un evento fuera de contrato: reconcilia contra `Evolith.Messaging.Contracts` (DS-12) y añade un contract test del productor. -- Faults transitorios repetidos → revisa la salud de la BD del consumidor (`MasterDataDb`) y la - postura de readiness (readiness nunca debe condicionarse a AMQP — §5.4). +- Faults transitorios repetidos → revisa la base de datos del Tracker donde escribe el consumidor + (T-059 retiró la cadena de conexión aparte `MasterDataDb`; la proyección aterriza en el esquema + propio del Tracker) y la postura de readiness (readiness nunca debe condicionarse a AMQP — §5.4). ## Referencias - Estrategia de despliegue §5.3 (manejo de veneno) · ADR-0108 (topología propiedad de MassTransit) · GT-463. diff --git a/product/operations/incident-response-poison-message-reprocess.md b/product/operations/incident-response-poison-message-reprocess.md index 899390007..4dcf980f7 100644 --- a/product/operations/incident-response-poison-message-reprocess.md +++ b/product/operations/incident-response-poison-message-reprocess.md @@ -2,7 +2,7 @@ > **Bilingual Navigation:** [Versión en Español](./incident-response-poison-message-reprocess.es.md) -Operational playbook for handling faulted master-data projection messages. Fires from the +Operational playbook for handling faulted tenant-snapshot projection messages. Fires from the `MassTransitPoisonMessages` alert (deployment strategy §5.3 · ADR-0108 · GT-463). ## What triggered this @@ -10,10 +10,15 @@ Operational playbook for handling faulted master-data projection messages. Fires MassTransit retries a failing consume (exponential, 5 attempts). When retries are exhausted it **moves** the message to `_error` — it never `nack`s, so the broker's dead-letter exchange never fires. **Any** message sitting in an `_error` queue is a poison message a consumer -could not process. The affected queues for the tenant projection are: +could not process. The tenant snapshot has exactly one consumer, so exactly one queue can hold +poison for it: -- `ums.tenant-projection_error` -- `tracker.tenant-projection_error` +- `tracker.tenant-snapshot_error` + +UMS publishes the snapshot and consumes nothing of it (ADR-0129 · ADR-UMS-107), so there is no +`ums.*` projection queue to shovel. The `ums.tenant-projection_error` and +`tracker.tenant-projection_error` this playbook named until 2026-08-22 belonged to the MMS-era +consumer that T-059 removed. ## Severity @@ -27,7 +32,7 @@ could not process. The affected queues for the tenant projection are: 1. Inspect a faulted message (RabbitMQ management UI → the `_error` queue → *Get messages*, requeue=true): ```bash kubectl port-forward -n evolith-messaging svc/evolith-rabbitmq 15672:15672 - # open http://localhost:15672 → Queues → ums.tenant-projection_error + # open http://localhost:15672 → Queues → tracker.tenant-snapshot_error ``` 2. Read the MassTransit fault headers (`MT-Fault-Message`, `MT-Fault-StackTrace`) on the message. 3. Classify the root cause **before** reprocessing: @@ -41,30 +46,34 @@ Once the root cause is transient or fixed, move the messages back with a **dynam ```bash kubectl exec -n evolith-messaging evolith-rabbitmq-server-0 -c rabbitmq -- \ - rabbitmqctl set_parameter shovel reprocess-ums-projection \ - '{"src-protocol":"amqp091","src-uri":"amqp:///","src-queue":"ums.tenant-projection_error", - "dest-protocol":"amqp091","dest-uri":"amqp:///","dest-queue":"ums.tenant-projection", + rabbitmqctl set_parameter shovel reprocess-tenant-snapshot \ + '{"src-protocol":"amqp091","src-uri":"amqp:///","src-queue":"tracker.tenant-snapshot_error", + "dest-protocol":"amqp091","dest-uri":"amqp:///","dest-queue":"tracker.tenant-snapshot", "src-delete-after":"queue-length","ack-mode":"on-confirm"}' ``` - `src-delete-after: queue-length` drains only the messages present when the shovel starts (does not loop forever on newly-faulted ones). -- Repeat with `tracker.tenant-projection_error` → `tracker.tenant-projection` for Tracker. -- Delete the shovel when done: `rabbitmqctl clear_parameter shovel reprocess-ums-projection`. +- Delete the shovel when done: `rabbitmqctl clear_parameter shovel reprocess-tenant-snapshot`. ## Verify 1. `_error` queue depth returns to 0 and the `MassTransitPoisonMessages` alert clears. -2. The projection catches up: `masterdata_projection_applied_total` increases; no matching rise in - `masterdata_projection_discarded_total` beyond the expected stale-guard discards. -3. Spot-check the affected tenant(s) in `masterdata.tenant_projection` (UMS + Tracker) match MMS. +2. The projection catches up: `tracker.tenant-snapshot` drains and stays near-empty. There is no + applied/discarded counter to read — the MMS-era `masterdata_projection_*` metrics named here + until 2026-08-22 never had an emitter, and T-059 did not introduce replacements. Queue depth is + the signal. +3. Spot-check the affected tenant(s): the Tracker's `tenants` row carries the + `ums_projection_version` UMS last published, and `code`/`name`/`status` match UMS, which is the + master (ADR-0129). ## Prevent recurrence - Persistent poison-by-content → the producer emitted an out-of-contract event: reconcile against `Evolith.Messaging.Contracts` (DS-12) and add a producer contract test. -- Repeated transient faults → check the consumer's DB (`MasterDataDb`) health and the readiness - posture (readiness must never gate on AMQP — §5.4). +- Repeated transient faults → check the Tracker database the consumer writes to (T-059 removed the + separate `MasterDataDb` connection string; the projection lands in the Tracker's own schema) and + the readiness posture (readiness must never gate on AMQP — §5.4). ## References - Deployment strategy §5.3 (poison handling) · ADR-0108 (MassTransit-owned topology) · GT-463. diff --git a/reference/core/architecture/adrs/README.es.md b/reference/core/architecture/adrs/README.es.md index c77f58841..bd4634e9d 100644 --- a/reference/core/architecture/adrs/README.es.md +++ b/reference/core/architecture/adrs/README.es.md @@ -77,7 +77,8 @@ Decisiones aplicables a cualquier producto construido sobre el framework, sin im | [ADR 0103: Architecture Planning Gate](./core/0103-architecture-planning-gate-intake.es.md) | Architecture Planning Gate como intake de pre-discovery | Establecer la etapa de admisión temprana | ADR Core | Sí | | [ADR 0104: Gobernanza Advisory en Fase de Diseño](./core/0104-topology-driven-advisory-design-governance.es.md) | Gobernanza en diseño por topologías y blueprints | Gobernar composición mediante blueprints | ADR Core | Sí | | [ADR 0105: Proyección de Conocimiento OKF](./core/0105-okf-knowledge-projection.es.md) | OKF como proyección portable del Knowledge OS | Estandarizar intercambio de conocimiento | ADR Core | Sí | -| [ADR 0106: Tenant Maestro y Proyecciones por Contexto](./core/0106-master-tenant-context-projections.es.md) | Registro de Tenant maestro en MMS y proyecciones de contexto | Aislar contextos de inquilino de forma segura | ADR Core | Sí | +| [ADR 0106: Tenant Maestro y Proyecciones por Contexto](./core/0106-master-tenant-context-projections.es.md) | Registro de Tenant maestro en MMS y proyecciones de contexto — **superseded por ADR-0129**; MMS nunca se construyó | Aislar contextos de inquilino de forma segura | ADR Core | No | +| [ADR 0129: El maestro de Tenant es UMS](./core/0129-ums-is-the-tenant-master.es.md) | UMS posee el tenant y publica un retrato versionado que la suite proyecta | Poner la frontera del tenant donde está el código | ADR Core | Sí | --- diff --git a/reference/core/architecture/adrs/README.md b/reference/core/architecture/adrs/README.md index d28ef99c1..1e3f58b2b 100644 --- a/reference/core/architecture/adrs/README.md +++ b/reference/core/architecture/adrs/README.md @@ -77,7 +77,8 @@ Decisions applicable to any product built on top of the framework, regardless of | [ADR 0103: Architecture Planning Gate](./core/0103-architecture-planning-gate-intake.md) | Architecture Planning Gate as Pre-Discovery Intake | Establish early governance intake | Core ADR | Yes | | [ADR 0104: Advisory Design-Phase Governance](./core/0104-topology-driven-advisory-design-governance.md) | Topology-Driven Advisory Design-Phase Governance | Govern composition via blueprints | Core ADR | Yes | | [ADR 0105: OKF Knowledge Projection](./core/0105-okf-knowledge-projection.md) | OKF as the Portable Projection of the Knowledge OS | Standardize knowledge interchange | Core ADR | Yes | -| [ADR 0106: Master Tenant and Context Projections](./core/0106-master-tenant-context-projections.md) | Master Tenant Registry in MMS and Context Projections | Isolate tenant contexts safely | Core ADR | Yes | +| [ADR 0106: Master Tenant and Context Projections](./core/0106-master-tenant-context-projections.md) | Master Tenant Registry in MMS and Context Projections — **superseded by ADR-0129**; MMS was never built | Isolate tenant contexts safely | Core ADR | No | +| [ADR 0129: The Tenant Master Is UMS](./core/0129-ums-is-the-tenant-master.md) | UMS owns the tenant and publishes a versioned snapshot the suite projects | Put the tenant boundary where the code is | Core ADR | Yes | --- diff --git a/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.es.md b/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.es.md index 8bc1391a9..afe6fb7be 100644 --- a/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.es.md +++ b/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.es.md @@ -5,7 +5,10 @@ > **Firma del Agente:** Agente Arquitecto (Winston) ## Estado -Accepted +Superseded by [ADR-0129](./0129-ums-is-the-tenant-master.es.md) — 2026-08-22. +MMS, nombrado más abajo como único propietario del Tenant maestro, nunca se construyó. UMS posee el +tenant y lo publica; ver la decisión que lo supersede. El texto de abajo se conserva tal cual se +escribió, sin enmendar. ## Fecha 2026-07-08 diff --git a/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.md b/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.md index 8c4ceda9c..68435f2e9 100644 --- a/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.md +++ b/reference/core/architecture/adrs/core/0106-master-tenant-context-projections.md @@ -5,7 +5,9 @@ > **Agent Signature:** Architect Agent (Winston) ## Status -Accepted +Superseded by [ADR-0129](./0129-ums-is-the-tenant-master.md) — 2026-08-22. +MMS, named below as the sole owner of the Master Tenant, was never built. UMS owns the tenant and +publishes it; see the superseding decision. The text below is kept as written, unamended. ## Date 2026-07-08 diff --git a/reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.es.md b/reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.es.md new file mode 100644 index 000000000..b358dbd3c --- /dev/null +++ b/reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.es.md @@ -0,0 +1,151 @@ +> **Navegación Bilingüe:** [See English Version](./0129-ums-is-the-tenant-master.md) + +# ADR-0129: El maestro de Tenant es UMS, y la suite proyecta un retrato versionado + +## Estado + +Accepted — 2026-08-22. En vigor. Supersede a [ADR-0106](./0106-master-tenant-context-projections.es.md). + + +> **Estado de implementación en este repositorio: ninguna** (2026-08-22). Evolith Core ni emite ni +> consume el retrato del tenant: es un motor de evaluación sin estado y recibe el tenant como +> contexto ([ADR-0101](./0101-core-stateless-evaluation-engine.es.md)). Este ADR deja escrito dónde +> está ahora la frontera para que el corpus del Core deje de nombrar un sistema que no existe. El +> emisor vive en UMS y el consumidor en el Tracker; ambos se nombran en Referencias. + +## Fecha + +2026-08-22 + +## Contexto y Problema + +[ADR-0106](./0106-master-tenant-context-projections.es.md) se aceptó el 2026-07-08 y nombraba a **MMS** +(Master Data Management) «único propietario de la identidad y el ciclo de vida del Tenant maestro», +con MMS publicando una `TenantProjection` hacia UMS y hacia el Evolith Tracker. + +**MMS nunca existió** — ni en BeyondNetCode ni en la suite Evolith. La decisión se escribió contra un +sistema que jamás se construyó, y cada satélite pagó el coste a su manera: + +- UMS cargaba con ADR-UMS-083, que declaraba a UMS *consumidor* de un tenant maestro que en realidad + posee: el agregado, los comandos de alta, modificación, activación y suspensión, y los endpoints que + los exponen viven en UMS. Sus pruebas arrastraban parches cuyo único fin era neutralizar una + proyección que nadie alimentaba. +- El Tracker cargaba con `TenantProjectionConsumer`, escuchando + `Evolith.Contracts.MasterData.TenantEvent` y escribiendo `masterdata.tenant_projection` — un + consumidor que jamás recibió un mensaje, hacia una tabla que jamás tuvo lector. + +El 2026-08-22 ambos satélites invirtieron la dirección: **ADR-UMS-107** supersede a UMS-083 y hace de +UMS el maestro de Tenant, y **T-059** retira del Tracker el consumidor de MMS, su contrato, las +migraciones de su read model, su sonda de salud y el requisito `ConnectionStrings:MasterDataDb`, y los +sustituye por un consumidor de lo que UMS publica de verdad. + +Evolith Core no posee nada de ese código, pero sí posee el **corpus que describe la suite**, y ese +corpus sigue diciendo MMS: ADR-0106 como decisión Accepted viva con un ruleset generado detrás, cuatro +ADR hermanos que nombran MMS de pasada, y ocho documentos de producto —entre ellos un runbook de +incidentes y la estrategia de despliegue de la suite— que dibujan `masterdata.tenant_projection` +dentro de UMS y del Tracker. Quien hoy hace grep en este repositorio encuentra una arquitectura +aceptada para un sistema que nadie va a construir. + +## Objetivo y Alcance + +Dejar registrado, en el repositorio que publica la arquitectura de la suite, dónde está de verdad la +frontera del tenant, y retirar la descripción de MMS de todos los sitios donde aquí sobrevive. + +**Dentro del alcance:** el corpus de ADR del Core, la documentación de suite y de producto de este +repositorio, y el ruleset generado que sostiene ADR-0106. + +**Fuera del alcance, a propósito:** el comportamiento del propio Core. +[ADR-0101](./0101-core-stateless-evaluation-engine.es.md) queda intacto — el Core no persiste un +registro de tenants, no se suscribe al retrato y sigue recibiendo la identidad del tenant como +contexto de evaluación. Este ADR no mueve código en `src/`. + +## Opciones Consideradas + +**Opción 1 — Enmendar ADR-0106 en su sitio.** Reescribir su sección de decisión para nombrar a UMS. +Rechazada: borra el razonamiento que produjo la elección de MMS, y el siguiente lector no puede saber +si MMS se consideró y se descartó o si nunca se consideró. El modo de fallo de aquí —una arquitectura +escrita contra un sistema que nunca se construyó— es justo el que conviene dejar legible. + +**Opción 2 — Borrar ADR-0106.** Rechazada por la misma razón, de forma más aguda, y además rompe todos +los enlaces entrantes. + +**Opción 3 — Superseder ADR-0106 con una decisión nueva (elegida).** Es la convención que el corpus ya +usa en [ADR-0099](./0099-opa-bundle-s3-distribution.es.md): la decisión vieja conserva su texto y gana +un estado `Superseded by` que apunta hacia delante. La historia sobrevive, la respuesta actual queda +sin ambigüedad, y el ruleset generado tras 0106 pierde su respaldo Accepted honestamente. + +## Decisión y Justificación + +**UMS es el maestro de Tenant y publica su estado; el resto de la suite proyecta.** + +### El contrato es de la suite, no del emisor + +`Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent` lleva `TenantId`, `Code`, `Name`, `Status`, +`ParentTenantId`, `IsManagementOwner`, `Version`, `ChangeType`, `OccurredAtUtc` y `SpecVersion`. El +namespace es de **suite** y no del producto emisor porque MassTransit enruta por URN: el nombre del +tipo *es* el acuerdo entre las dos partes. Cualquier sistema Evolith que en el futuro proyecte tenants +—incluido este, si alguna vez se decidiera— tiene que declarar ese mismo tipo bajo ese mismo +namespace, o estará suscrito a nada. + +### Un retrato versionado, no un caudal de deltas + +El mensaje lleva estado y una `Version` monotónica, así que el consumidor hace upsert por versión y +descarta lo que llega rezagado, sin reconstruir nada y sin fiarse del orden. Un bróker reordena y +reentrega; eso es su comportamiento normal, no una anomalía. El payload es corto a propósito: +sucursales, proveedores de identidad y parámetros no viajan, porque un contrato que reflejara el +modelo interno del emisor habría que versionarlo cada vez que ese modelo crece. + +### El Core se queda fuera + +El Core evalúa; no guarda un registro de tenants. Nada en esta decisión le da uno. Es la misma +frontera que dibuja ADR-0101, y nombrar a UMS maestro no la mueve — solo cambia un propietario +ficticio por el real. + +## Evidencia y Criterios de Evaluación + +El criterio es la procedencia: una decisión sobre quién posee el tenant solo merece registrarse aquí +si los sistemas propietarios ya se comprometieron a ella en sus propios repositorios, con código +detrás. + +| Afirmación | Dónde está registrada, con su mecanismo | +|---|---| +| UMS posee el tenant y lo publica | **ADR-UMS-107** (Aceptado, 2026-08-22, supersede a UMS-083). La versión sale de la secuencia de base de datos `tenant_projection_version` — el `RowVersion` del agregado no ordena (el interceptor lo rota al azar) y la marca de tiempo empata. `nextval` avanza fuera de la transacción y deja huecos deliberados, porque un contador que reutilizara un valor tras un rollback haría que el consumidor descartara como rezagado el evento bueno. | +| La publicación es atómica con el cambio | ADR-UMS-107: los cinco comandos que mutan llaman a `ITenantSnapshotPublisher` **antes** de `SaveEntitiesAsync`, respaldados por el bus-outbox, así que el mensaje se estaciona en la misma transacción y se entrega tras el commit. A propósito no se hace desde un manejador de evento de dominio: ese despacho es post-commit y best-effort, así que el mensaje perdería la atomicidad y su pérdida sería silenciosa. | +| El Tracker proyecta en vez de duplicar | **T-059** (Aceptado, 2026-08-22). `code`/`name`/`status` y la existencia del tenant los escribe solo `TenantSnapshotConsumer`; `display_name`, `contact_email`, `tier`, `settings` y la localización siguen siendo del Tracker, porque UMS no los conoce. | +| La entrega tardía y duplicada está cubierta | T-059: `ON CONFLICT (id) DO UPDATE … WHERE ums_projection_version < EXCLUDED.ums_projection_version`. Un read-check-write dejaría pasar dos eventos en vuelo del mismo tenant, y si el de versión menor confirma el último la proyección se queda rezagada de forma permanente y silenciosa. El inbox de MassTransit deduplica por `messageId`; la guarda cubre lo que el inbox no puede. | +| MMS nunca existió | Ambos ADR lo afirman, y la evidencia es la propia ausencia: un consumidor que jamás recibió un mensaje y un read model que jamás tuvo lector. | + +## Consecuencias, Riesgos y Compromisos + +**A favor.** El corpus deja de publicar una arquitectura para un sistema que nadie va a construir. +Quien busque el maestro de tenant encuentra una sola respuesta, y coincide con el código de los dos +repositorios que lo sostienen. + +**En contra, y aceptado.** El ruleset generado de ADR-0106 +(`src/rulesets/adr/generated/adr-0106-master-tenant-and-context-projections.rules.json`; la copia del +CLI bajo `src/sdk/cli/rulesets/` la produce `copy-rulesets` y está gitignorada) lleva una única regla +`CORE-0106-01` que pide honrar lo que ahora es una decisión superseded. Regenerar la marca como +superseded en vez de borrarla, así que un satélite que hubiera fijado ese id de regla sigue +resolviéndolo y puede leer por qué ya no obliga. + +**Riesgo que se asume, no que se resuelve.** El contrato está duplicado literalmente en UMS y en el +Tracker porque `Unimar.Ums.Sdk.Contracts` tiene metadatos de paquete pero nunca se ha publicado. Dos +copias de un tipo cuyo *nombre* es la clave de enrutado divergirán en silencio — renombrar un campo no +rompe nada en tiempo de compilación en ninguno de los dos repositorios y lo rompe todo en ejecución. +Este ADR deja constancia de la exposición; el arreglo es empaquetado .NET y pertenece a esos +repositorios, no al scope npm del Core. + +## Referencias + +- ADR-UMS-107 — *UMS es el Maestro de Tenant y lo Publica para que la Suite lo Proyecte* (repo `ums`, + `reference/architecture/adrs/UMS-107-ums-publica-el-tenant-para-que-la-suite-lo-proyecte.es.md`) +- T-059 — *The Tracker projects the UMS Tenant, and stops writing what UMS owns* (repo + `evolith_tracker`, `docs/adrs/T-059-proyectar-el-tenant-de-ums.md`) +- ADR-UMS-083 — *Consumir proyección de tenant de MMS* (repo `ums`, rechazado y superseded por UMS-107) + +## Decisiones y Estándares Relacionados + +- [ADR-0101](./0101-core-stateless-evaluation-engine.es.md) — el Core es un motor de evaluación sin estado. Esta decisión no lo cambia. +- [ADR-0106](./0106-master-tenant-context-projections.es.md) — superseded por este ADR. +- [ADR-0108](./0108-masstransit-owned-message-topology.es.md) — MassTransit posee la topología de mensajes; el enrutado por URN del que depende esta decisión se describe allí. +- [ADR-0109](./0109-multi-project-satellite-governance.es.md) — cómo se relacionan las decisiones de los satélites con el corpus del Core. diff --git a/reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md b/reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md new file mode 100644 index 000000000..0f78f26eb --- /dev/null +++ b/reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md @@ -0,0 +1,147 @@ +> **Bilingual Navigation:** [Ver versión en Español](./0129-ums-is-the-tenant-master.es.md) + +# ADR-0129: The Tenant Master Is UMS, and the Suite Projects a Versioned Snapshot + +## Status + +Accepted — 2026-08-22. In force. Supersedes [ADR-0106](./0106-master-tenant-context-projections.md). + + +> **Implementation status in this repository: none** (2026-08-22). Evolith Core neither emits nor +> consumes the tenant snapshot: it is a stateless evaluation engine and receives the tenant as +> context ([ADR-0101](./0101-core-stateless-evaluation-engine.md)). This ADR records where the +> boundary now sits so the Core corpus stops naming a system that does not exist. The emitter lives +> in UMS and the consumer in the Tracker; both are named under References. + +## Date + +2026-08-22 + +## Context and Problem + +[ADR-0106](./0106-master-tenant-context-projections.md) was accepted on 2026-07-08 and named **MMS** +(Master Data Management) "the sole owner of the Master Tenant identity and lifecycle", with MMS +publishing a `TenantProjection` to UMS and to the Evolith Tracker. + +**MMS never existed** — not in BeyondNetCode and not in the Evolith suite. The decision was written +against a system that was never built, and each satellite absorbed the cost differently: + +- UMS carried ADR-UMS-083, which declared UMS a *consumer* of a tenant master + it in fact owns: the aggregate, the create/modify/activate/suspend commands and the endpoints that + expose them all live in UMS. Its tests carried patches whose only purpose was neutralising a + projection nothing fed. +- The Tracker carried `TenantProjectionConsumer`, listening for + `Evolith.Contracts.MasterData.TenantEvent` and writing `masterdata.tenant_projection` — a consumer + that never received a message, into a table that never had a reader. + +On 2026-08-22 both satellites reversed the direction: **ADR-UMS-107** supersedes UMS-083 and makes +UMS the tenant master, and **T-059** removes the Tracker's MMS consumer, its contract, its read-model +migrations, its health probe and its `ConnectionStrings:MasterDataDb` requirement, replacing them +with a consumer of what UMS actually publishes. + +Evolith Core owns none of that code, but it owns the **corpus that describes the suite**, and that +corpus still says MMS: ADR-0106 as a live Accepted decision with a generated ruleset behind it, four +sibling ADRs naming MMS in passing, and eight product documents — including an incident runbook and +the suite deployment strategy — drawing `masterdata.tenant_projection` into UMS and the Tracker. A +reader who greps this repository today finds an accepted architecture for a system nobody will build. + +## Objective and Scope + +Record, in the repository that publishes the suite's architecture, where the tenant boundary actually +sits, and retire the MMS description everywhere it survives here. + +**In scope:** the Core ADR corpus, the suite and product documentation in this repository, and the +generated ruleset carrying ADR-0106. + +**Out of scope, deliberately:** Core's own behaviour. [ADR-0101](./0101-core-stateless-evaluation-engine.md) +is untouched — the Core does not persist a tenant registry, does not subscribe to the snapshot, and +continues to receive tenant identity as evaluation context. This ADR moves no code in `src/`. + +## Options Considered + +**Option 1 — Amend ADR-0106 in place.** Rewrite its Decision section to name UMS. Rejected: it erases +the reasoning that produced the MMS choice, and the next reader cannot tell whether MMS was +considered and dropped or never considered at all. The failure mode here — an architecture written +against a system that was never built — is exactly the one worth leaving legible. + +**Option 2 — Delete ADR-0106.** Rejected for the same reason, more sharply, and it breaks every +inbound link. + +**Option 3 — Supersede ADR-0106 with a new decision (chosen).** The corpus convention already used by +[ADR-0099](./0099-opa-bundle-s3-distribution.md): the old decision keeps its text and gains a +`Superseded by` status pointing forward. History survives, the current answer is unambiguous, and the +generated ruleset behind 0106 loses its Accepted backing honestly. + +## Decision and Rationale + +**UMS is the master of Tenant and publishes its state; the rest of the suite projects.** + +### The contract belongs to the suite, not to the emitter + +`Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent` carries `TenantId`, `Code`, `Name`, +`Status`, `ParentTenantId`, `IsManagementOwner`, `Version`, `ChangeType`, `OccurredAtUtc` and +`SpecVersion`. The namespace is a **suite** namespace rather than the emitting product's, because +MassTransit routes by URN: the type name *is* the agreement between the two sides. Any Evolith system +that later projects tenants — including this one, were that ever decided — must declare that same +type under that same namespace, or it is subscribing to nothing. + +### A versioned portrait, not a stream of deltas + +The message carries state and a monotonic `Version`, so a consumer upserts by version and discards +what arrives late, reconstructing nothing and trusting no ordering. A broker reorders and redelivers; +that is its normal behaviour, not an anomaly. The payload is short on purpose — branches, identity +providers and parameters do not travel, because a contract that mirrored the emitter's internal model +would need versioning every time that model grew. + +### Core stays out of it + +The Core evaluates; it does not hold a tenant registry. Nothing in this decision gives it one. This is +the same boundary ADR-0101 draws, and naming UMS as master does not move it — it only replaces a +fictional owner with the real one. + +## Evidence and Evaluation Criteria + +The criterion is provenance: a decision about who owns the tenant is only worth recording here if the +owning systems have already committed to it in their own repositories, with code behind it. + +| Claim | Where it is recorded, with its mechanism | +|---|---| +| UMS owns the tenant and publishes it | **ADR-UMS-107** (Accepted, 2026-08-22, supersedes UMS-083). Version comes from the `tenant_projection_version` database sequence — an aggregate `RowVersion` does not order (the interceptor rotates it randomly) and a timestamp ties. `nextval` advances outside the transaction, leaving deliberate gaps, because a counter that reused a value after a rollback would make the consumer discard the good event as stale. | +| The publish is atomic with the change | ADR-UMS-107: the five mutating commands call `ITenantSnapshotPublisher` **before** `SaveEntitiesAsync`, backed by the bus outbox, so the message is parked in the same transaction and delivered after commit. It is deliberately not a domain-event handler: that dispatch is post-commit and best-effort, so the message would lose atomicity and its loss would be silent. | +| The Tracker projects rather than mirrors | **T-059** (Accepted, 2026-08-22). `code`/`name`/`status` and the tenant's existence are written only by `TenantSnapshotConsumer`; `display_name`, `contact_email`, `tier`, `settings` and localisation stay the Tracker's, because UMS does not know them. | +| Late and duplicate delivery is handled | T-059: `ON CONFLICT (id) DO UPDATE … WHERE ums_projection_version < EXCLUDED.ums_projection_version`. A read-check-write would let two in-flight events for the same tenant both pass, and if the lower version commits last the projection stays stale permanently and silently. The MassTransit inbox dedups by `messageId`; the guard covers what the inbox cannot. | +| MMS never existed | Both ADRs state it, and the evidence is the absence itself: a consumer that never received a message and a read model that never had a reader. | + +## Consequences, Risks, and Trade-offs + +**Positive.** The corpus stops publishing an architecture for a system nobody will build. A reader +grepping for the tenant master finds one answer, and it matches the code in the two repositories that +hold it. + +**Negative and accepted.** ADR-0106's generated ruleset +(`src/rulesets/adr/generated/adr-0106-master-tenant-and-context-projections.rules.json`; the CLI's +copy under `src/sdk/cli/rulesets/` is produced by `copy-rulesets` and gitignored) carries a single +`CORE-0106-01` rule asking that design honour what is now a superseded decision. Regenerating marks +it superseded rather than deleting it, so a satellite that pinned that rule id still resolves it and +can read why it no longer binds. + +**Risk carried, not solved.** The contract is duplicated literally in UMS and in the Tracker because +`Unimar.Ums.Sdk.Contracts` has package metadata but has never been published. Two copies of a type +whose *name* is the routing key will diverge silently — a renamed field breaks nothing at compile +time in either repository and breaks everything at runtime. This ADR records the exposure; the fix is +.NET packaging and belongs to those repositories, not to Core's npm scope. + +## References + +- ADR-UMS-107 — *UMS es el Maestro de Tenant y lo Publica para que la Suite lo Proyecte* (repo `ums`, + `reference/architecture/adrs/UMS-107-ums-publica-el-tenant-para-que-la-suite-lo-proyecte.es.md`) +- T-059 — *The Tracker projects the UMS Tenant, and stops writing what UMS owns* (repo + `evolith_tracker`, `docs/adrs/T-059-proyectar-el-tenant-de-ums.md`) +- ADR-UMS-083 — *Consumir proyección de tenant de MMS* (repo `ums`, rejected and superseded by UMS-107) + +## Related Decisions and Standards + +- [ADR-0101](./0101-core-stateless-evaluation-engine.md) — Core is a stateless evaluation engine. Unchanged by this decision. +- [ADR-0106](./0106-master-tenant-context-projections.md) — superseded by this ADR. +- [ADR-0108](./0108-masstransit-owned-message-topology.md) — MassTransit owns the message topology; the URN routing this decision relies on is described there. +- [ADR-0109](./0109-multi-project-satellite-governance.md) — how satellite decisions relate to Core's corpus. diff --git a/reference/core/architecture/adrs/core/README.es.md b/reference/core/architecture/adrs/core/README.es.md index a86aebda8..1f8dbd134 100644 --- a/reference/core/architecture/adrs/core/README.es.md +++ b/reference/core/architecture/adrs/core/README.es.md @@ -82,7 +82,7 @@ * [0103-architecture-planning-gate-intake](./0103-architecture-planning-gate-intake.es.md) — **Architecture Planning Gate como intake pre-Discovery** * [0104-topology-driven-advisory-design-governance](./0104-topology-driven-advisory-design-governance.es.md) — **Gobernanza advisory de Design dirigida por topología; blueprint como guía de desarrollo componible (extiende ADR-0079/0101)** * [0105-okf-knowledge-projection](./0105-okf-knowledge-projection.es.md) — **Proyección de conocimiento OKF** -* [0106-master-tenant-context-projections](./0106-master-tenant-context-projections.es.md) — **Proyecciones de contexto del Tenant maestro (MMS → UMS/Tracker)** +* [0106-master-tenant-context-projections](./0106-master-tenant-context-projections.es.md) — **Proyecciones de contexto del Tenant maestro (MMS → UMS/Tracker) — superseded por ADR-0129; MMS nunca se construyó** * [0107-single-cluster-kubernetes-deployment-topology](./0107-single-cluster-kubernetes-deployment-topology.es.md) — **Topología de despliegue Kubernetes de cluster único para la suite Evolith** * [0108-masstransit-owned-message-topology](./0108-masstransit-owned-message-topology.es.md) — **MassTransit es dueño de la topología de mensajes; los CRDs del broker son solo RBAC** * [0109-multi-project-satellite-governance](./0109-multi-project-satellite-governance.es.md) — **Gobernanza de satélites multi-proyecto (satélites monorepo); Core permanece soberano** @@ -95,6 +95,7 @@ * [0117-bilingual-parity-scope-authored-sources](./0117-bilingual-parity-scope-authored-sources.es.md) — **La paridad bilingüe aplica a fuentes AUTORADAS, nunca a proyecciones generadas; los árboles generados heredan la paridad de sus fuentes, exclusiones declaradas en código con generador + razón** * [0118-core-hub-repository-taxonomy](./0118-core-hub-repository-taxonomy.es.md) — **La raíz del hub Core tiene taxonomía propia, distinta de la de satélites: reference/ + product/ + src/ + .harness/; ADR-0070 gobierna solo satélites; la imposición es 03-validate-root-cleanliness.mjs + TAX-05** * [0128-llm-provider-abstraction-per-tenant](./0128-llm-provider-abstraction-per-tenant.es.md) +* [0129-ums-is-the-tenant-master](./0129-ums-is-the-tenant-master.es.md) — **El maestro de Tenant es UMS, que publica un retrato versionado que la suite proyecta; supersede a ADR-0106, cuyo MMS nunca se construyó** --- [Volver al Nivel Superior](../README.es.md) diff --git a/reference/core/architecture/adrs/core/README.md b/reference/core/architecture/adrs/core/README.md index f83ac909d..97c04acf8 100644 --- a/reference/core/architecture/adrs/core/README.md +++ b/reference/core/architecture/adrs/core/README.md @@ -82,7 +82,7 @@ * [0103-architecture-planning-gate-intake](./0103-architecture-planning-gate-intake.md) — **Architecture Planning Gate as pre-Discovery intake** * [0104-topology-driven-advisory-design-governance](./0104-topology-driven-advisory-design-governance.md) — **Topology-driven advisory Design-phase governance; blueprint as composable development guide (extends ADR-0079/0101)** * [0105-okf-knowledge-projection](./0105-okf-knowledge-projection.md) — **OKF knowledge projection** -* [0106-master-tenant-context-projections](./0106-master-tenant-context-projections.md) — **Master Tenant context projections (MMS → UMS/Tracker)** +* [0106-master-tenant-context-projections](./0106-master-tenant-context-projections.md) — **Master Tenant context projections (MMS → UMS/Tracker) — superseded by ADR-0129; MMS was never built** * [0107-single-cluster-kubernetes-deployment-topology](./0107-single-cluster-kubernetes-deployment-topology.md) — **Single-cluster Kubernetes deployment topology for the Evolith suite** * [0108-masstransit-owned-message-topology](./0108-masstransit-owned-message-topology.md) — **MassTransit owns the message topology; broker CRDs are RBAC-only** * [0109-multi-project-satellite-governance](./0109-multi-project-satellite-governance.md) — **Multi-project satellite governance (monorepo satellites); Core stays sovereign** @@ -95,6 +95,7 @@ * [0117-bilingual-parity-scope-authored-sources](./0117-bilingual-parity-scope-authored-sources.md) — **Bilingual parity applies to AUTHORED sources, never to generated projections; generated trees inherit their sources' parity, exclusions declared in source with generator + reason** * [0118-core-hub-repository-taxonomy](./0118-core-hub-repository-taxonomy.md) — **The Core hub root has its own taxonomy, distinct from satellites: reference/ + product/ + src/ + .harness/; ADR-0070 governs satellites only; enforcement is 03-validate-root-cleanliness.mjs + TAX-05** * [0128-llm-provider-abstraction-per-tenant](./0128-llm-provider-abstraction-per-tenant.md) +* [0129-ums-is-the-tenant-master](./0129-ums-is-the-tenant-master.md) — **The tenant master is UMS, which publishes a versioned snapshot the suite projects; supersedes ADR-0106, whose MMS was never built** --- [Back to Upper Level](../README.md) diff --git a/reference/core/sdlc/glossary/glossary-ecosystem.es.md b/reference/core/sdlc/glossary/glossary-ecosystem.es.md index 8d9e6bc30..dc93f7b1c 100644 --- a/reference/core/sdlc/glossary/glossary-ecosystem.es.md +++ b/reference/core/sdlc/glossary/glossary-ecosystem.es.md @@ -9,7 +9,7 @@ Este es el glosario oficial y canónico del ecosistema Evolith: **Evolith Core** | Término | Definición | Regla de uso canónica | |---|---|---| | Evolith Core | La plataforma/fundación (`src/packages/core`, `core-domain`, `infra-providers`, `sdk-client`, `mcp-tools`): las reglas universales más el motor de gobernanza SDLC consumido por cada producto. Es la fuente autoritativa de decisiones, estándares y patrones. | Úsalo para la fundación que los productos consumen. Nunca llames a Core un "producto de la suite"; los productos implementan Core, no lo redefinen. | -| Evolith Products (Product Suite) | Los productos construidos **sobre** Core: MMS, UMS, Evolith Tracker, Evolith CLI, Core API, Evolith MCP Services y el UMS Reference. Cada hub de producto vive bajo `product/products/` cuando está representado en este corpus. | Úsalo para cualquier producto que consuma Core. La dirección de dependencia es unidireccional: los productos consumen Core y no pueden redefinir las reglas universales de Core. | +| Evolith Products (Product Suite) | Los productos construidos **sobre** Core: UMS, Evolith Tracker, Evolith CLI, Core API, Evolith MCP Services y el UMS Reference. Cada hub de producto vive bajo `product/products/` cuando está representado en este corpus. | Úsalo para cualquier producto que consuma Core. La dirección de dependencia es unidireccional: los productos consumen Core y no pueden redefinir las reglas universales de Core. | | SDLC | El ciclo de vida de software gobernado desde la idea hasta el producto, expresado como **fases** ordenadas, cada una cerrada por un **gate**. | Úsalo para el ciclo de vida gobernado de extremo a extremo. El conjunto de fases es fijo (ver Phase); no confundas las fases SDLC con la madurez de topología (F1–F5). | | Phase | Una de las etapas SDLC canónicas y ordenadas, con los nombres autoritativos de gobernanza: `f1` Conception & Discovery (corto Discovery), `f2` Design & Architecture (corto Architecture), `f3` Construction (corto Build), `f4` Validation & QA (corto Validation), `f5` Delivery & Operations (corto Delivery). El CLI/API exponen **claves** operativas de fase (`discovery`, `design`, `construction`, `qa`, `release`) — el tipo `GatePhase` de `sdk-client` y `validate --phase` — que mapean a f1..f5 pero constituyen una diferencia conocida de etiqueta de superficie respecto a los nombres cortos de gobernanza. | Usa los nombres de fase de gobernanza (f1..f5 / Conception & Discovery … Delivery & Operations) para el modelo de ciclo de vida, y las claves operativas (`discovery`/`design`/`construction`/`qa`/`release`) al documentar el CLI/API. Una fase avanza solo cuando pasa su gate de salida; nunca uses F1–F5 como IDs de fase. | | Gate | Un punto de control de gobernanza de salida de fase que evalúa los artefactos, esquemas, rulesets, ADRs y políticas OPA requeridos antes de permitir una transición de fase. Los IDs de gate canónicos son `gate-f1` Business Sign-Off, `gate-f2` Design Baseline Approved, `gate-f3` Successful Build, `gate-f4` RC Stamped, `gate-f5` Production Live. | Úsalo para el punto de control basado en evidencia que puede bloquear una transición de fase. Usa los IDs canónicos `gate-f1`…`gate-f5`; `PG1-01`…`PG5-01` son solo alias de la API de `sdk-client`. Un gate obligatorio fallido no puede anularse por aprobación informal; solo aplica una exención de gobernanza explícita. | @@ -21,8 +21,7 @@ Este es el glosario oficial y canónico del ecosistema Evolith: **Evolith Core** | OPA | Open Policy Agent — políticas `.rego` compiladas a `policy.wasm`, evaluadas para decisiones ABAC y de gobernanza. | Úsalo para policy-as-code. Cada regla de ruleset Native debe tener una política `.rego` correspondiente; el gate de paridad falla ante drift. | | Schema | Un contrato JSON Schema que valida manifiestos y artefactos (p. ej. `topology-manifest.schema.json`, `sdlc-gate.schema.json`). | Úsalo para el contrato que valida la estructura. Los manifiestos y artefactos solo son válidos contra su schema declarado. | | Manifest | Un descriptor declarativo estructurado (`evolith.yaml`, `topology.manifest.json`, etc.) que vincula un contexto a su contrato gobernado. | Úsalo para el descriptor legible por máquina que resuelven las superficies operativas. Un manifest expone solo el contrato técnico, no datos de negocio/Funnel-0. | -| Tenant | Un contexto aislado de consumidor/organización; ABAC porta el tenant y soporta personalización por tenant. | Úsalo para aislamiento y alcance de política por tenant. Las decisiones de gobernanza se resuelven en el contexto del tenant. La identidad maestra del Tenant en la suite pertenece a MMS; UMS y Tracker mantienen proyecciones. | -| MMS | Producto de Master Data Management de la suite Evolith. Posee la identidad maestra del Tenant, la clave global del Tenant, el estado de ciclo de vida y la metadata corporativa/base. | Úsalo como fuente maestra de identidad del Tenant. No asignes autorización de usuarios ni decisiones de gobernanza SDLC a MMS. | +| Tenant | Un contexto aislado de consumidor/organización; ABAC porta el tenant y soporta personalización por tenant. | Úsalo para aislamiento y alcance de política por tenant. Las decisiones de gobernanza se resuelven en el contexto del tenant. La identidad maestra del Tenant en la suite pertenece a UMS, que publica un retrato versionado que el resto de la suite proyecta (ADR-0129). | | UMS | Producto/referencia User Management System para identidad, usuarios, membresías, perfiles, roles, permisos y grafos de autorización por tenant. | Úsalo para identidad y autorización de usuarios dentro de un Tenant. No conviertas UMS en maestro del Tenant ni en dueño de gates SDLC. | | Tracker | Evolith Tracker — el producto activo de seguimiento de gobernanza (superficies de gap-tracking más las interfaces técnicas del tracker SDLC); también posee el timing de negocio, ownership, priorización y Funnel 0 vía su ACL. | Úsalo para el producto de seguimiento de gobernanza. El timing/ROI de negocio vive en Tracker, nunca en los manifiestos de topología de Core. | | SmartCLI | El producto de tooling Evolith CLI (`src/sdk/cli`) que ejecuta validación y gobernanza desde la terminal. | Úsalo para gobernanza desde la terminal. `--phase` toma las claves operativas de fase; el legacy `--phase f1..f5` está DEPRECADO (usa `--topology`/`--arch-level`). | diff --git a/reference/core/sdlc/glossary/glossary-ecosystem.md b/reference/core/sdlc/glossary/glossary-ecosystem.md index 7fa4c634f..152879782 100644 --- a/reference/core/sdlc/glossary/glossary-ecosystem.md +++ b/reference/core/sdlc/glossary/glossary-ecosystem.md @@ -9,7 +9,7 @@ This is the official, canonical glossary for the Evolith ecosystem: **Evolith Co | Term | Definition | Canonical usage rule | |---|---|---| | Evolith Core | The platform/foundation (`src/packages/core`, `core-domain`, `infra-providers`, `sdk-client`, `mcp-tools`): the universal rules plus SDLC governance engine consumed by every product. It is the authoritative source of decisions, standards, and patterns. | Use for the foundation that products consume. Never call Core a "suite product"; products implement Core, they never redefine it. | -| Evolith Products (Product Suite) | The products built **on** Core: MMS, UMS, Evolith Tracker, Evolith CLI, Core API, Evolith MCP Services, and the UMS Reference. Each product hub lives under `product/products/` when represented in this corpus. | Use for any product that consumes Core. The dependency direction is one-way: products consume Core and cannot redefine universal Core rules. | +| Evolith Products (Product Suite) | The products built **on** Core: UMS, Evolith Tracker, Evolith CLI, Core API, Evolith MCP Services, and the UMS Reference. Each product hub lives under `product/products/` when represented in this corpus. | Use for any product that consumes Core. The dependency direction is one-way: products consume Core and cannot redefine universal Core rules. | | SDLC | The governed software lifecycle from idea to product, expressed as ordered **phases**, each closed by a **gate**. | Use for the end-to-end governed lifecycle. The phase set is fixed (see Phase); do not conflate SDLC phases with topology maturity (F1–F5). | | Phase | One of the canonical ordered SDLC stages, using the authoritative governance names: `f1` Conception & Discovery (short Discovery), `f2` Design & Architecture (short Architecture), `f3` Construction (short Build), `f4` Validation & QA (short Validation), `f5` Delivery & Operations (short Delivery). The CLI/API expose operational phase **keys** (`discovery`, `design`, `construction`, `qa`, `release`) — the `sdk-client` `GatePhase` type and `validate --phase` — which map to f1..f5 but are a known surface-label difference from the governance short names. | Use the governance phase names (f1..f5 / Conception & Discovery … Delivery & Operations) for the lifecycle model, and the operational keys (`discovery`/`design`/`construction`/`qa`/`release`) when documenting the CLI/API. A phase advances only when its exit gate passes; never use F1–F5 as phase IDs. | | Gate | A phase-exit governance checkpoint that evaluates required artifacts, schemas, rulesets, ADRs, and OPA policies before allowing a phase transition. Canonical gate IDs are `gate-f1` Business Sign-Off, `gate-f2` Design Baseline Approved, `gate-f3` Successful Build, `gate-f4` RC Stamped, `gate-f5` Production Live. | Use for the evidence-based control point that may block a phase transition. Use the canonical IDs `gate-f1`…`gate-f5`; `PG1-01`…`PG5-01` are `sdk-client` API aliases only. A failed mandatory gate cannot be overridden by informal approval; only an explicit governance waiver applies. | @@ -21,8 +21,7 @@ This is the official, canonical glossary for the Evolith ecosystem: **Evolith Co | OPA | Open Policy Agent — `.rego` policies compiled to `policy.wasm`, evaluated for ABAC and governance decisions. | Use for policy-as-code evaluation. Each Native ruleset rule must have a matching `.rego` policy; the parity gate fails on drift. | | Schema | A JSON Schema contract validating manifests and artifacts (e.g. `topology-manifest.schema.json`, `sdlc-gate.schema.json`). | Use for the contract that validates structure. Manifests and artifacts are valid only against their declared schema. | | Manifest | A structured declarative descriptor (`evolith.yaml`, `topology.manifest.json`, etc.) binding a context to its governed contract. | Use for the machine-readable descriptor that operational surfaces resolve. A manifest exposes only the technical contract, not business/Funnel-0 data. | -| Tenant | An isolated consumer/org context; ABAC carries the tenant and supports per-tenant customization. | Use for isolation and per-tenant policy scoping. Governance decisions resolve in the tenant's context. The suite master identity for a Tenant belongs to MMS; UMS and Tracker hold projections. | -| MMS | Master Data Management product for the Evolith suite. It owns Tenant master identity, global Tenant key, lifecycle status, and baseline corporate metadata. | Use for the master source of Tenant identity. Do not assign user authorization or SDLC governance decisions to MMS. | +| Tenant | An isolated consumer/org context; ABAC carries the tenant and supports per-tenant customization. | Use for isolation and per-tenant policy scoping. Governance decisions resolve in the tenant's context. The suite master identity for a Tenant belongs to UMS, which publishes a versioned snapshot the rest of the suite projects (ADR-0129). | | UMS | User Management System product/reference for identity, users, memberships, profiles, roles, permissions, and tenant-scoped authorization graphs. | Use for user identity and authorization inside a Tenant. Do not make UMS the Tenant master or the owner of SDLC gates. | | Tracker | Evolith Tracker — the active governance-tracking product (gap-tracking surfaces plus SDLC tracker technical interfaces); also owns business timing, ownership, prioritization, and Funnel 0 via its ACL. | Use for the governance-tracking product. Business timing/ROI lives in Tracker, never in Core topology manifests. | | SmartCLI | The Evolith CLI tooling product (`src/sdk/cli`) running validation and governance from the terminal. | Use for terminal-driven governance. `--phase` takes the operational phase keys; legacy `--phase f1..f5` is DEPRECATED (use `--topology`/`--arch-level`). | diff --git a/src/packages/core-domain/src/application/validators/enforcement/adapters/netarchtest-adapter.ts b/src/packages/core-domain/src/application/validators/enforcement/adapters/netarchtest-adapter.ts index 0ed5fa92f..ac3db242b 100644 --- a/src/packages/core-domain/src/application/validators/enforcement/adapters/netarchtest-adapter.ts +++ b/src/packages/core-domain/src/application/validators/enforcement/adapters/netarchtest-adapter.ts @@ -2,7 +2,7 @@ * NetArchTestAdapter (GT-524 · axis 2 — positioning §13.2). * * The second concrete enforcer adapter, targeting .NET — the suite's PRIMARY runtime - * (UMS/Tracker/MMS are .NET clean/hexagonal), which had no architecture enforcer while + * (UMS and the Tracker are .NET clean/hexagonal), which had no architecture enforcer while * only Node/TS (GT-515) did. NetArchTest is a fluent assertion library run INSIDE a * test project, so there is no standalone JSON report: this parses the `dotnet test` * console output, mapping each FAILED architecture test into one canonical diff --git a/src/rulesets/adr/generated/adr-0106-master-tenant-and-context-projections.rules.json b/src/rulesets/adr/generated/adr-0106-master-tenant-and-context-projections.rules.json index 5144c6652..91371f7fe 100644 --- a/src/rulesets/adr/generated/adr-0106-master-tenant-and-context-projections.rules.json +++ b/src/rulesets/adr/generated/adr-0106-master-tenant-and-context-projections.rules.json @@ -7,7 +7,7 @@ "audience": "core", "adrId": "ADR-0106", "adrTitle": "Master Tenant and Context Projections", - "status": "Accepted", + "status": "Superseded by [ADR-0129](./0129-ums-is-the-tenant-master.md) — 2026-08-22.", "date": "2026-07-08", "rules": [ { diff --git a/src/rulesets/adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json b/src/rulesets/adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json new file mode 100644 index 000000000..3bf38914b --- /dev/null +++ b/src/rulesets/adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json @@ -0,0 +1,30 @@ +{ + "$schema": "../../schema/ruleset-standard.schema.json", + "$id": "https://evolith.dev/rulesets/adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json", + "title": "ADR-0129 — The Tenant Master Is UMS, and the Suite Projects a Versioned Snapshot Rules (generated)", + "description": "Auto-generated ruleset encoding ADR-0129 (core track). Classification: executable. Generated by .harness/scripts/generate-adr-rulesets.mjs — do not edit by hand.", + "version": "1.0.0", + "audience": "core", + "adrId": "ADR-0129", + "adrTitle": "The Tenant Master Is UMS, and the Suite Projects a Versioned Snapshot", + "status": "Accepted — 2026-08-22. In force. Supersedes [ADR-0106](./0106-master-tenant-context-projections.md).", + "date": "2026-08-22", + "rules": [ + { + "id": "CORE-0129-01", + "severity": "MUST", + "category": "adr-conformance", + "title": "Conform to ADR-0129: The Tenant Master Is UMS, and the Suite Projects a Versioned Snapshot", + "description": "Implementations MUST conform to the decision recorded in ADR-0129 (core track). ADR decision: UMS is the master of Tenant and publishes its state; the rest of the suite projects. Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent carries TenantId , Code , Name , Status , ParentTenantId , IsManagementOwner , Version , ChangeType , OccurredAtUtc and SpecVersion . This ADR contains enforceable language (must, naming); conformance is expected to be verifiable via static analysis, linting, CI gates, or structural checks.", + "statement": "ADR decision: UMS is the master of Tenant and publishes its state; the rest of the suite projects. Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent carries TenantId , Code , Name , Status , ParentTenantId , IsManagementOwner , Version , ChangeType , OccurredAtUtc and SpecVersion .", + "rationale": "Derived from ADR-0129 \"Decision\" section. Enforcement signals detected: must, naming.", + "validationQuery": "Verify codebase/CI compliance with ADR-0129 via static analysis, lint rules, or pipeline gates covering: must, naming. Concrete checks to be wired into the harness.", + "blocking": false, + "enforcement": "executable" + } + ], + "references": [ + "reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md" + ], + "effectiveDate": "2026-08-22" +} diff --git a/src/sdk/cli/src/commands/architecture/scaffold.command.ts b/src/sdk/cli/src/commands/architecture/scaffold.command.ts index ed0b24688..56abbc36c 100644 --- a/src/sdk/cli/src/commands/architecture/scaffold.command.ts +++ b/src/sdk/cli/src/commands/architecture/scaffold.command.ts @@ -51,7 +51,7 @@ export class ScaffoldCommand extends BaseEvolithCommand { schemaVersion: OUTPUT_ENVELOPE_SCHEMA_VERSION, }; - // GT-455: .NET target. The suite (UMS/Tracker/MMS) is .NET; scaffold a + // GT-455: .NET target. The suite (UMS and the Tracker) is .NET; scaffold a // clean/hexagonal ASP.NET Core solution instead of the Node/Nx workspace. const runtime = ((options?.runtime as string) || 'nodejs').toLowerCase(); if (runtime === 'dotnet' || runtime === 'csharp' || runtime === '.net') { From 3823f1fa85e9a382bd9cf99fbd0e65d63767c657 Mon Sep 17 00:00:00 2001 From: aarroyo Date: Sat, 22 Aug 2026 19:46:46 -0500 Subject: [PATCH 2/4] docs(suite): the two design documents that still drew MMS as the tenant master MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both describe CURRENT intended behaviour rather than a point-in-time decision, so unlike ADRs 0106-0110 they are corrected in place: - the governed-composition target design carried MMS as a node in three diagrams, as a row in the tenant-responsibility table, and as the first step of the tenant sequence - the Tracker technical interfaces carried the same, plus a REST intake (`PUT /tenants/projections/...`) authorized for "MMS integration credentials". T-059 replaced that with a bus consumer, so the PUT is gone and the section says how intake actually arrives: Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent on tracker.tenant-snapshot, upserted by version The suite deployment strategy is deliberately NOT in this commit. Its §5 derives a messaging topology over two projection queues, with exchange bindings and the DLX retirement resting on it; that is a redrawing, not a rename, and it gets its own change. Co-Authored-By: Claude Opus 5 --- .../sdlc-tracker-technical-interfaces.es.md | 27 ++++++------ .../sdlc-tracker-technical-interfaces.md | 27 ++++++------ ...h-governed-composition-target-design.es.md | 42 ++++++++----------- ...lith-governed-composition-target-design.md | 42 ++++++++----------- 4 files changed, 58 insertions(+), 80 deletions(-) diff --git a/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.es.md b/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.es.md index f34d6a5a9..d8128570f 100644 --- a/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.es.md +++ b/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.es.md @@ -18,7 +18,7 @@ Este documento define las interfaces técnicas mediante las cuales Evolith Track El modelo de responsabilidades de tenant es: -> **MMS gobierna la identidad maestra del Tenant. UMS gobierna la identidad y autorización de usuarios dentro del Tenant. Tracker gobierna la operación SDLC del Tenant proyectado.** +> **UMS gobierna la identidad maestra del Tenant, y la identidad y autorización de usuarios dentro de él. Tracker gobierna la operación SDLC del Tenant proyectado.** Tracker no es una extensión del CLI. Es el sistema canónico de gobernanza en runtime. @@ -27,7 +27,7 @@ Tracker no es una extensión del CLI. Es el sistema canónico de gobernanza en r ## 2. Invariantes Arquitectónicos 1. Tracker posee procesos, fases, gates, decisiones, aprobaciones, excepciones y auditoría. -2. Tracker almacena una proyección de Tenant, no el registro maestro del Tenant. La proyección debe referenciar la clave global del Tenant en MMS. +2. Tracker almacena una proyección de Tenant, no el registro maestro del Tenant. La proyección debe referenciar la clave global del Tenant que publica UMS ([ADR-0129](../../../reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.es.md)). 3. Tracker delega autenticación y autorización en UMS y consume un grafo de autorización por tenant antes de habilitar acciones gobernadas. 4. Evolith Core es read-only en runtime y suministra reglas, schemas, estándares y contratos versionados. 5. CLI, MCP, CI y evaluadores externos retornan resultados técnicos; nunca mutan el estado canónico. @@ -51,8 +51,7 @@ flowchart TB HUMAN["Humanos y Clientes Empresariales"]:::actor AGENT["Agentes Autónomos y LLMs"]:::actor - MMS["MMS\nDato Maestro de Tenant"]:::provider - UMS["UMS\nAuthN/AuthZ"]:::provider + UMS["UMS\nMaestro de Tenant · AuthN/AuthZ"]:::provider subgraph TRACKER["Evolith Tracker"] API["REST API de Gobernanza"]:::tracker @@ -77,7 +76,7 @@ flowchart TB HUMAN --> API AGENT --> MCPGW - MMS -->|TenantProjection| TENANT + UMS -->|TenantSnapshot| TENANT API -->|solicitud delegada de autorizacion| UMS UMS -->|grafo de autorización| API API --> ORCH @@ -107,7 +106,7 @@ flowchart TB ### 4.1 Tenant Projection -Tracker recibe proyecciones de Tenant desde MMS. La proyección es la frontera local de gobernanza y no debe convertirse en un segundo maestro de Tenant. +Tracker recibe el retrato de Tenant que publica UMS. La proyección es la frontera local de gobernanza y no debe convertirse en un segundo maestro de Tenant. ```typescript interface TenantProjection { @@ -279,9 +278,8 @@ sequenceDiagram participant T as API Tracker participant S as Servicio de Proyección de Tenant participant U as UMS - participant M as MMS - M-->>S: Publicar TenantProjection + U-->>S: Publicar TenantSnapshot C->>T: Abrir Tracker o solicitar acción gobernada T->>U: Validar token y solicitar grafo de autorización U-->>T: Permisos por Tenant @@ -340,17 +338,16 @@ sequenceDiagram ## 6. API REST del Tracker **Base URL:** `https://tracker.evolith.io/api/v1` -**Autorización:** Bearer token delegado a UMS y grafo de autorización por tenant. Cada comando también resuelve una `TenantProjection` activa del Tracker para la misma clave global del Tenant en MMS. +**Autorización:** Bearer token delegado a UMS y grafo de autorización por tenant. Cada comando también resuelve una `TenantProjection` activa del Tracker para la misma clave global del Tenant que publicó UMS. ### 6.1 Ingesta de Proyección de Tenant ```text -PUT /tenants/projections/:globalTenantKey GET /tenants/projections/:globalTenantKey POST /tenants/projections/:globalTenantKey/verify ``` -La ingesta de proyecciones es system-to-system. Se autoriza solo para credenciales de integración de MMS y no crea usuarios, membresías ni permisos en Tracker. +La ingesta de proyecciones es system-to-system y no crea usuarios, membresías ni permisos en Tracker. Desde T-059 llega por el bus y no por HTTP: el Tracker consume `Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent` en `tracker.tenant-snapshot` y hace upsert por versión. Los endpoints de lectura de arriba siguen siendo la forma local de inspeccionar lo aplicado. ### 6.2 Productos y Procesos @@ -498,7 +495,7 @@ Todo puerto admite múltiples plugins y defaults configurables por tenant. ```mermaid erDiagram - TENANT_MASTER ||--o{ TENANT_PROJECTION : proyecta + TENANT_MASTER_EN_UMS ||--o{ TENANT_PROJECTION : proyecta TENANT_PROJECTION ||--o{ PRODUCT : posee PRODUCT ||--o{ SDLC_PROCESS : ejecuta SDLC_PROCESS ||--o{ PHASE_EXECUTION : contiene @@ -518,7 +515,7 @@ erDiagram | Agregado | Responsabilidad | |---|---| -| **Tenant Projection** | Frontera local del Tracker que referencia la identidad maestra del Tenant en MMS | +| **Tenant Projection** | Frontera local del Tracker que referencia la identidad maestra del Tenant en UMS | | **SDLC Process** | Fase actual y ciclo de vida | | **Phase Execution** | Entrada, actividad, finalización e historial | | **Evidence Graph** | Identidad, linaje, relaciones e integridad | @@ -558,7 +555,7 @@ Los agentes reciben contrato de actividad, contexto aprobado, herramientas permi | Evidencia embebida en gate | Gate Decision referencia snapshot del Evidence Graph | | ACL solo para Jira-like | Provider ports y plugins para toda capacidad externa | | Default fijo | Default configurable y reemplazable por scope | -| Tracker crea Tenant directamente | MMS crea el Tenant maestro; Tracker consume `TenantProjection` | +| Tracker crea Tenant directamente | UMS crea el Tenant maestro; Tracker consume el retrato publicado | ADR 0073 continúa válido para el envelope unificado, pero requiere una decisión complementaria sobre semántica de evaluación versus decisión. @@ -572,7 +569,7 @@ ADR 0073 continúa válido para el envelope unificado, pero requiere una decisi - [ ] Taxonomía de provider ports aprobada. - [ ] Modelo de plugins y defaults aprobado. - [ ] Contratos REST y MCP revisados. -- [ ] Contrato de proyección de Tenant desde MMS revisado. +- [ ] Contrato del retrato de Tenant de UMS revisado. - [ ] Flujo UMS revisado. - [ ] Aislamiento y clasificación de datos revisados. - [ ] ADRs requeridos identificados. diff --git a/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.md b/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.md index 7e2ac9419..aea4cffa2 100644 --- a/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.md +++ b/product/products/evolith-tracker/sdlc-tracker-technical-interfaces.md @@ -20,7 +20,7 @@ The responsibility model is: The tenant responsibility model is: -> **MMS governs the Tenant master identity. UMS governs user identity and authorization inside the Tenant. Tracker governs SDLC operation for the projected Tenant.** +> **UMS governs the Tenant master identity, and the user identity and authorization inside it. Tracker governs SDLC operation for the projected Tenant.** Tracker is not an extension of the CLI. It is the canonical runtime governance system. @@ -29,7 +29,7 @@ Tracker is not an extension of the CLI. It is the canonical runtime governance s ## 2. Architectural Invariants 1. Tracker owns process, phase, gate, decision, approval, exception, and audit state. -2. Tracker stores a Tenant projection, not the Tenant master record. The projection must reference the MMS global Tenant key. +2. Tracker stores a Tenant projection, not the Tenant master record. The projection must reference the global Tenant key UMS publishes ([ADR-0129](../../../reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md)). 3. Tracker delegates authentication and authorization to UMS and consumes a tenant-scoped authorization graph before enabling governed actions. 4. Evolith Core is read-only at runtime and supplies versioned rules, schemas, standards, and contracts. 5. CLI, MCP, CI, and external evaluators return technical results; they never mutate canonical phase state. @@ -52,8 +52,7 @@ flowchart TB HUMAN["Humans and Enterprise Clients"]:::actor AGENT["Autonomous Agents and LLMs"]:::actor - MMS["MMS\nTenant Master Data"]:::provider - UMS["UMS\nAuthN/AuthZ"]:::provider + UMS["UMS\nTenant Master · AuthN/AuthZ"]:::provider subgraph TRACKER["Evolith Tracker"] API["Governance REST API"]:::tracker @@ -78,7 +77,7 @@ flowchart TB HUMAN --> API AGENT --> MCPGW - MMS -->|TenantProjection| TENANT + UMS -->|TenantSnapshot| TENANT API -->|delegated auth request| UMS UMS -->|authorization graph| API API --> ORCH @@ -108,7 +107,7 @@ flowchart TB ### 4.1 Tenant Projection -Tracker receives Tenant projections from MMS. The projection is the local governance boundary and must not become a second Tenant master. +Tracker receives the Tenant snapshot UMS publishes. The projection is the local governance boundary and must not become a second Tenant master. ```typescript interface TenantProjection { @@ -280,9 +279,8 @@ sequenceDiagram participant T as Tracker API participant S as Tenant Projection Service participant U as UMS - participant M as MMS - M-->>S: Publish TenantProjection + U-->>S: Publish TenantSnapshot C->>T: Open Tracker or request governed action T->>U: Validate token and request authorization graph U-->>T: Tenant-scoped permissions @@ -341,17 +339,16 @@ sequenceDiagram ## 6. Tracker REST API **Base URL:** `https://tracker.evolith.io/api/v1` -**Authorization:** UMS-delegated bearer token and tenant-scoped authorization graph. Every command also resolves an active Tracker `TenantProjection` for the same MMS global Tenant key. +**Authorization:** UMS-delegated bearer token and tenant-scoped authorization graph. Every command also resolves an active Tracker `TenantProjection` for the same global Tenant key UMS published. ### 6.1 Tenant Projection Intake ```text -PUT /tenants/projections/:globalTenantKey GET /tenants/projections/:globalTenantKey POST /tenants/projections/:globalTenantKey/verify ``` -Projection intake is system-to-system. It is authorized for MMS integration credentials only and does not create users, memberships, or permissions in Tracker. +Projection intake is system-to-system and does not create users, memberships, or permissions in Tracker. Since T-059 it arrives over the bus rather than over HTTP: the Tracker consumes `Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent` on `tracker.tenant-snapshot` and upserts by version. The read endpoints above remain the local way to inspect what was applied. ### 6.2 Products and Processes @@ -498,7 +495,7 @@ interface ProviderPort { ```mermaid erDiagram - TENANT_MASTER ||--o{ TENANT_PROJECTION : projects + TENANT_MASTER_IN_UMS ||--o{ TENANT_PROJECTION : projects TENANT_PROJECTION ||--o{ PRODUCT : owns PRODUCT ||--o{ SDLC_PROCESS : runs SDLC_PROCESS ||--o{ PHASE_EXECUTION : contains @@ -518,7 +515,7 @@ erDiagram | Aggregate | Primary Responsibility | |---|---| -| **Tenant Projection** | Local Tracker boundary referencing the MMS Tenant master identity | +| **Tenant Projection** | Local Tracker boundary referencing the UMS Tenant master identity | | **SDLC Process** | Current phase and lifecycle | | **Phase Execution** | Entry, activity, completion and transition history | | **Evidence Graph** | Evidence identity, lineage, relationships and integrity | @@ -569,7 +566,7 @@ Agents return outputs and execution evidence only. | CI receives gate verdict directly from evaluator | CI submits evidence; Tracker returns canonical decision status | | One evidence payload embedded in gate record | Evidence Graph snapshot referenced by Gate Decision | | ACL only for Jira-style systems | Provider ports and ACLs across all external capabilities | -| Tracker creates Tenant directly | MMS creates Tenant master; Tracker consumes `TenantProjection` | +| Tracker creates Tenant directly | UMS creates the Tenant master; Tracker consumes the published snapshot | Existing ADR 0073 remains valid for the unified output envelope but requires a companion decision clarifying evaluation-versus-decision semantics before implementation. @@ -582,7 +579,7 @@ Existing ADR 0073 remains valid for the unified output envelope but requires a c - [ ] Evidence Graph aggregate boundaries approved. - [ ] Provider-port taxonomy approved. - [ ] REST and MCP contracts reviewed. -- [ ] MMS Tenant projection contract reviewed. +- [ ] UMS Tenant snapshot contract reviewed. - [ ] UMS authorization flow reviewed. - [ ] Tenant isolation and data-classification rules reviewed. - [ ] Required ADRs identified. diff --git a/product/suite/architecture/evolith-governed-composition-target-design.es.md b/product/suite/architecture/evolith-governed-composition-target-design.es.md index e4919cde9..721517c4a 100644 --- a/product/suite/architecture/evolith-governed-composition-target-design.es.md +++ b/product/suite/architecture/evolith-governed-composition-target-design.es.md @@ -20,7 +20,7 @@ El modelo central de responsabilidades es: El modelo de responsabilidades de tenant a nivel suite es: -> **MMS gobierna la identidad maestra del Tenant. UMS gobierna la identidad y autorización de usuarios dentro del Tenant. Evolith Tracker gobierna la operación SDLC del Tenant.** +> **UMS gobierna la identidad maestra del Tenant, y la identidad y autorización de usuarios dentro de él. Evolith Tracker gobierna la operación SDLC del Tenant.** El diseño reemplaza la interpretación anterior en la que CLI, CI o agentes autónomos podían parecer propietarios del veredicto final de un Phase Gate. @@ -29,10 +29,10 @@ El diseño reemplaza la interpretación anterior en la que CLI, CI o agentes aut ## 2. Invariantes del Sistema 1. **Evolith Core es constitucional y de solo lectura en runtime.** Define rulesets, schemas, estándares, taxonomías, gates y contratos de proveedores. -2. **MMS posee el dato maestro canónico del Tenant.** El Tenant se registra primero en MMS y recibe la clave global usada por todos los productos de la suite. +2. **UMS posee el dato maestro canónico del Tenant.** El Tenant se registra en UMS y recibe la clave global usada por todos los productos de la suite ([ADR-0129](../../../reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.es.md)). 3. **UMS posee la identidad y autorización de usuarios dentro del Tenant.** Autentica usuarios, mantiene membresías, perfiles, roles y permisos, y devuelve grafos de autorización limitados por la clave global del Tenant. 4. **Evolith Tracker posee el estado canónico de gobernanza en runtime.** Controla proyecciones SDLC del tenant, procesos, fases, decisiones, aprobaciones, excepciones y auditoría. -5. **Los registros de Tenant en UMS y Tracker son proyecciones, no identidades maestras.** Deben referenciar la clave global de MMS y pueden estar inactivos, desactualizados o rechazados de forma independiente por cada dominio consumidor. +5. **El registro de Tenant en el Tracker es una proyección, no una identidad maestra.** Debe referenciar la clave global que publica UMS y puede estar inactivo, desactualizado o rechazado de forma independiente por cada dominio consumidor. 6. **CLI, MCP, CI y agentes son evaluadores sin estado o productores de evidencia.** Nunca modifican directamente el estado canónico de una fase. 7. **Los sistemas externos conservan autoridad sobre sus hechos operativos.** SCM posee commits, CI posee ejecuciones, observabilidad posee traces y los sistemas de trabajo poseen sus work items nativos. 8. **Tracker es autoritativo para interpretar la gobernanza.** Decide si la evidencia recolectada satisface las políticas del Core y del tenant. @@ -59,8 +59,7 @@ flowchart TB AGENTS["Agentes Autónomos y LLMs"]:::actor CORE["Evolith Core\nConstitución · Reglas · Schemas · Contratos"]:::core - MMS["MMS\nDato Maestro de Tenant"]:::product - UMS["UMS\nIdentidad · Membresía · Autorización"]:::product + UMS["UMS\nMaestro de Tenant · Identidad · Membresía · Autorización"]:::product TRACKER["Evolith Tracker\nPlano de Control de Gobernanza"]:::tracker WORK["Sistemas de Trabajo\nJira · Azure DevOps · GitHub Issues · Alternativas"]:::provider @@ -69,12 +68,11 @@ flowchart TB BI["Analítica y Visualización\nSuperset · Grafana · Alternativas"]:::provider TEST["Proveedores de Testing, Seguridad y Despliegue"]:::provider - PRODUCTS["Productos Satélite\nMMS · UMS · Evolith Tracker · Productos Futuros"]:::product + PRODUCTS["Productos Satélite\nUMS · Evolith Tracker · Productos Futuros"]:::product BOARD -->|aprueba evolución constitucional| CORE CORE -->|reglas, schemas y contratos| TRACKER - MMS -->|TenantProjection con clave global| UMS - MMS -->|TenantProjection con clave global| TRACKER + UMS -->|TenantSnapshot con clave global| TRACKER TRACKER -->|solicitud delegada de autorizacion| UMS UMS -->|grafo de autorización por Tenant| TRACKER HUMANS -->|solicitudes, aprobaciones y excepciones| TRACKER @@ -117,8 +115,7 @@ flowchart TB CORE["Evolith Core\nRulesets · Schemas · Taxonomía · ADRs"]:::core CLI["Evolith SDK / CLI / MCP\nRuntime de Evaluación sin Estado"]:::core - MMS["MMS\nDato Maestro de Tenant"]:::adapter - UMS["UMS\nIdentidad y Autorización"]:::adapter + UMS["UMS\nMaestro de Tenant · Identidad y Autorización"]:::adapter subgraph EXTERNAL["Proveedores Externos"] WP["Adaptador de Gestión de Trabajo"]:::adapter @@ -133,7 +130,7 @@ flowchart TB UX --> API API --> TENANT API --> ORCH - MMS -->|TenantProjection| TENANT + UMS -->|TenantSnapshot| TENANT API -->|autenticación y autorización delegada| UMS UMS -->|grafo de autorización| API TENANT --> ORCH @@ -170,7 +167,7 @@ flowchart TB |---|---|---| | **Experiencia Web Unificada** | Navegación, vistas de evidencia, acciones gobernadas, aprobaciones y deep links | La verdad operativa del proveedor | | **API de Gobernanza** | Contrato externo estable y frontera de autorización | Reglas de negocio duplicadas desde Core | -| **Servicio de Proyección de Tenant** | Vista local activa/inactiva del Tracker sobre la identidad del Tenant en MMS y frescura de proyección | Identidad maestra del Tenant, identidad legal, membresía de usuarios o grafo de autorización | +| **Servicio de Proyección de Tenant** | Vista local activa/inactiva del Tracker sobre la identidad del Tenant en UMS y frescura de proyección | Identidad maestra del Tenant, identidad legal, membresía de usuarios o grafo de autorización | | **Orquestador de Procesos y Fases** | Ciclo de vida y solicitudes de transición | La implementación técnica de evaluación | | **Motor de Decisiones de Gate** | Decisión canónica, combinación de políticas, aprobaciones y excepciones | Ejecución de herramientas fuente | | **Servicio Evidence Graph** | Identidad, linaje, relaciones, integridad y consulta de evidencia | Almacenes crudos de proveedores | @@ -183,19 +180,17 @@ flowchart TB ## 5. Dato Maestro de Tenant y Proyecciones por Contexto -El Tenant se crea una sola vez en MMS como dato maestro. UMS y Evolith Tracker consumen proyecciones del Tenant para que cada bounded context conserve autonomía y, al mismo tiempo, comparta la misma clave global del Tenant. +El Tenant se crea una sola vez en UMS como dato maestro. Evolith Tracker consume el retrato versionado que UMS publica, así cada bounded context conserva su autonomía y comparte la misma clave global del Tenant. ```mermaid sequenceDiagram autonumber - participant M as MMS participant U as UMS participant T as Evolith Tracker participant A as Usuario - M->>M: Registrar identidad maestra del Tenant - M-->>U: Publicar TenantProjection - M-->>T: Publicar TenantProjection + U->>U: Registrar identidad maestra del Tenant + U-->>T: Publicar TenantSnapshot (versionado) U->>U: Habilitar membresías, roles, perfiles y permisos T->>T: Habilitar frontera de gobernanza SDLC A->>T: Abrir workspace SDLC gobernado @@ -209,13 +204,12 @@ sequenceDiagram | Sistema | Responsabilidad sobre Tenant | Usa la Clave Global del Tenant Para | No Debe Poseer | |---|---|---|---| -| **MMS** | Identidad maestra, ciclo de vida, metadata canónica y publicación de proyecciones | Correlación cross-suite | Autorización de usuarios o estado de procesos SDLC | -| **UMS** | Identidad de usuarios, membresías, perfiles, roles y permisos dentro del Tenant | Scoping del grafo de autorización | Identidad maestra del Tenant o decisiones de gate SDLC | +| **UMS** | Identidad maestra del Tenant y su ciclo de vida, publicación del retrato, y la identidad de usuarios, membresías, perfiles, roles y permisos dentro del Tenant | Correlación cross-suite y scoping del grafo de autorización | Estado de procesos SDLC o decisiones de gate | | **Evolith Tracker** | Proceso SDLC, gates, evidencias, aprobadores, excepciones, auditoría y configuración operacional del tenant | Frontera de gobernanza y partición de evidencias | Identidad maestra del Tenant o autoridad de credenciales de usuario | Tracker debe rechazar acciones gobernadas cuando el grafo de autorización de UMS referencia un Tenant ausente, inactivo, vencido según política o inconsistente con la proyección local del Tenant en Tracker. -Los datos de proyección del Tenant son locales al dominio por diseño. Una proyección puede incluir la clave global del Tenant, nombre visible, estado de ciclo de vida, perfil de clasificación de datos, referencia de perfil de gobernanza, versión de proyección y metadata de sincronización. No debe convertirse en una copia de todos los campos de MMS o UMS. +Los datos de proyección del Tenant son locales al dominio por diseño. Una proyección puede incluir la clave global del Tenant, nombre visible, estado de ciclo de vida, perfil de clasificación de datos, referencia de perfil de gobernanza, versión de proyección y metadata de sincronización. No debe convertirse en una copia de todos los campos de UMS. --- @@ -431,8 +425,7 @@ La experiencia muestra primero el estado canónico Evolith y luego los detalles ```text Un tenant - -> una identidad maestra de Tenant en MMS - -> una proyección activa del Tenant en UMS + -> una identidad maestra de Tenant en UMS -> una proyección activa del Tenant en Tracker -> un producto -> un proceso SDLC de cinco fases @@ -448,8 +441,7 @@ Un tenant El diseño se acepta únicamente si: - Tracker sigue siendo autoritativo; -- MMS sigue siendo autoritativo para la identidad maestra del Tenant; -- UMS sigue siendo autoritativo para identidad y autorización de usuarios; +- UMS sigue siendo autoritativo para la identidad maestra del Tenant y para la identidad y autorización de usuarios; - Tracker valida una proyección local activa del Tenant antes de cualquier acción gobernada; - toda evidencia conserva linaje del proveedor; - los adaptadores son reemplazables; @@ -482,7 +474,7 @@ Rulesets, schemas y código quedan fuera de este primer conjunto de cambios de d - límites de agregados del Evidence Graph; - taxonomía inicial de provider ports; - niveles de certificación de adaptadores; -- contrato de proyección de Tenant desde MMS hacia UMS y Tracker; +- contrato del retrato de tenant de UMS hacia Tracker (`Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent`); - contrato de grafo de autorización de UMS consumido por Tracker; - corte vertical mínimo; - terminología de `compliant`, `approved` y `passed`; diff --git a/product/suite/architecture/evolith-governed-composition-target-design.md b/product/suite/architecture/evolith-governed-composition-target-design.md index c3cde217a..96943c633 100644 --- a/product/suite/architecture/evolith-governed-composition-target-design.md +++ b/product/suite/architecture/evolith-governed-composition-target-design.md @@ -20,7 +20,7 @@ The central responsibility model is: The suite-level tenant responsibility model is: -> **MMS governs the Tenant master identity. UMS governs user identity and authorization inside the Tenant. Evolith Tracker governs the Tenant's SDLC operation.** +> **UMS governs the Tenant master identity, and the user identity and authorization inside it. Evolith Tracker governs the Tenant's SDLC operation.** The design replaces the previous interpretation in which CLI, CI, or autonomous agents could appear to own the final Phase Gate verdict. @@ -29,10 +29,10 @@ The design replaces the previous interpretation in which CLI, CI, or autonomous ## 2. System Invariants 1. **Evolith Core is constitutional and read-only at runtime.** It defines rulesets, schemas, standards, taxonomies, gate definitions, and provider contracts. -2. **MMS owns canonical Tenant master data.** A Tenant is registered first in MMS and receives the global Tenant key used by all suite products. +2. **UMS owns canonical Tenant master data.** A Tenant is registered in UMS and receives the global Tenant key used by all suite products ([ADR-0129](../../../reference/core/architecture/adrs/core/0129-ums-is-the-tenant-master.md)). 3. **UMS owns user identity and authorization inside the Tenant.** It authenticates users, maintains memberships, profiles, roles, and permissions, and returns authorization graphs scoped to the global Tenant key. 4. **Evolith Tracker owns canonical runtime governance state.** It owns tenant SDLC projections, processes, phase state, decisions, approvals, exceptions, and audit history. -5. **Tenant records in UMS and Tracker are projections, not master identities.** They must reference the MMS global Tenant key and may be inactive, stale, or rejected independently by each consuming domain. +5. **Tenant records in the Tracker are a projection, not a master identity.** They must reference the global Tenant key UMS publishes and may be inactive, stale, or rejected independently by each consuming domain. 6. **CLI, MCP, CI, and agents are stateless evaluators or evidence producers.** They never mutate canonical phase state directly. 7. **External systems remain authoritative for their operational facts.** SCM owns commits, CI owns runs, observability owns traces, and work systems own their native work items. 8. **Tracker is authoritative for governance interpretation.** It decides whether collected evidence satisfies Core and tenant policy. @@ -59,8 +59,7 @@ flowchart TB AGENTS["Autonomous Agents and LLMs"]:::actor CORE["Evolith Core\nConstitution · Rules · Schemas · Contracts"]:::core - MMS["MMS\nTenant Master Data"]:::product - UMS["UMS\nIdentity · Membership · Authorization"]:::product + UMS["UMS\nTenant Master · Identity · Membership · Authorization"]:::product TRACKER["Evolith Tracker\nGovernance Control Plane"]:::tracker WORK["Work Systems\nJira · Azure DevOps · GitHub Issues · Alternatives"]:::provider @@ -69,12 +68,11 @@ flowchart TB BI["Analytics and Visualization\nSuperset · Grafana · Alternatives"]:::provider TEST["Testing, Security and Deployment Providers"]:::provider - PRODUCTS["Satellite Products\nMMS · UMS · Evolith Tracker · Future Products"]:::product + PRODUCTS["Satellite Products\nUMS · Evolith Tracker · Future Products"]:::product BOARD -->|approves constitutional evolution| CORE CORE -->|rules, schemas and contracts| TRACKER - MMS -->|TenantProjection with global Tenant key| UMS - MMS -->|TenantProjection with global Tenant key| TRACKER + UMS -->|TenantSnapshot with global Tenant key| TRACKER TRACKER -->|delegated auth request| UMS UMS -->|Tenant-scoped authorization graph| TRACKER HUMANS -->|requests, approvals and exceptions| TRACKER @@ -118,8 +116,7 @@ flowchart TB CORE["Evolith Core\nRulesets · Schemas · Taxonomy · ADRs"]:::core CLI["Evolith SDK / CLI / MCP\nStateless Evaluation Runtime"]:::core - MMS["MMS\nTenant Master Data"]:::adapter - UMS["UMS\nIdentity and Authorization"]:::adapter + UMS["UMS\nTenant Master · Identity and Authorization"]:::adapter subgraph EXTERNAL["External Providers"] WP["Work Management Adapter"]:::adapter @@ -134,7 +131,7 @@ flowchart TB UX --> API API --> TENANT API --> ORCH - MMS -->|TenantProjection| TENANT + UMS -->|TenantSnapshot| TENANT API -->|delegated authentication and authorization| UMS UMS -->|authorization graph| API TENANT --> ORCH @@ -171,7 +168,7 @@ flowchart TB |---|---|---| | **Unified Web Experience** | Navigation, evidence views, governed actions, approvals and deep links | Provider operational truth | | **Governance API** | Stable external contract and authorization boundary | Business rules duplicated from Core | -| **Tenant Projection Service** | Local active/inactive Tracker view of MMS Tenant identity and projection freshness | Tenant master identity, legal identity, user membership, or authorization graph | +| **Tenant Projection Service** | Local active/inactive Tracker view of the UMS Tenant identity and projection freshness | Tenant master identity, legal identity, user membership, or authorization graph | | **Process and Phase Orchestrator** | Process lifecycle and transition requests | Final technical evaluation implementation | | **Gate Decision Engine** | Canonical decision, policy combination, approval and exception handling | Source-tool execution | | **Evidence Graph Service** | Evidence identity, lineage, relationships, integrity and retrieval | Raw provider data stores | @@ -184,19 +181,17 @@ flowchart TB ## 5. Tenant Master Data and Context Projections -The Tenant is created once in MMS as master data. UMS and Evolith Tracker consume Tenant projections so each bounded context can stay autonomous while still sharing the same global Tenant key. +The Tenant is created once in UMS as master data. Evolith Tracker consumes the versioned snapshot UMS publishes, so each bounded context stays autonomous while sharing the same global Tenant key. ```mermaid sequenceDiagram autonumber - participant M as MMS participant U as UMS participant T as Evolith Tracker participant A as User - M->>M: Register Tenant master identity - M-->>U: Publish TenantProjection - M-->>T: Publish TenantProjection + U->>U: Register Tenant master identity + U-->>T: Publish TenantSnapshot (versioned) U->>U: Enable memberships, roles, profiles and permissions T->>T: Enable SDLC governance boundary A->>T: Open governed SDLC workspace @@ -210,13 +205,12 @@ sequenceDiagram | System | Tenant Responsibility | Uses Global Tenant Key For | Must Not Own | |---|---|---|---| -| **MMS** | Tenant master identity, lifecycle, canonical metadata and projection publication | Cross-suite identity correlation | User authorization or SDLC process state | -| **UMS** | User identity, memberships, profiles, roles and permissions inside the Tenant | Authorization graph scoping | Tenant master identity or SDLC gate decisions | +| **UMS** | Tenant master identity and lifecycle, snapshot publication, and the user identity, memberships, profiles, roles and permissions inside the Tenant | Cross-suite identity correlation and authorization graph scoping | SDLC process state or gate decisions | | **Evolith Tracker** | SDLC process, gates, evidence, approvers, exceptions, audit and tenant operational configuration | Governance boundary and evidence partitioning | Tenant master identity or user credential authority | Tracker must reject governed actions when the UMS authorization graph references a Tenant that is missing, inactive, stale beyond policy, or mismatched against the local Tracker Tenant projection. -Tenant projection data is domain-local by design. A projection may carry the global Tenant key, display name, lifecycle status, data-classification profile, governance profile reference, projection version, and synchronization metadata. It must not become a copy of every MMS or UMS field. +Tenant projection data is domain-local by design. A projection may carry the global Tenant key, display name, lifecycle status, data-classification profile, governance profile reference, projection version, and synchronization metadata. It must not become a copy of every UMS field. --- @@ -434,8 +428,7 @@ The first implementation following this design will prove one vertical slice: ```text One tenant - -> one MMS Tenant master identity - -> one active UMS Tenant projection + -> one UMS Tenant master identity -> one active Tracker Tenant projection -> one product -> one five-phase SDLC process @@ -451,8 +444,7 @@ One tenant The design is accepted only if: - Tracker remains authoritative for every decision; -- MMS remains authoritative for Tenant master identity; -- UMS remains authoritative for user identity and authorization; +- UMS remains authoritative for Tenant master identity, user identity and authorization; - Tracker validates an active local Tenant projection before any governed action; - every evidence item preserves provider lineage; - adapters can be replaced without changing canonical entities; @@ -487,7 +479,7 @@ The Architecture Board must approve: - the Evidence Graph aggregate boundaries; - the initial provider-port taxonomy; - adapter certification levels; -- MMS-to-UMS and MMS-to-Tracker Tenant projection contract; +- the UMS-to-Tracker tenant snapshot contract (`Evolith.Contracts.Tenancy.TenantSnapshotIntegrationEvent`); - UMS authorization graph contract consumed by Tracker; - the minimum vertical slice; - terminology for `compliant`, `approved`, and `passed`; From 89c82bf51ca89a13d39020797e72dc53b67966af Mon Sep 17 00:00:00 2001 From: aarroyo Date: Sat, 22 Aug 2026 19:57:36 -0500 Subject: [PATCH 3/4] chore(derived): replay the artifact chain ADR-0129 moved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding one ADR adds one generated conformance ruleset, and four artifacts descend from that corpus. CI found them in the order the chain declares: - native-evaluability-snapshot.json (415 rules, documentation-only 140→141) - iso-5055-mapping.{json,csv} - maturity-reconciliation.json - the three pinned counts in rule-corpus-triage.spec.ts The spec's numbers are pinned deliberately, so each one carries a note saying what moved it. Superseding costs +1 rather than trading one for another: ADR-0106 keeps its placeholder, because a superseded decision keeps its record. Co-Authored-By: Claude Opus 5 --- .../maturity-reconciliation.json | 4 +- .../validators/rule-corpus-triage.spec.ts | 20 +++++++--- src/rulesets/standards/iso-5055-mapping.csv | 1 + src/rulesets/standards/iso-5055-mapping.json | 37 ++++++++++++++----- .../native-evaluability-snapshot.json | 11 +++--- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/reference/core/control-center/maturity-reports/maturity-reconciliation.json b/reference/core/control-center/maturity-reports/maturity-reconciliation.json index 7bb51e9ff..b53c8fd82 100644 --- a/reference/core/control-center/maturity-reports/maturity-reconciliation.json +++ b/reference/core/control-center/maturity-reports/maturity-reconciliation.json @@ -12,8 +12,8 @@ "evidence": { "closureRecords": 659, "cliPackage": "@beyondnet/evolith-cli@1.3.2", - "adrCount": 143, - "rulesetCount": 183, + "adrCount": 144, + "rulesetCount": 184, "schemaCount": 50 }, "readiness": [ diff --git a/src/packages/core-domain/src/application/validators/rule-corpus-triage.spec.ts b/src/packages/core-domain/src/application/validators/rule-corpus-triage.spec.ts index 0a1e59ebf..3a39820e2 100644 --- a/src/packages/core-domain/src/application/validators/rule-corpus-triage.spec.ts +++ b/src/packages/core-domain/src/application/validators/rule-corpus-triage.spec.ts @@ -115,7 +115,11 @@ const PINNED_CLASS_COUNTS: Readonly> = { // 138 -> 139 on 2026-08-18: ADR-0127's, by the same mechanism. Worth noting what the // count is measuring here — the ADR retires Knowledge-First Discovery, so the corpus // grows by one rule in order to record the removal of a concept that never had one. - 'documentation-only': 140, + // 140 -> 141 on 2026-08-22: ADR-0129's (the tenant master is UMS; ADR-0106's MMS was + // never built). Same mechanism again, and the same reading: superseding a decision + // costs one more rule nothing can run, because the superseding ADR owes a conformance + // placeholder of its own while the superseded one keeps the placeholder it already had. + 'documentation-only': 141, 'unimplemented-native': 52, 'needs-external-system': 20, 'needs-runtime': 17, @@ -251,9 +255,12 @@ describe('GT-595 · the published breakdown, with its denominator', () => { // 152 -> 153 on 2026-08-18: ADR-0127's, by the same mechanism — the ADR that retires // Knowledge-First Discovery. The corpus grows by one rule nothing can run in order to // record the removal of a concept nothing could run either. - expect(SUMMARY.nonExecutable).toBe(154); - expect(SUMMARY.executableTotal).toBe(SUMMARY.total - 154); - expect(SUMMARY.nonExecutableRuleIds).toHaveLength(154); + // 154 -> 155 on 2026-08-22: ADR-0129's. Superseding ADR-0106 does not retire 0106's + // placeholder — a superseded decision keeps its record — so the denominator grows by + // one rather than trading one for another. + expect(SUMMARY.nonExecutable).toBe(155); + expect(SUMMARY.executableTotal).toBe(SUMMARY.total - 155); + expect(SUMMARY.nonExecutableRuleIds).toHaveLength(155); }); it('names the blocking rules that can never produce a verdict', () => { @@ -314,8 +321,11 @@ describe('GT-595 · the handler slice that landed', () => { // surface). Same shape as the ADR-0125 bump above — one accepted ADR, one generated // conformance placeholder, claimed by the conformance handler. // 135 -> 136 on 2026-08-18: ADR-0127 (Knowledge-First Discovery is retired). Same shape. + // 137 -> 138 on 2026-08-22: ADR-0129 (the tenant master is UMS). Same shape once more, + // and the `every(claims)` below is again the assertion that matters: the new decision + // arrives CLAIMED by the conformance handler rather than in the unclaimed pile. const adrConformance = CORPUS.filter(r => r.category === 'adr-conformance'); - expect(adrConformance).toHaveLength(137); + expect(adrConformance).toHaveLength(138); expect(adrConformance.every(claims)).toBe(true); }); diff --git a/src/rulesets/standards/iso-5055-mapping.csv b/src/rulesets/standards/iso-5055-mapping.csv index aa53a05fc..df981544c 100644 --- a/src/rulesets/standards/iso-5055-mapping.csv +++ b/src/rulesets/standards/iso-5055-mapping.csv @@ -157,6 +157,7 @@ CORE-0125-01,adr/generated/adr-0125-a-single-artifact-registry-keyed-by-slug.rul CORE-0126-01,adr/generated/adr-0126-the-bilingual-mandate-narrows-to-an-entry-surface.rules.json,architecture-decision,,,none,no,documentation-only CORE-0127-01,adr/generated/adr-0127-knowledge-first-discovery-is-retired-and-with-it-the-kdd-con.rules.json,architecture-decision,,,none,no,documentation-only CORE-0128-01,adr/generated/adr-0128-llm-providers-are-a-catalog-the-core-publishes-and-a-choice-.rules.json,architecture-decision,,,none,no,documentation-only +CORE-0129-01,adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json,architecture-decision,,,none,no,documentation-only AI-0001-01,adr/generated/adr-ai-augmented-0001-harness-engineering-for-ai-augmented-development.rules.json,architecture-decision,,,none,no,documentation-only AI-0002-01,adr/generated/adr-ai-augmented-0002-mcp-integration-protocol-for-agent-tool-invocation.rules.json,architecture-decision,,,none,no,documentation-only AI-0003-01,adr/generated/adr-ai-augmented-0003-model-selection-governance-for-ai-augmented-workflows.rules.json,architecture-decision,,,none,no,documentation-only diff --git a/src/rulesets/standards/iso-5055-mapping.json b/src/rulesets/standards/iso-5055-mapping.json index 25f820efb..9649c5f8b 100644 --- a/src/rulesets/standards/iso-5055-mapping.json +++ b/src/rulesets/standards/iso-5055-mapping.json @@ -12,24 +12,24 @@ "weaknessCount": 138 }, "corpus": { - "rulesetFiles": 183, - "rules": 416, + "rulesetFiles": 184, + "rules": 417, "note": "Files that carry gate definitions or topology recommendations rather than conformance rules contribute no rows: architecture/topology-recommendation.rules.json and sdlc/phase-gates.rules.json." }, "summary": { - "rules": 416, + "rules": 417, "mappedToIso5055": 37, "mappedDirect": 8, "mappedPartial": 29, - "noInternationalEquivalent": 379, - "adoptedFraction": 0.0889, + "noInternationalEquivalent": 380, + "adoptedFraction": 0.0887, "analyserAdoptable": 46, "analyserAdoptablePartial": 23, - "analyserAdoptableFraction": 0.1106, - "analyserAdoptableFractionIncludingPartial": 0.1659, + "analyserAdoptableFraction": 0.1103, + "analyserAdoptableFractionIncludingPartial": 0.1655, "byClass": { "architecture-decision": { - "rules": 166, + "rules": 167, "mapped": 12, "adoptable": 6 }, @@ -146,7 +146,7 @@ "adoptablePartialRuleIds": [] }, "documentation-only": { - "rules": 140, + "rules": 141, "mappedToIso5055": 9, "analyserAdoptable": 6, "analyserAdoptablePartial": 7, @@ -3403,6 +3403,25 @@ "nativeEvaluability": "documentation-only", "note": "Conformance to a recorded architecture decision. ISO/IEC 5055 measures source structure, not whether a decision was honoured." }, + { + "ruleId": "CORE-0129-01", + "sourceFile": "adr/generated/adr-0129-the-tenant-master-is-ums-and-the-suite-projects-a-versioned-.rules.json", + "title": "Conform to ADR-0129: The Tenant Master Is UMS, and the Suite Projects a Versioned Snapshot", + "severity": "MUST", + "ruleClass": "architecture-decision", + "iso5055": { + "cwes": [], + "weaknesses": [], + "measures": [], + "strength": "none" + }, + "analyser": { + "adoptable": "no", + "examples": [] + }, + "nativeEvaluability": "documentation-only", + "note": "Conformance to a recorded architecture decision. ISO/IEC 5055 measures source structure, not whether a decision was honoured." + }, { "ruleId": "AI-0001-01", "sourceFile": "adr/generated/adr-ai-augmented-0001-harness-engineering-for-ai-augmented-development.rules.json", diff --git a/src/rulesets/standards/native-evaluability-snapshot.json b/src/rulesets/standards/native-evaluability-snapshot.json index 10defc9b1..e2842f10b 100644 --- a/src/rulesets/standards/native-evaluability-snapshot.json +++ b/src/rulesets/standards/native-evaluability-snapshot.json @@ -3,7 +3,7 @@ "title": "Native-engine evaluability class per rule (snapshot)", "description": "Per-rule evaluability class as computed by the Core native evaluator triage. This is a GENERATED CAPTURE, not the source of truth: the authority is src/packages/core-domain/src/application/validators/rule-evaluability.ts and the handler set registered in native-evaluator.ts. It is recorded here so the ISO/IEC 5055 mapping can be scoped to the real handler backlog without src/rulesets depending on a package it does not own. Do not hand-edit — regenerate.", "version": "1.1.0", - "capturedOn": "2026-08-22", + "capturedOn": "2026-08-23", "capturedFrom": [ "src/packages/core-domain/src/application/validators/rule-evaluability.ts (RULE_TRIAGE, classifyRule, ADR_CONFORMANCE_CATEGORY)", "src/packages/core-domain/src/application/validators/evaluators/native-evaluator.ts (registered handler set)", @@ -11,12 +11,12 @@ "src/packages/core-domain/test/rule-corpus-triage.ts (corpus loader, classification and this renderer)" ], "regenerateWith": "node src/rulesets/standards/capture-native-evaluability-snapshot.mjs", - "validation": "Rendered by test/rule-corpus-triage.ts from the live triage, written by capture-native-evaluability-snapshot.mjs and pinned byte-for-byte by rule-corpus-triage.spec.ts, so a divergence between this file and Core is a failing test rather than silent drift (corpus 414; native-handler 171, documentation-only 140, unimplemented-native 52, needs-external-system 20, needs-runtime 17, underspecified 14).", - "corpusSize": 414, - "distinctRuleIds": 414, + "validation": "Rendered by test/rule-corpus-triage.ts from the live triage, written by capture-native-evaluability-snapshot.mjs and pinned byte-for-byte by rule-corpus-triage.spec.ts, so a divergence between this file and Core is a failing test rather than silent drift (corpus 415; native-handler 171, documentation-only 141, unimplemented-native 52, needs-external-system 20, needs-runtime 17, underspecified 14).", + "corpusSize": 415, + "distinctRuleIds": 415, "counts": { "native-handler": 171, - "documentation-only": 140, + "documentation-only": 141, "unimplemented-native": 52, "needs-external-system": 20, "needs-runtime": 17, @@ -181,6 +181,7 @@ "CORE-0126-01": "documentation-only", "CORE-0127-01": "documentation-only", "CORE-0128-01": "documentation-only", + "CORE-0129-01": "documentation-only", "AI-0001-01": "documentation-only", "AI-0002-01": "documentation-only", "AI-0003-01": "documentation-only", From 1530d57a8f406099da9b481804bacc9acc4069a6 Mon Sep 17 00:00:00 2001 From: aarroyo Date: Sat, 22 Aug 2026 20:05:30 -0500 Subject: [PATCH 4/4] chore(derived): the inventory summaries count ADRs too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sixth artifact downstream of one new decision record: ADRs 143 -> 144 in both language copies. Found by CI rather than by the local chain replay, because 07-generate-inventories.mjs is not one of the nine links guard 46 walks — worth knowing the next time an ADR lands. Co-Authored-By: Claude Opus 5 --- .../control-center/maturity-reports/inventory-summary.es.md | 6 +++--- .../control-center/maturity-reports/inventory-summary.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/reference/core/control-center/maturity-reports/inventory-summary.es.md b/reference/core/control-center/maturity-reports/inventory-summary.es.md index 87cab9b6a..2d64853c9 100644 --- a/reference/core/control-center/maturity-reports/inventory-summary.es.md +++ b/reference/core/control-center/maturity-reports/inventory-summary.es.md @@ -7,8 +7,8 @@ Este es el conteo automatizado del inventario de la arquitectura de referencia c | Tipo de Artefacto | Conteo | Ubicación | |---|:---:|---| -| **Architecture Decision Records (ADR)** | 143 | `reference/core/architecture/adrs/` | -| **Rulesets Legibles por Máquina** | 183 | `src/rulesets/` (en 20 categorías) | +| **Architecture Decision Records (ADR)** | 144 | `reference/core/architecture/adrs/` | +| **Rulesets Legibles por Máquina** | 184 | `src/rulesets/` (en 20 categorías) | | **Schemas de Phase-Gates** | 50 | `src/rulesets/schema/` | -*Última Actualización: 2026-08-22* +*Última Actualización: 2026-08-23* diff --git a/reference/core/control-center/maturity-reports/inventory-summary.md b/reference/core/control-center/maturity-reports/inventory-summary.md index 17d50ea1b..3bf7e20b9 100644 --- a/reference/core/control-center/maturity-reports/inventory-summary.md +++ b/reference/core/control-center/maturity-reports/inventory-summary.md @@ -7,8 +7,8 @@ This is the automated inventory tally of the core reference architecture and gov | Artifact Type | Count | Location | |---|:---:|---| -| **Architecture Decision Records (ADR)** | 143 | `reference/core/architecture/adrs/` | -| **Machine-Readable Rulesets** | 183 | `src/rulesets/` (across 20 categories) | +| **Architecture Decision Records (ADR)** | 144 | `reference/core/architecture/adrs/` | +| **Machine-Readable Rulesets** | 184 | `src/rulesets/` (across 20 categories) | | **Phase-Gate Schemas** | 50 | `src/rulesets/schema/` | -*Last Updated: 2026-08-22* +*Last Updated: 2026-08-23*