# LRU_ARQUITECTURA

> Fichero: `LRU_ARQUITECTURA.md` · documento vivo sin sufijo de sesión
> Ubicación canónica: `public/docs-app/v1/doc/LRU_ARQUITECTURA.md`
> Origen: sesión s58 · 2026-04-18 · actualizado s61 · sincronizado s63 · sincronizado s64 · sincronizado s65 · sincronizado s67 · 2026-04-20 · **sincronizado s77** · 2026-04-22 · sincronizado s91 (anotación P10 en §8) · sincronizado s99 (anotación P11 en §8) · **sincronizado s123** · 2026-05-05 (anotación P12 en §8 al integrar T-S117-F1 al núcleo) · Manuel & Claude Opus 4.7
> Precedente: sobreescribe la versión sincronizada en s67 con los cambios de s77 — **primera sesión de código de T-S67-A1** materializó el Bloque 1 ítem 1 del triaje s75 (`apply-profile` como 5º kind de `ScenarioEvent` con unión discriminada estricta + `ProfileApplication` de 4 shapes + `ScenarioDef.durSeconds?` + validación monotónica) aplicando literalmente el destilado `lru_scenario_event_extension_s64.md` a `src/realm/{types,validators,index}.ts`. 3 tensiones cerradas completas (T-S63-8, T-AU72-1, T-AU72-4), 1 parcial (T-AU72-2), 1 nueva de prioridad baja (T-S77-C1). Test de smoke 24/24 verde. §7.4 y §10 actualizadas.
> Fuentes fusionadas: `lru_architecture_consolidated_s53.html`, `LRU_PROJECT_REFERENCE_v5_1_s54.md`, estado Realm v2 desplegado en s56
> Propósito: describir cómo está construido el sistema ahora mismo — ficheros, tamaños, responsabilidades, dependencias
> Documentos hermanos del núcleo: `LRU_MISION`, `LRU_FUNDAMENTOS`, `LRU_MAPA`, `LRU_METODO`

---

## Qué es este documento

`LRU_MISION` declara **para qué** existe el proyecto. `LRU_FUNDAMENTOS` articula **de qué está hecho** el sistema conceptualmente. `LRU_ARQUITECTURA` describe **cómo está construido hoy**: las piezas de código concretas, su estado, sus interdependencias, sus fronteras. Es el documento al que se acude cuando hay que saber dónde vive una responsabilidad, qué fichero tocar, qué capa respetar.

Este documento habla del sistema real. Cuando algo mencionado aquí esté desplegado y en uso, se afirma en presente. Cuando algo esté diseñado pero no operativo, se nombra con su estado explícito. Los gaps conocidos y las tensiones arquitecturales abiertas viven en `LRU_MAPA`; aquí solo se nombran en la sección final como recordatorio con puntero, sin desarrollarse.

La autoridad conceptual sobre las primitivas, las operaciones, la sesión multipistas y las distinciones canónicas reside en `LRU_FUNDAMENTOS`. Este documento no las redefine — las **materializa en ingeniería**.

---

## 1 · Estado general

**Plataforma:** PWA6 — React 18 + TypeScript + Vite. Caso de uso principal aeronáutico (A320 CFM56-5B); arquitectura agnóstica al dominio por diseño.

**Ubicación de código:** `C:\react\pwa6\`. Backups automáticos en `D:\lru-backups\`.

**Infraestructura activa:**

* Docker Desktop corriendo RabbitMQ 4.2.5, Redis 7.4 y Caddy 2.
* Node-RED como servicio Windows vía `nssm`.
* Firebase para persistencia selectiva (detalles en `LRU_METODO` §10 pendiente de articular).
* Tres ESP32 con firmware `rpc1cl` v2.1.0 conectados por BLE y/o MQTT.
* RabbitMQ traduce simultáneamente entre cinco protocolos: 1883 MQTT 5.0, 5672 AMQP 0-9-1, 15674 STOMP/WebSocket, 5552 Streams, 15672 Management HTTP.

**Estado de madurez al cierre de s60:**

* Capas 0-2 consolidadas en código desde hace muchas sesiones. Sin cambios estructurales desde s44.
* Capa 3 (Realm v2) **schema desplegado** en `src/realm/` desde s56 — validado, con typecheck limpio y 10/10 tests pasando, pero **no consumido todavía** por el runtime (`SimService` sigue usando `sim-catalog.ts`). C2 sigue abierto como ítem crítico único.
* Sistema documental refundado en s57 en tres anillos (ver `LRU_METODO` §1); anillo 0 (Proyecto de Claude como memoria de arranque) articulado en s60. Este documento es parte del núcleo resultante.
* Destilado `lru_realm_v2_analysis_s60.md` disponible como andamiaje para C2 fase A.

**Qué está desplegado y qué no, en una línea cada uno:**

| Pieza | Estado |
|---|---|
| DataPlane (capa 1) | Desplegado, estable, canónico |
| CommsManager + 3 adaptadores activos | Desplegado, 4 adaptadores pendientes de roadmap |
| ORC + EnlacesPanel + RouterService + ControlBus | Desplegado, estable |
| Analyzer1 | Desplegado con 3 de 9 protocolos |
| SimService | Desplegado, motor vigente |
| IB_04 | Desplegado, IB_03 eliminado en s38 |
| Realm v2 schema | **Desplegado s56 — no integrado en runtime todavía** |
| Debug1 subsistema transversal | Parcial — viewer externo es el mayor gap del proyecto |
| Motor de vistas unificado | No existe — horizonte implícito articulado en `LRU_FUNDAMENTOS` §3.4 |

Las secciones siguientes detallan cada pieza.

---

## 2 · Las cuatro capas

El sistema se estratifica en cuatro capas con propósito único cada una. Las capas superiores dependen de las inferiores; las inferiores ignoran por completo a las superiores. Esta estratificación **no es imposición** — emerge descriptiva del código existente y consolidó en s53 como modelo arquitectural.

Las cuatro fronteras de comunicación (F1 in-memory, F2 inter-tab, F3 red, F4 hardware) son **ortogonales a las capas**: las capas describen niveles de abstracción; las fronteras describen barreras físicas que los datos atraviesan. Un mismo valor puede cruzar varias fronteras mientras permanece en la misma capa. `LRU_FUNDAMENTOS` §2.2 añade F5 sesión↔sesión como frontera emergente para el viewer externo de Debug1 y los replay mixed.

### 2.1 · Capa 0 — Hardware

El mundo físico donde las magnitudes existen antes de ser números. Sensores reales (ADC, GPIO, I2C, SPI), actuadores reales, protocolos eléctricos (ARINC 429, MIL-STD-1553 en roadmap, MQTT/BLE/WebSocket en producción).

Desde el punto de vista del código, esta capa es **externa**. El sistema la observa y la intervene a través del CommsManager (capa 2), pero ningún fichero del repo vive en la capa 0.

Actores concretos conectados hoy:

* Tres ESP32 con firmware `rpc1cl` v2.1.0 (identificados `A92418` entre otros). Conexión dual BLE (GATT, 7 tipos de mensaje) o MQTT (broker RabbitMQ).
* Teléfonos con sensores de IMU, GPS, giróscopo como fuente de señales.
* Banco de pruebas con motor real (ocasional, fabricante de motores) cuando se prueba integración con Realm A320 híbrido.

### 2.2 · Capa 1 — DataPlane

**Único bus de tiempo real del sistema.** Responde a la pregunta *"¿dónde vive el valor de cualquier señal ahora mismo?"*.

| | |
|---|---|
| Fichero | `src/simulation/DataPlane.ts` (24.88 KB) |
| Estructura | `Float64Array` con 137+ canales (62 sim + 75 hw al último inventario) |
| Identificación | `sensorId` como string opaco con formato `{fuente}/{puerto}` |
| Lookup | O(1) por `sensorId` |
| API pública | `writeBySensorId(sensorId, value, source)`, `readBySensorId(sensorId)`, `subscribe(callback)` |
| Arbitraje | Toda escritura pasa por `ControlBus.allows(id, source)` antes de ejecutarse |
| Frecuencia | Productores escriben a 60fps; consumidores leen cuando necesitan (patrón pull) |
| Throttle | Dirty-flags + throttle 20fps para consumidores React |
| Exposición debug | `window.__DataPlane_instance` para inspección desde consola |

El DataPlane **no conoce semántica**. Almacena un número con un identificador opaco. No sabe que `sim1_eng/N1_L` es una velocidad de rotación de motor. La semántica vive en la capa 3 (Realm); el DataPlane ignora por completo su existencia.

Cualquier valor de sensor en el sistema **fluye por el DataPlane**. Los hot paths no usan React Context, DOM events ni serialización JSON innecesaria. React Context queda para estado de UI, no para datos de sensores (principio 4 del §8).

### 2.3 · Capa 2 — Tres servicios paralelos

Alrededor del DataPlane giran tres servicios independientes con roles ortogonales. **No son variaciones del mismo concepto** — son tres cosas distintas. Detalle de cada uno en §3.

Resumen de la capa 2:

| Servicio | Pregunta que responde | Estado |
|---|---|---|
| CommsManager | ¿Cómo llegan los datos al sistema? | 3 adaptadores activos de 7 declarados |
| ORC + EnlacesPanel | ¿Quién alimenta a quién dentro del sistema? | Estable, modular |
| Analyzer1 | ¿Qué está pasando por los cables ahora mismo? | 3 protocolos de 9 en roadmap |

Se añade un **cuarto elemento transversal** — Debug1 como subsistema que observa las cuatro capas. No es cuarto servicio paralelo (articulación de s55): es perpendicular a los tres anteriores. Detalle en §3.4.

### 2.4 · Capa 3 — Realm (semántica)

La capa que cierra el modelo. Responde a la pregunta *"¿qué significa cada señal?"*.

Un Realm es un **fichero declarativo** que describe un conjunto coherente de señales con su significado completo: entidades semánticas, taxonomía (sistemas ATA, habitaciones, líneas de producción), quantities y unidades, escenarios predefinidos, fallos inyectables, relaciones.

Un Realm **NO contiene**: instancias concretas (viven en runtime), estado vivo (vive en DataPlane), cableado (vive en ORC), configuración de hardware (runtime), representación visual (código de componentes).

La distinción fundamental — articulada en `LRU_FUNDAMENTOS` §8.1 — es **descripción ≠ encarnación**. El Realm describe; la sesión concreta encarna. La misma descripción admite muchas encarnaciones (simulación pura, hardware real, replay histórico, híbrido) sin que el Realm cambie.

**Estado del despliegue** (detallado en §7):

* Schema TypeScript desplegado en `src/realm/` desde s56 — 2108 líneas de código TS nuevo.
* Validadores Zod paralelos con 10/10 tests pasando.
* Realm A320 canónico operativo como ejemplo (`a320.realm.ts`, 18 entities, 4 escenarios, 4 fallos, 2 profiles).
* **NO integrado en runtime todavía.** El SimService sigue consumiendo `sim-catalog.ts`. La integración es el item C2 del tech debt, planificado para sesiones de código después de la refundación documental.

### 2.5 · Por qué la separación de capas funciona

Cada capa ignora la capa superior. El DataPlane no sabe que existe el Realm. El ORC no sabe que `N1_L` es una velocidad de rotación; solo sabe que es un puerto de un hwObj con un canal asignado. El CommsManager no sabe si el payload MQTT es aviación o domótica; solo transporta bytes.

Esta ignorancia deliberada es la propiedad fundamental que permite la **agnosia al dominio** del sistema. Permite que:

* El mismo DataPlane sirva para un A320 simulado hoy y una fábrica real mañana, sin cambios.
* El mismo CommsManager transporte datos del ESP32 o de un PLC industrial, sin cambios.
* El mismo EnlacesPanel conecte instrumentos virtuales con hardware físico o con otros instrumentos virtuales, sin cambios.
* El mismo Analyzer1 inspeccione protocolos aeroespaciales, industriales o IoT, sin cambios.

Lo que cambia entre dominios es exclusivamente la **capa 3**, el Realm. Un `a320.realm` y un `factory.realm` son ficheros distintos, pero los servicios que los consumen son los mismos.

---

## 3 · Los servicios paralelos de la Capa 2

Tres servicios con responsabilidades ortogonales, más un cuarto elemento transversal (Debug1) que observa las cuatro capas.

### 3.1 · CommsManager — transporte

Único servicio que cruza fronteras físicas. Mueve bytes entre la memoria de la aplicación (F1) y el mundo exterior: otros procesos en el mismo navegador (F2), otras máquinas en la red (F3), o el hardware físico (F4). **Su responsabilidad es únicamente llevar datos de un lado a otro.** No conoce semántica, no arbitra, no cablea.

**Adaptadores declarados (7) y estado:**

| Adaptador | Frontera | Estado | Uso |
|---|---|---|---|
| MqttAdapter | F3 | Activo | ESP32 ↔ RabbitMQ ↔ PWA |
| WebSocketAdapter | F3 | Activo | Node-RED ↔ PWA |
| LoopbackAdapter | F1 | Activo | Testing sin hardware |
| StompAdapter | F3 | Pendiente | RabbitMQ STOMP puerto 15674 |
| BleAdapter | F3+F4 | Planificado | ESP32 directo sin broker |
| SerialAdapter | F3+F4 | Planificado | Dispositivos USB/UART |
| SimulationCommsAdapter | F1→F3 | Pendiente | Bridge simulación externa |

Cada adaptador implementa la misma interfaz mínima — `connect / disconnect / subscribe / publish` — y la aplicación ignora por completo cuál está activo. Un mensaje puede llegar por MQTT o por WebSocket; el código que lo recibe es el mismo.

**Anatomía del contrato:** uniforme entre adaptadores en la interfaz, pero **la naturaleza de los datos transportados varía**. MQTT transporta valores de sensores. WebSocket transporta comandos de instructor. BLE transporta ráfagas binarias. El contrato de transporte es uniforme; el significado de lo transportado no.

**Topología de topics MQTT** (dual-publish desde versiones anteriores): `<red>/<id>/<direccion>/<canal>`. Dos direcciones publicadas simultáneamente permiten coexistencia de múltiples instancias PWA sin interferencia.

### 3.2 · ORC + EnlacesPanel — cableado lógico

Corazón del sistema en términos de gestión de entidades. Responde a la pregunta *"¿quién alimenta a quién dentro del sistema?"*.

**ObjectRegistryContext** (`src/datosLru01/ObjectRegistryContext.tsx`, 959 líneas tras modularización s40-s44) mantiene el registro completo:

* `hwObjs` — dispositivos de hardware (físicos o virtuales) con sus sensores (`hw1`) y configuración de escalado (`cfg1`).
* `swObjs` — objetos de software (clips de simulación, componentes React, generadores virtuales) que consumen datos.
* `enlaces` — matriz N:M bidireccional entre los anteriores, con `channelMap` por enlace.

**Módulos extraídos en s40:**

* `registryDebug.ts` — sistema de debug.
* `registryEnlaces.ts` — matriz de enlaces y helpers.
* `registrySensorUtils.ts` — utilidades de sensores (con 3 deduplicaciones aplicadas).
* `registryMqttHandlers.ts` — 6 factories de handlers MQTT vía `MqttHandlerDeps` interface (718 líneas).

**EnlacesPanel** (`src/pages/EnlacesPanel/`) es la UI que permite al usuario construir el grafo visualmente — la **mesa de parcheo** del sistema. Cada celda de la matriz representa un posible cableado entre un instrumento y un dispositivo; click conecta o desconecta señales. Exclusividad atómica por canal: un canal solo puede tener una fuente activa a la vez.

**La distinción puerto ≠ canal** (articulada en `LRU_FUNDAMENTOS` §8.2) se materializa en los tipos canónicos de `src/datosLru01/types.ts` (747 líneas):

* `puerto` es un nombre físico del hardware (`adc1`, `sd2`, `gpio17`).
* `canal` es un nombre lógico asignado por el instrumento (`aguja_altitud`, `led_alerta`, `slider_throttle`).
* `ChannelMap = Record<canal, puerto>` es el cableado entre ambos.

Esta separación permite que el mismo hardware alimente distintos instrumentos sin conocerse entre sí, y que el mismo instrumento se conecte a distintos hardware sin cambiar su definición.

**Servicios arbitrales que orbitan alrededor del ORC:**

| Componente | Fichero | Rol |
|---|---|---|
| RouterService | `src/simulation/RouterService.ts` (15.11 KB) | Gestiona `routerMode` activo por hwObj (`Loc`/`Rnd`/`HwSim`/`HwReal`). Exactamente uno activo a la vez. Maneja transiciones con countdowns (Rnd→HwReal 30s, Loc→HwReal 10s) e inhibiciones de firmware |
| ControlBus | `src/simulation/ControlBus.ts` (10.1 KB) | Arbitra quién puede escribir al DataPlane según el modo del router. Portero: antes de cualquier `writeBySensorId`, se consulta `ControlBus.allows(id, source)` |

**Tabla de permisos del ControlBus** (tras unificación s28 de RandomGenerator en SimService):

| routerMode | slider | mqtt-hw | mqtt-sim | registry |
|---|---|---|---|---|
| `Loc` | ✅ | ❌ | ❌ | ❌ |
| `Rnd` | ❌ | ❌ | ❌ | ✅ |
| `HwReal` | ❌ | ✅ | ❌ | ✅ |
| `HwSim` | ❌ | ❌ | ✅ | ✅ |

### 3.3 · Analyzer1 — observación

El microscopio del sistema. **No mueve datos ni los cablea** — solo los mira cuando cruzan un protocolo.

| | |
|---|---|
| Ubicación | `src/pages/Analyzer1/` (múltiples módulos tras refactor s18) |
| Arquitectura | `ProtocolAdapter` universal. 3 familias soportadas simultáneamente |
| Protocolos activos | ARINC 429 (aviación), MQTT, WebSocket (IoT) |
| Protocolos en roadmap | MIL-STD-1553, AFDX, OPC UA, Modbus, PROFINET, BLE |
| Triple vida | Página, overlay, panel (pattern compartido con EnlacesPanel) |

**Principio de diseño explícito:** *el Analyzer no conoce los detalles de ningún protocolo, solo conoce la interfaz `ProtocolAdapter`*. Añadir un protocolo nuevo es implementar un adaptador y registrarlo.

**Tap points pasivos** (no interfieren con el flujo):

| Tap Point | Origen | Propósito |
|---|---|---|
| `mqtt-debugbus` | DebugBus canal `mqtt+comms` | Ver tráfico real del hardware |
| `mqtt-sim` | MqttSimulator interno | Tráfico sintético para test |
| `arinc429-sim` | Arinc429Simulator vinculado a Sim1 | Tráfico A429 generado por simulación |
| `ws-real` | CommsManager canal `ws*` | Tráfico WebSocket a Node-RED |

**Valor arquitectural:** el Analyzer es **pasivo pero activo en valor**. Permite al ingeniero verificar que el contrato entre hardware y software se respeta en tiempo real, sin depurar con breakpoints ni inyectar instrumentación. Es la *transparencia arquitectural* (principio 8) materializada.

**Tabla A320 ARINC 429:** ampliada en s20 de 22 a 98 labels (9 sistemas ATA, con sistema de confianza ★/★★/★★★).

### 3.4 · Debug1 — subsistema transversal

Debug1 **no es cuarto servicio paralelo** de la capa 2 (articulación de s55, ratificada en s56). Es un **subsistema transversal** que observa las cuatro capas simultáneamente: puede inyectar valores al DataPlane (capa 1), interceptar tráfico de CommsManager (capa 2), inspeccionar el ORC (capa 2), y eventualmente mostrar semántica del Realm (capa 3).

**Inventario de componentes** (cruzados en análisis s55, 10 elementos identificados):

* `DebugBus` — bus de eventos de diagnóstico (F1 in-memory), con 10-11 canales documentados según fuente (tensión abierta registrada en `LRU_MAPA`).
* `Debug1` (página) — UI principal del subsistema en `src/pages/Debug1/`.
* `Debug1Overlay` — overlay que se monta sobre otras vistas.
* `DebugLogPanel` — panel de logs embebible.
* `RemoteViewer` — visor remoto (parcialmente implementado).
* `DataPlaneDebug` — inspector del estado del DataPlane.
* `RegistryDebug` — inspector del estado del ORC (`window.RegistryDebug`).
* `DebugBusController` — singleton para monitoreo de sesión remota.
* Clips de debug embebidos en instrumentos — ver tensión abierta sobre nomenclatura `Det2` vs `Debug1` registrada en `LRU_MAPA`.
* Subsistema `test1` (self-test embebido en clips) — documentación pendiente, registrada en tech debt.

**Estado actual:** parcial. Hay piezas funcionales dispersas pero **el viewer externo unificado no existe**. La visión fundacional de marzo 2026 prometía *"pantalla externa · filtros · timeline · diff · replay"* y sigue sin materializarse completa. Este es el **gap mayor entre visión e implementación del proyecto** — ver §9.

**Las 4 restricciones de observabilidad** (articuladas en `lru_debug1_analysis_s55.md` como requisitos del Realm v2 para no bloquear el viewer) **están cumplidas** por el schema desplegado en s56:

1. JSON-serializable verificado con round-trip (9.4 KB).
2. `ScenarioDef` y `FaultDef` con identidad inspectable.
3. `FaultDef.emitOnBus` declarativo (no requiere código ad-hoc para emitir eventos).
4. El schema no restringe `authorId` en runtime (preserva atribución).

Esto significa que **el viewer externo puede construirse sin refactor arquitectural** del schema — está desbloqueado arquitecturalmente.

---

## 4 · Las fronteras de comunicación

Ortogonales a las capas. Las capas describen **niveles de abstracción**; las fronteras describen **barreras físicas** que los datos atraviesan. Un valor puede cruzar tres fronteras (ESP32 físico → red MQTT → memoria del navegador) mientras permanece en la misma capa (capa 1 / DataPlane).

| Frontera | Qué atraviesa | Mecanismo típico | Estado |
|---|---|---|---|
| **F1** | In-memory dentro del mismo proceso | Llamada de función directa, ref compartida, DataPlane | Siempre activa |
| **F2** | Inter-tab dentro del mismo navegador | BroadcastChannel, SharedWorker | Usada para PipManager |
| **F3** | Red — entre máquinas distintas | MQTT, WebSocket, STOMP, AMQP | Activa con tres adaptadores |
| **F4** | Hardware — mundo eléctrico ↔ digital | ADC, GPIO, I2C, SPI, BLE GATT, serial | Activa vía firmware ESP32 |
| **F5** | Sesión↔sesión — entre dos instancias temporales | RabbitMQ Streams, sesión multipistas | **Emergente** — articulada en `LRU_FUNDAMENTOS` §2.2, sin implementación |

F5 es frontera nueva articulada en s57. Conecta dos encarnaciones distintas del mismo sistema a través del tiempo: una sesión grabada hoy y otra reproducida mañana, o una sesión en Madrid y su viewer en otra ciudad. La infraestructura (RabbitMQ Streams) está desplegada; el módulo que graba y el que reproduce como sesión multipistas está pendiente. Ver §9.

---

## 5 · La señal con 8 dimensiones

La señal es el ladrillo atómico del sistema (articulada conceptualmente en `LRU_FUNDAMENTOS` §2.1). Esta sección documenta **cómo se materializa cada dimensión en ingeniería**, qué fichero la contiene, qué gap queda en cada una.

**Dimensiones intrínsecas** (viven en el Realm — capa 3):

| # | Dimensión | Materialización actual | Fichero | Gap |
|---|---|---|---|---|
| 1 | **Identidad** — `sensorId` con formato `{fuente}/{puerto}` | Tipo `SensorId` en types canónicos; clave primaria en DataPlane y ORC | `src/datosLru01/types.ts`, `src/simulation/DataPlane.ts` | Ninguno |
| 2 | **Dirección** — observable / actuable / bidireccional | Campo `emitsAs?: ObjId` en `SwObj` permite productor+consumidor simultáneo | `src/datosLru01/types.ts` | Representación explícita en UI parcial |
| 3 | **Magnitud** — familia física | `QuantityDef` hardcoded hoy; migrará a Realm v2 | `src/simulation/quantities.ts` (36.65 KB · 21 quantities · 62 sensor map · modelo ISA dimensionado con 6 funciones directas+inversas) | Migración al Realm pendiente (C2) |
| 4 | **Contrato de unidades** — rango, unidad primaria, conversiones | `units.ts` (7.2 KB) con ISA simplificado aeronáutico (3 funciones ratio) + 6 fórmulas de aeronáutica aplicada (iasTas/tasMach/machTas/iasMach/computeTAT/computeVMO); el schema Realm v2 declara, no ejecuta | `src/simulation/units.ts` | Motor de evaluación en SimService pendiente (C2 fase C). Las 6 fórmulas aerodinámicas son candidatas directas a `namedFormulas` del motor |
| 5 | **Contrato de transporte** — codificación por protocolo | `ProtocolEncoding` en schema Realm v2; no consumido aún | `src/realm/types.ts` | Uso efectivo por Analyzer1 y CommsManager pendiente |

**Dimensiones contingentes** (viven en runtime — no en el Realm):

| # | Dimensión | Materialización actual | Fichero | Gap |
|---|---|---|---|---|
| 6 | **Origen** — RouterMode (Loc/Rnd/HwSim/HwReal) | `routerMode` string literal union por hwObj; gestionado por RouterService | `src/simulation/RouterService.ts` | Ninguno |
| 7 | **Calidad** — válido / sospechoso / muerto / sintético | Parcial en `StatusMap` del hwObj (`isOnline`, `isRx`...) | ORC `StatusMap` | Modelo de calidad completo pendiente |
| 8 | **Tiempo** — vivo / grabado / programado | Vivo en DataPlane. Grabado en `SimSequenceRecorder/Player`. Programado en `EscenariosContext` | Tres sitios distintos | **Sin herramienta unificada** — ver §9 |

**Gotcha de debugging registrado** (hallazgo 4 de la síntesis s55, no documentado en el consolidated s53): `source` en el DataPlane no identifica origen real del dato, identifica puerta de entrada al ORC. `source='registry'` puede significar tanto *"SimService demoInterval"* como *"ORC tras procesar events MQTT"* según el `routerMode` activo. Para trazabilidad real hay que cruzar `source` con `routerMode`. Esta tensión está registrada en `LRU_MAPA`.

---

## 6 · Componentes principales del código

Inventario operativo. Cada entrada con path, tamaño/líneas, responsabilidad y dependencias. Organizado por capa y por servicio para facilitar la consulta.

### 6.1 · Capa 1 — DataPlane

| Componente | Path | Tamaño | Estado |
|---|---|---|---|
| DataPlane | `src/simulation/DataPlane.ts` | 24.88 KB | Canónico, sin cambios estructurales desde s22 |
| DataPlaneDebug | `src/simulation/DataPlaneDebug.ts` | — | Inspector del estado del DataPlane (parte de Debug1) |

### 6.2 · Capa 2 — ORC y arbitraje

| Componente | Path | Tamaño | Estado |
|---|---|---|---|
| ObjectRegistryContext | `src/datosLru01/ObjectRegistryContext.tsx` | 976 líneas | Modular desde s40 (4 módulos extraídos) |
| Tipos canónicos | `src/datosLru01/types.ts` | 747 líneas | Geometría del dominio |
| registryDebug | `src/datosLru01/registryDebug.ts` | — | Debug del registro |
| registryEnlaces | `src/datosLru01/registryEnlaces.ts` | — | Matriz y helpers de enlaces |
| registrySensorUtils | `src/datosLru01/registrySensorUtils.ts` | — | Utilidades de sensores |
| registryMqttHandlers | `src/datosLru01/registryMqttHandlers.ts` | 718 líneas | 6 factories MQTT vía `MqttHandlerDeps` |
| RouterService | `src/simulation/RouterService.ts` | 15.11 KB | Singleton, gestiona routerMode |
| ControlBus | `src/simulation/ControlBus.ts` | 10.1 KB | Singleton, arbitra escrituras |

### 6.3 · Capa 2 — CommsManager

| Componente | Path | Estado |
|---|---|---|
| CommsManager núcleo | `src/comms/` (varios) | Orquestador con interfaz uniforme |
| MqttAdapter | `src/comms/adapters/` | Activo |
| WebSocketAdapter | `src/comms/adapters/` | Activo |
| LoopbackAdapter | `src/comms/adapters/` | Activo |
| BleAdapter | `src/comms/adapters/` | Planificado |
| 3 adaptadores más (Stomp, Serial, SimComms) | — | Pendiente o planificado |

### 6.4 · Capa 2 — Simulación

| Componente | Path | Estado |
|---|---|---|
| SimService | `src/simulation/SimService.ts` | Singleton vanilla TS persistente, sobrevive a navegación React. 60fps |
| SimServiceReact | `src/simulation/SimServiceReact.ts` | Bridge pattern React ↔ SimService |
| SimSequenceRecorder | `src/simulation/` | Grabación básica funcional |
| SimSequencePlayer | `src/simulation/` | Reproducción básica funcional |
| sim-catalog | `src/simulation/sim-catalog.ts` | 89.75 KB · 5 displays (ecam/pfd/nd/fdr/hw), 25 systems, 205 sensores (127 declarados + 78 hw generados dinámicamente por `buildHwSensors` sobre 3 dispositivos BLE), 17 escenarios (10 fases normales + 7 emergencias) con ~600 overrides temporales, 33 faults — **candidato a migrar al Realm v2** (C2) |
| quantities | `src/simulation/quantities.ts` | 36.65 KB · 21 quantities · 14 dimensiones · 62 sensor→quantity map · modelo ISA dimensionado con 6 funciones (directas+inversas: pressure↔altitude, temperature↔density, density@altitude) + `represent()` · candidato al Realm v2 |
| pfd-catalog | `src/simulation/pfd-catalog.ts` | 23.44 KB · source s37 congelado tras integración a `sim-catalog.ts` · conserva 7 flight phases y 17 A320_CHARACTERISTIC_SPEEDS · candidato al Realm v2 (los characteristic speeds a `AircraftProfile`) |
| units | `src/simulation/units.ts` | 7.2 KB · ISA simplificado aeronáutico (3 funciones ratio: `isaSAT`, `isaPressureRatio`, `isaDensityRatio`) + 6 fórmulas de aeronáutica aplicada (`iasTas`, `tasMach`, `machTas`, `iasMach`, `computeTAT`, `computeVMO`) — se mantiene fuera del schema Realm; las 6 fórmulas son candidatas a `namedFormulas` del motor (fase C) |
| EscenariosContext | `src/pages/Sim1/EscenariosContext.tsx` | Presets de cableado (snapshots EnlacesPanel) |

### 6.5 · Capa 2 — Bridge e instrumentación

| Componente | Path | Estado |
|---|---|---|
| InstrumentBridge_04 (IB_04) | `src/pages/Sim1/bridge/` | Consolidado s37-s44. Pipeline 2-link: `DataPlane → IB_04.tick → change1()` |
| useIB04Registry hook | — | Registro ORC ↔ IB_04 |
| IB04RegistryBridge | — | Componente puente |
| ClipManifest | — | Tipo con `defaultHwObj` para hardware real mode |

**IB_03 eliminado en s38** (~4800 líneas, 9 ficheros, 14 paquetes npm: BabylonJS, Three.js, etc.). Pendiente auditar residuos (tech debt).

### 6.6 · Capa 2 — Analyzer1

| Componente | Path | Estado |
|---|---|---|
| Analyzer1 shell | `src/pages/Analyzer1/` | Triple vida: página, overlay, panel |
| protocols/ (por subfolder) | `src/pages/Analyzer1/protocols/` | Uno por protocolo; 3 activos |
| bottom/ | `src/pages/Analyzer1/bottom/` | 10 ficheros, 6 tabs |
| ARINC 429 adapter | `protocols/arinc429/` | 98 labels, 9 sistemas ATA, ★/★★/★★★ |
| MQTT adapter | `protocols/mqtt/` | Tap points mqtt-debugbus y mqtt-sim |
| WebSocket adapter | `protocols/ws/` | Tap point ws-real |

### 6.7 · Capa 2 — UI principal

| Componente | Path | Estado |
|---|---|---|
| Sim1 | `src/pages/Sim1/` | Vista típica del instructor |
| Col4 | `src/pages/Col4/` | Vista del alumno |
| PanelGrid v3 | `src/panelgrid/` | Sistema de paneles con drag, split/merge, float, snap, tab reorder |
| EnlacesPanel | `src/pages/EnlacesPanel/` | Panel #10 — mesa de parcheo |
| Analyzer1 embebido | Panel #11 | Triple vida |
| Hw1AdminPanel | `src/pages/Hw1Admin/` | Modularizado s44 (2539→118 líneas, 11 ficheros) |
| TechModal | `src/components/` | Draggable/resizable genérico extraído en s49 |

### 6.8 · Capa 3 — Realm v2

| Componente | Path | Líneas | Estado |
|---|---|---|---|
| types | `src/realm/types.ts` | 542 | 18 interfaces TS puras, 100% JSON-serializable |
| validators | `src/realm/validators.ts` | 670 | Zod paralelo, `parseRealm`, `safeParseRealm`, `applyProfile`, 7 validaciones cruzadas |
| index | `src/realm/index.ts` | 73 | Barrel de exports públicos |
| a320.realm ejemplo | `src/realm/examples/a320.realm.ts` | 674 | 18 entities, 4 taxonomy nodes, 4 derivados, 2 escenarios, 4 fallos, 2 profiles (a320-214, a320neo) |

**Total Realm v2: 2108 líneas TS**, código nuevo introducido en s56 sin modificar código existente. Typecheck limpio, 10/10 tests pasando. Detalle completo en §7.

### 6.9 · Infraestructura Realm v1 (s52) — estado tras s53/s56

Infraestructura creada en s52 con un schema v1 que quedó obsoleto tras el rediseño de s53 y la implementación v2 en s56:

| Fichero | Estado |
|---|---|
| `src/realm/schema.ts` (Zod v1) | Obsoleto — reemplazado por `types.ts` + `validators.ts` del v2 |
| `src/realm/RealmLoader.ts` | Estado actual desconocido — **tensión abierta registrada en `LRU_MAPA`** |
| `src/realm/query.ts` | Estado actual desconocido — tensión abierta registrada en `LRU_MAPA` |
| `src/realm/crossFunctions.ts` | Estado actual desconocido — tensión abierta registrada en `LRU_MAPA` |
| `src/realm/a320.realm` (v1 ejemplo) | Descartado |
| `src/realm/home-lab.realm` (v1 ejemplo) | Descartado |

Auditoría de residuos v1 pendiente como item de cleanup. Ver `LRU_MAPA`.

### 6.10 · Debug1 — subsistema transversal

Ver §3.4. Componentes distribuidos en `src/pages/Debug1/`, `src/simulation/DataPlaneDebug.ts`, `src/comms/DebugBus.ts`. Estado parcial; viewer externo unificado es gap mayor documentado en §9.

---

## 7 · El sistema Realm v2

El Realm v2 es la materialización en TypeScript de la capa 3 (semántica) del sistema. Schema desplegado en s56 tras un rediseño iniciado en s53 y tras una lectura arquitectural profunda en s55 que articuló restricciones concretas.

### 7.1 · Estado del despliegue

Todos los ficheros en `src/realm/` (2108 líneas nuevas de TS). Validaciones pasadas:

1. Typecheck TS estricto sin errores.
2. Realm A320 valida contra `RealmManifestSchema`.
3. `safeParseRealm` rechaza input inválido.
4. `applyProfile(a320neo)` aplica overrides correctamente (EGT_L range [0,1050], thresholds w=[900,960]).
5. Round-trip JSON (9.4 KB serializable sin pérdida).
6. Detecta sensorId inexistente en initialState.
7. Detecta faultId inexistente.
8. Detecta profile.extendsRealm incorrecto.
9. Detecta input inexistente en derivedSensor.
10. Formulas piecewise anidadas aceptadas (recursión `z.lazy`).

Smoke test en runtime del navegador verificado el 2026-04-18.

### 7.2 · Las cuatro decisiones arquitecturales (Q1 / Q2-ter / Q3 / Q4)

Decisiones tomadas en s56 con consentimiento explícito de Manuel, tras pushback productivo que refinó Q2 de su formulación inicial a una variante aceptable arquitecturalmente.

**Q1 — Actor/pieza/frontera (misión).** Solo campo `describes: string` obligatorio en `RealmManifest`. El actor vive en runtime (ORC + EnlacesPanel), no en el schema. Preserva la separación descripción/encarnación y evita duplicar con el ORC.

**Q2-ter — Derivaciones declarativas híbrido A+B.** Las derivaciones son datos, no código. El schema declara fórmulas como union discriminada:

* 6 primitivos declarativos cubren el 95% de casos: `constant`, `linear`, `piecewise`, `lookup`, `arithmetic`, `mach-to-ias`.
* `named` como excepción para implementaciones complejas (ISA atmosphere, TAT ram recovery, Saint-Venant).

**Consecuencia arquitectural preservada:** IB_04 sigue siendo agnóstico (`dp.read(slot)` sin más). SimService ejecutará las fórmulas vía `evaluateFormula(formula, dp)` — pendiente, forma parte de C2 fase C.

**Q3 — Selección de perfil.** Cada `ScenarioDef` declara `realm: string` y `profile?: string`. EscenariosContext los activa al entrar. Máxima flexibilidad: cambiar escenario = cambiar avión si se desea.

**Q4 — IAS↔Mach.** Aproximación subsónica. Las fórmulas viven en `units.ts` (ya existe), fuera del schema. El schema solo declara qué quantities y unidades existen.

### 7.3 · Las cuatro restricciones cumplidas

Las restricciones del handoff s55→s56 — todas cumplidas por el schema:

| # | Restricción | Implementación |
|---|---|---|
| 1 | Misión: actor + pieza + frontera | `describes: string` obligatorio; actor vive en runtime |
| 2 | Perfiles multi-modelo | `RealmProfile` genérico + `AircraftProfile` extendido + `applyProfile()` inmutable |
| 3 | Escenarios didácticos vivos | `ScenarioDef` con timeline, availableFaults, availableOverrides, conditions, narrative |
| 4 | Observabilidad Debug1 | JSON-serializable (round-trip OK, 9.4 KB); ScenarioDef y FaultDef con identidad inspectable; `FaultDef.emitOnBus` declarativo; schema no restringe `authorId` runtime |

### 7.4 · Los ficheros a migrar al Realm (C2)

Contenido semántico hoy hardcoded en `src/simulation/`, total **157 KB** (tamaños reales detectados en inventarios s62-s63), candidato a migración a `a320.realm.ts` ampliado:

| Fichero | Contenido | Destino en Realm v2 |
|---|---|---|
| `sim-catalog.ts` | 5 displays (ecam/pfd/nd/fdr/hw), 25 systems, 205 sensores (127 declarados + 78 hw generados), 17 escenarios con ~600 overrides temporales, 33 faults, `HW_DEVICE_IDS` + `buildHwSensors` | `RealmManifest.entities` + `taxonomy` + `scenarios` + `sensorFaults` + `actuatorFaults` + instancias de `HardwareProfile` para los 3 dispositivos BLE. **Scenarios: `apply-profile` MATERIALIZADO EN CÓDIGO en s77** (T-S63-8 cerrada completa por materialización + T-AU72-4 cerrada): el diseño del destilado s64 aplicado literalmente a `src/realm/types.ts` + `validators.ts` (`ScenarioEvent` como unión discriminada estricta de 5 kinds + `ProfileApplication` de 4 shapes + `ScenarioDef.durSeconds?` + validación monotónica de `t`). Test de smoke 24/24 verde contra ejemplo `preflight` del destilado §4.2. **Faults MATERIALIZADOS EN CÓDIGO en s78** (T-S60-O6 cerrada por materialización + T-AU72-2 cerrada completa + T-S63-renombres cerrados por materialización): separación `SensorFaultDef` (16 kinds) + `ActuatorFaultDef` (6 kinds) con `FaultDefBase` común aplicada al schema. `RealmManifest.faults?` desdoblado en `sensorFaults?` + `actuatorFaults?` con unicidad cruzada de ids validada en `superRefine`. **CruisePattern MATERIALIZADO EN CÓDIGO en s78**: `CruisePatternDef` como unión discriminada de 3 types (constant/sine/linear) en `EntityDef.pattern?` con `cyclic` absorbido en `sine+wrapAt?`. Test de smoke 31/31 verde (no-regresión + CruisePatternDef + SensorFaultDef + ActuatorFaultDef + regresiones deseadas + superRefine cruzado). Migración de los 17 scenarios + 33 faults reales (~300 overrides con profile declarado + resto como freeze implícito; 28 sensor faults + 2 actuator faults + 3 genéricos UI excluidos del realm) pendiente para cuando el motor consuma Realm v3. Ver `lru_scenario_event_extension_s64.md` (referencia histórica post-materialización) y `lru_faultdef_refactor_s65.md` (referencia histórica post-materialización tras s78) |
| `quantities.ts` | 21 `QuantityDef`, 62 sensor map, modelo ISA dimensionado | `RealmManifest.quantities` |
| `pfd-catalog.ts` | 7 flight phases, 17 A320_CHARACTERISTIC_SPEEDS | Los speeds a `AircraftProfile.vmo/mmo/crossoverAlt/vfe/vle`; las phases no migran como entidad — emergen de `scenarios` con overrides `at: 0` |
| `units.ts` | ISA simplificado aeronáutico + 6 fórmulas aerodinámicas | **Se mantiene fuera** del schema (Q4) — el schema declara, no ejecuta. Las 6 fórmulas pueblan el registry `namedFormulas` del motor |

**Plan de migración C2** (tres fases, ver tech debt para detalle):

* **Fase A** — extraer `sim-catalog.ts` al formato Realm manteniendo SIM_CATALOG operativo en paralelo.
* **Fase B** — SimService carga Realm en lugar de SIM_CATALOG. Resolución de sensorIds sigue igual (DataPlane no cambia).
* **Fase C** — implementar `evaluateFormula(formula, dp)` en SimService. Registro de `namedFormulas` con ISA, TAT, TAS.

### 7.5 · Relación con documentos arquitecturales anteriores

El schema v2 **no se inventó** — absorbe material arquitectural documentado meses antes:

| Documento precedente | Absorción en schema |
|---|---|
| `lru_fase4_signal_model.html` (s18) | Interfaces `QuantityDef`, `ProtocolEncoding`, `SensorDefinition` adoptadas literales en `types.ts` |
| `lru_ib04_binding_model_s32.html` | Dos modos de declaración (sensorId/channel) en `NeedleManifest` |
| `lru_ib04_hardware_model_s32.html` | Jerarquía display/system/sensor mapeada a `RealmManifest.entities + taxonomy` |
| `lru_aircraft_model_design_s33.html` | `AircraftProfile` con 15+ campos materializado. D1/D2/D3 resueltas como Q2-ter/Q3/Q4 |

Estos documentos siguen vigentes como **fuente del porqué** de cada campo del schema, pero su contenido arquitectural está materializado. El MAPA los referencia como "prehistoria absorbida".

### 7.6 · Lo que el Realm v2 NO hace

Clarificaciones importantes para evitar malentendidos:

* **NO ejecuta cálculos** — los declara como datos. El motor (SimService) ejecuta.
* **NO conoce runtime** — no sabe del DataPlane, del ORC, de conexiones activas.
* **NO reemplaza EscenariosContext** — los "escenarios" del EnlacesPanel (cableado) son distintos de los "scenarios" del Realm (comportamiento temporal). Ambos coexisten. Ver `LRU_FUNDAMENTOS` §8.3.
* **NO introduce `Date`, `Map`, `Function`** — es 100% JSON-serializable por decisión de diseño (round-trip garantizado).
* **NO está consumido todavía** — SimService sigue usando `sim-catalog.ts`. La integración real es C2.

---

## 8 · Los diez principios arquitecturales

Consolidados en s53 a partir de patrones ya presentes en el código. **No son principios que se proponen — son principios que el sistema ya sigue**, tan consistentemente que merecen estar escritos. Cualquier decisión futura que los viole probablemente está mal.

1. **Agnosia al dominio.** Ninguna capa por debajo de la 3 sabe si está tratando con un avión, una fábrica, una casa o un cuerpo humano. La especialización vive en el Realm, que es un fichero de datos.

2. **Protocolo = contrato.** Los topics MQTT, los ProtocolAdapter, las interfaces de tipo son contratos explícitos y estables. Los implementadores se obedecen mutuamente a través del contrato; no se conocen directamente.

3. **Transporte invisible.** La aplicación no sabe si los datos llegan por MQTT, WebSocket, BLE o generador local. Cambiar el transporte no requiere cambiar el código que consume los datos.

4. **DataPlane como única fuente de verdad en tiempo real.** Todo valor de sensor vivo fluye por el DataPlane. Los hot paths no usan React Context, DOM events ni serialización JSON innecesaria. React Context queda para estado de UI, no para datos de sensores.

5. **Los datos fluyen, las páginas observan.** Ningún componente de UI debe ser necesario para que los datos existan. Los servicios producen, los buses transportan, las páginas consumen. SimService sigue funcionando aunque no haya ningún panel montado.

6. **Escritura protegida.** Cualquier escritura al DataPlane pasa por `ControlBus.allows()`. Cualquier cambio de modo pasa por `RouterService`. No hay escritura libre — siempre hay árbitro.

7. **Sin ruptura.** Cada cambio importante es aditivo, no sustitutivo. IB_04 convivió con IB_03 durante meses. El Realm v2 convive con `sim-catalog.ts` durante la transición C2. El sistema siempre está en estado funcional.

8. **Transparencia arquitectural.** Cada capa es inspeccionable. Analyzer1 para protocolos. `window.RegistryDebug` para el ORC. DebugBus para eventos. DataPlaneDebug para el bus. El usuario puede ver qué está pasando en cualquier punto.

9. **La mesa de parcheo como control del usuario.** El usuario (no la aplicación) decide qué alimenta a qué. EnlacesPanel, presets de escenarios, RouterMode son formas de dar control al operador. El sistema propone defaults razonables; el operador decide la realidad.

10. **Memoria permanente del proyecto.** Cada sesión produce documentos canónicos (ver `LRU_METODO` §3). La arquitectura se documenta fuera del código. Los principios se escriben. Las decisiones no obvias se registran. Una nueva instancia de colaborador — humano o IA — llega a velocidad completa leyendo los cinco documentos del núcleo.

**Principio 11 implícito, pendiente de articulación explícita** (hallazgo 14 síntesis s55, registrado en `LRU_MAPA`): **la plataforma es herramienta de aprendizaje**. La dimensión formativa es origen fundacional del proyecto y vive en `LRU_MISION`, pero no está articulada como principio arquitectural explícito en este inventario. Si una sesión futura decide añadirla a los 10 consolidados, será por aportación sustancial al núcleo y no por inercia.

**Postulado 10 amplía pero no absorbe el principio 11 implícito** (anotación s91 al integrar T-S84-F1). El Postulado 10 *"El diseño del Realm como espacio de innovación dentro del respeto"* — integrado al núcleo en s91 como `LRU_FUNDAMENTOS §11quater` — articula la libertad creadora del diseñador del Realm como propiedad estructural del **acto de construir** la plataforma. Tiene cercanía con el principio 11 implícito pero no es idéntico: P10 articula la dimensión del acto de construir el Realm (libertad del diseñador para innovar dentro del marco de respeto regulatorio); el principio 11 implícito articula la dimensión arquitectural propia (cómo se construye el sistema técnicamente para servir aprendizaje). Son complementarios, no sustitutivos. El principio 11 sigue pendiente de articulación arquitectural propia si una sesión futura decide elevarlo. La diferencia se preserva por honestidad estructural.

**Postulado 11 refuerza el principio 1 (agnosia al dominio) en su dimensión estructural fina** (anotación s99 al integrar T-S97-F1). El Postulado 11 *"Disposición evolutiva del diseño estructural del realm específico"* — integrado al núcleo en s99 como `LRU_FUNDAMENTOS §11quinquies` — articula que la agnosticidad al dominio no se agota en "el código de capas 0-2 no sabe del dominio" (principio 1 clásico): incluye también que **el código del realm específico tiene libertad estructural propia** sin replicar plantilla impuesta. La cadena de refuerzos del principio agnóstico al dominio es: P5 establece agnosticidad estratégica (qué dominio se aborda primero · `LRU_MISION`) · §11ter la materializa espacialmente (paquetes separados por sector · `LRU_FUNDAMENTOS §11ter`) · P11 la materializa formalmente (forma interna libre por sector · `LRU_FUNDAMENTOS §11quinquies`). P11 tampoco absorbe el principio 11 implícito · son cercanos pero distintos como con P10. P10 y P11 forman par fundacional del diseñador del Realm: libertad creadora sobre el contenido (P10) + disciplina evolutiva sobre la estructura (P11) · creatividad estructurada como disciplina del diseñador.

**Postulado 12 formaliza la Capa 0 como sustrato del sistema** (anotación s123 al integrar T-S117-F1). El Postulado 12 *"El hardware físico es sustrato del sistema"* — integrado al núcleo en s123 como `LRU_FUNDAMENTOS §11sexies` — articula que la Capa 0 (Hardware) descrita en `§2.1` no es capa externa al sistema sino **sustrato suyo**. Tres encarnaciones físicas con propiedades estructurales propias (microcontroladores con firmware · teléfonos con sensores · banco de pruebas con motor real) coexisten bajo arquitectura común que las hace operacionalmente intercambiables: la distinción `puerto ≠ canal` documentada en `§3.2` (ORC + EnlacesPanel) · los cuatro modos del RouterService documentados en `§2.3` y `§3.3` · la cadena de transducción de cuatro capas. P12 es **postulado transversal**: refina P1 (perceptores físicos del bucle), P5 (sustrato común a todos los realms), P6 (bucle se cierra en mundo físico), P9 (continuidad simulación↔control real) y §2.3 (primitiva Actor) sin invalidarlos. La indistinguibilidad operativa que P12 articula **es propiedad descriptiva del código existente**, no promesa abstracta — la escribe el ORC cada vez que un instrumento lee de un slot Float64 sin saber si su productor es un ESP32, un teléfono o un motor real. P12 **no genera principio arquitectural nuevo** entre los diez consolidados ni añade a la cadena P10+P11 del par fundacional del diseñador — articula propiedad estructural del sustrato que los diez principios dan por supuesta. La articulación canónica complementaria *"el sistema LRU es un laboratorio real/virtual de señales"* (formulación s117 de Manuel) condensa esta propiedad en una sola fórmula operativa.

---

## 9 · Gaps arquitecturales abiertos

Esta sección lista gaps conocidos entre arquitectura y realidad, y gaps entre visión fundacional e implementación. No se desarrollan aquí — se nombran con puntero a `LRU_MAPA` donde viven como tensiones abiertas con su estado y decisión probable cuando la hay. Un gap puede ser oportunidad de expansión, inconsistencia a cerrar, o ausencia articulada que el proyecto reconoce sin comprometerse a resolver.

### 9.1 · Viewer externo de Debug1 — el gap mayor

Este gap merece subsección propia. Es la pieza mayor entre visión fundacional e implementación que el proyecto arrastra desde marzo 2026, y conecta con la dimensión formativa declarada en `LRU_MISION`.

**Qué prometía la visión fundacional** (Debug1 documentación marzo 2026): *"pantalla externa · filtros · timeline · diff · replay"*. El Debug1 como herramienta que permite a un observador — instructor, diagnosticador, experto remoto — ver una sesión desde fuera, con navegación temporal completa, comparación entre momentos, y reproducción flexible.

**Qué existe hoy.** Piezas dispersas operativas pero no unificadas: `SimSequenceRecorder` y `SimSequencePlayer` con funcionalidad básica de grabación y reproducción de valores, `DebugBus` para eventos de diagnóstico, RabbitMQ Streams en la infraestructura pero sin consumidor que lo use como FDR, visión parcial en `Debug1` página y `Debug1Overlay`. No hay una herramienta única que permita al observador externo hacer lo que la visión prometía.

**Qué modelo conceptual ya tiene articulado.** `LRU_FUNDAMENTOS` §5 articula la **sesión multipistas como objeto temporal de primera clase**: ocho tipos de pista (data, protocol, event, effect, annotation, media, actor, derived), cuatro modos de replay (REPLAY_SIM, REPLAY_COMMS, REPLAY_ANALYSIS, REPLAY_MIXED), navegación temporal completa (pausa, retroceso, velocidad variable, salto a marker, loop sobre región, scrubbing, sincronización a evento). Almacén natural: RabbitMQ Streams.

**Qué arquitectura desbloqueó s56.** Las 4 restricciones del schema Realm v2 (ver §7.3) garantizan que el viewer externo puede construirse sin refactor arquitectural: el schema es JSON-serializable, ScenarioDef y FaultDef son inspeccionables, `emitOnBus` es declarativo, y la atribución por `authorId` no está restringida. **El viewer está desbloqueado arquitecturalmente** — solo falta materializarlo.

**Por qué importa más que otros gaps.** La dimensión formativa declarada en `LRU_MISION` necesita al viewer: el debriefing post-sesión, la evaluación de reacciones del alumno contra expectativas del escenario, la comparación entre intentos, la reproducción mixta que usa vuelos reales como backing track — todo eso pasa por el viewer. Sin él, el proyecto no cumple la promesa central aunque el Realm sea perfecto.

**Estrategia sugerida para materializarlo.** Fragmentable en sub-sesiones: grabación básica funcional usando RabbitMQ Streams, reproducción con navegación temporal, pistas derivadas, replay mezclado. El `lru_debug1_analysis_s55.md` tiene 10 preguntas abiertas para sesión dedicada con código que guían la implementación.

Registrado en `LRU_MAPA` como tensión arquitectural de alta prioridad.

### 9.2 · Otros gaps arquitecturales abiertos

Listados breves con puntero a `LRU_MAPA` para detalle y estado:

**Motor de vistas unificado.** `LRU_FUNDAMENTOS` §3.4 articula la Vista de Actor como primitiva operativa y el motor de vistas como horizonte implícito. Hoy Sim1, Col4, Debug1 y EnlacesPanel componen las primitivas a mano, cada uno con su lógica. Funciona pero está por debajo de la abstracción que el proyecto ya pide. Cuando haya más tipos de actores y más vistas guardadas, el sistema será inmanejable sin motor unificado. No es trabajo para una sesión concreta — es dirección natural.

**Herramienta de inspección del tiempo.** La tabla de introspecciones de `LRU_FUNDAMENTOS` §7 articula que cada primitiva merece herramienta propia, y que **el tiempo no tiene herramienta dedicada todavía**. Hoy se inspecciona indirectamente (DataPlane en vivo, SimSequencePlayer en pasado, EscenariosContext en futuro programado) pero sin interfaz unificada. El viewer externo de Debug1 probablemente será esa herramienta cuando se materialice.

**RabbitMQ como gateway multi-protocolo no articulado.** Hallazgo 14 síntesis s55: RabbitMQ ya traduce simultáneamente entre MQTT, AMQP, STOMP y Streams en producción, pero ningún documento arquitectural lo reconoce formalmente como gateway. Esto tiene implicaciones concretas para los ProtocolAdapters futuros del Analyzer1 (pueden publicar y consumir en los mismos exchanges) y para el uso de Streams como FDR natural. Merece articulación explícita.

**Fases 5 y 6 del Analyzer1 ausentes del roadmap.** Hallazgo 16 síntesis s55: el documento fundacional del Analyzer (`lru_analyzer_protocol_platform.html`, huérfano útil mayor) articulaba seis fases, de las cuales las dos últimas — **error injection** (corrupción de bits, CRC inválido, timeouts, SSM forzado) y **exercises** (generador de ejercicios, evaluación, certificación) — están **no implementadas y no en tech debt**. Son precisamente la dimensión formativa del Analyzer. Recuperarlas como roadmap formativo es pendiente.

**Métodos `validate` y `getOsiMapping` de ProtocolAdapter.** Hallazgo 17 síntesis s55: la interface temprana boceteada en `lru_analyzer_protocol_platform.html` declara seis métodos, pero dos de ellos — `validate(buffer)` para validación CRC/paridad/rangos, y `getOsiMapping()` para mapeo de capas OSI por protocolo — no tienen implementación conocida post-s18. Sin ellos, la fase 3 (encapsulación OSI) no puede funcionar. Conectan con el schema Realm v2: cuando el Realm declare `ProtocolEncoding` completo, `validate()` y `getOsiMapping()` pueden materializarse desde los datos declarativos.

**Dimensión formativa no articulada como principio arquitectural.** Los diez principios consolidados en s53 son todos técnicos. La dimensión formativa, origen fundacional del proyecto según `LRU_MISION`, no aparece en esta lista como principio explícito. Si una sesión futura decide añadirla como principio 11, será aportación sustancial al núcleo.

### 9.3 · Tensiones arquitecturales heredadas, abiertas

Listadas también en `LRU_MAPA` con su estado detallado. Se nombran aquí para reconocimiento, sin intentar resolverlas en este documento:

* **DebugBus con dos listas de canales documentadas.** 10 canales según `lru_architecture_consolidated_s53.html` y 11 canales según `LRU_PROJECT_REFERENCE_v5_1`. Reconciliación pendiente.
* **Clip de debug con dos nombres.** `Det2` en cabina_01 y `Debug1` en pfd_01. Misma función, nomenclatura distinta. Unificar antes de renombrar nada en código React/TS.
* **Estado desconocido de infraestructura Realm v1.** Los ficheros `RealmLoader.ts`, `query.ts`, `crossFunctions.ts` creados en s52 con el schema v1 obsoleto — auditar si persisten, si son reutilizables en v2, o si son candidatos a eliminación.
* **Enlaces rotos `/doc/doc/` en `lru_architecture_consolidated_s53.html`.** Fix trivial pendiente cuando ese documento se archive tras la producción de este (ver handoff s57→s58 §4.2).

### 9.4 · Ausencias articuladas que el proyecto reconoce

No son gaps a cerrar — son reconocimientos honestos de lo que el proyecto **no hace** por decisión:

* **El Realm no ejecuta** — declara fórmulas, el motor las ejecuta. Ver §7.6.
* **No hay testing dedicado ni disciplina Playwright articulada** — `LRU_METODO` §10 lo reconoce como pendiente; mientras no se trabaje con contexto suficiente, no se articula.
* **No hay deploy a producción más allá de ZIP+PowerShell local** — mismo tratamiento.
* **No hay motor de vistas unificado** — ver 9.2. Horizonte, no gap urgente.

---

## 10 · Estado del tech debt al cierre

Este documento no reproduce el tech debt — vive en su propio canónico acumulativo. Al cierre de s78 el documento vigente es `LRU_TECH_DEBT_s78.md`. El contenido de fondo de los 22 items heredados sigue viviendo en `LRU_TECH_DEBT_s56.md`.

Resumen al último cierre (s78 · segunda sesión de código de T-S67-A1 · Bloque 1 del triaje s75 CERRADO):

| Categoría | Abiertos | Nuevos s78 | Cerrados s78 |
|---|---:|---:|---:|
| Crítico | 1 (C2 · **SUSPENDIDA** tras s67 — ver §10.1) | 0 | 0 |
| Arquitectural | 2 (baja de 3 por cierre completo de T-AU72-2) | 0 | 1 completa + 2 por materialización (T-S60-O6, T-S63-renombres) |
| Optimización | 5 | 0 | 0 |
| UX | 1 | 0 | 0 |
| Cleanup | 10 (sin cambios · T-S77-C1 sigue abierta) | 0 | 0 |
| **Total ingeniería** | **19 + 1 suspendido = 20** | **0** | **1 completa + 2 materializadas** |

### 10.1 · Novedad mayor de s78 — Bloque 1 del triaje s75 CERRADO · segunda aplicación del ciclo destilado → código

La sesión **s78** cerró el **Bloque 1 del triaje s75 por completo** materializando los items 2 y 3 (FaultDef sensor/actuator + CruisePatternDef) en `src/realm/{types,validators,index}.ts`, tras los items 1 y 4 materializados en s77. **Primera vez desde s56** que el schema v2 tiene todas las decisiones arquitecturales articuladas entre s63 y s65 aplicadas empíricamente. Segunda sesión consecutiva con código — el ciclo destilado → código consolida como patrón replicable (2 instancias).

**Cambios concretos al schema Realm v2 en s78**:
- `interface FaultDef` plana antigua **retirada sin compat shim** (decisión explícita de Manuel: sin consumidores reales).
- `FaultDefBase` común + `SensorFaultDef` como intersección con `{targets: string[]}` y unión discriminada de **16 kinds** (`stuck` · `ramp` · `drift` · `bias` · `gain` · `noise` · `spike` · `oscillate` · `erratic` · `delay` · `dropout` · `quantization` · `hysteresis` · `hard-over` · `dead` · `custom`) + `ActuatorFaultDef` con **6 kinds** (`lock-in-place` · `float` · `loss-of-effectiveness` · `hard-over` · `runaway` · `custom`) + `AnyFaultDef` unión.
- `RealmManifest.faults?` desdoblado en `sensorFaults?: SensorFaultDef[]` + `actuatorFaults?: ActuatorFaultDef[]`.
- `CruisePatternDef` como unión discriminada de 3 types (`constant` · `sine` con `amplitude`/`periodSeconds`/`baseline?`/`wrapAt?` · `linear` con `start`/`ratePerSecond`/`wrapAt?`), con `cyclic` absorbido en `sine+wrapAt?`.
- `EntityDef.pattern?: CruisePatternDef` añadido.
- `RealmManifestSchema.superRefine` reforzado con unicidad cruzada de ids (no puede haber un id simultáneamente en sensorFaults y actuatorFaults), unicidad intra-registro, validación de `targets` de sensorFaults contra `allSensorIds`, y ausencia deliberada de validación cruzada de `targets` de actuatorFaults con comentario apuntando a T-S65-N3.

**Tensiones cerradas en s78**: T-AU72-2 (completa — apply-profile en s77 + FaultDef + CruisePattern en s78), T-S60-O6 (por materialización — cerrada arquitecturalmente desde s65), T-S63-renombres (por materialización — `freeze→stuck`, `oscillate` promocionado, `cyclic→sine+wrapAt`, sufijos `Seconds`).

**Tensiones abiertas sin cambio (schema listo · motor no)**: T-S65-N1 (motor implementa 3/22 kinds), T-S65-N2 (bias vs gain en SimService sigue confundido), T-S65-N3 (ActuatorDef pendiente para v3), T-S65-N4 (3 faults genéricos como herramientas UI), T-S65-N5 (absorción cyclic sin fricción observada), T-S77-C1 (diferida por instrucción explícita).

**Tensión T-S67-A2 (C2 fase A suspendida)**: sin cambios. Se desbloquea cuando T-S67-A1 cierre.

**Ruta prevista tras s78**:
1. s79 — Bloque 2 del triaje s75 (refactor del motor `SimService.applyFault`) usando `applyScenarioOverride` como plantilla empírica (T-AU73-8 hallazgo positivo). Cierra T-S65-N1, T-S65-N2, T-AU73-6, T-AU73-7 de un golpe si el alcance lo permite.
2. s80+ — Bloque 3 (decisión arquitectural quantities.ts), Bloque 4 (tipos nuevos de v3 consumiendo marco fundacional), Bloque 5 (cleanup oportunístico).
3. Cierre de T-S67-A1 · desbloqueo de T-S67-A2 · ejecución de C2 fase A sobre Realm v3.

### 10.2 · Decisiones arquitecturales firmes acumuladas — 6 en destilados · 5 materializadas en código

Las 9 decisiones firmes acumuladas tras s65 se preservan; s77 trasladó 3 de ellas de "decisión en destilado" a "materialización en código", y **s78 traslada 2 más**:

1. Formato de thresholds canónico `{w, c, wLow, cLow}` con tuplas.
2. Flight phases emergen de scenarios, no son entidad primera.
3. Separación `units.ts` ↔ `quantities.ts`.
4. Cross-display estricto.
5. Formula evaluator: 6 primitivos + registry.
6. [s64 · **MATERIALIZADA s77**] ScenarioEvent unión discriminada + `apply-profile` + `initialState` + `durSeconds?` + validación monotónica.
7. [s65 · **MATERIALIZADA s78**] FaultDef sensor/actuator separados, 22 kinds totales (16 sensor + 6 actuator), `FaultDefBase` común con `extras?` para extensibilidad, `RealmManifest.faults?` desdoblado en `sensorFaults?`/`actuatorFaults?`.
8. [s65 · **MATERIALIZADA s78**] CruisePatternDef como `EntityDef.pattern?`, 3 types discriminados (constant/sine/linear), `cyclic` absorbido en `sine+wrapAt?`.
9. [s65 · **MATERIALIZADA s77 + s78**] Renombres menores completos: sufijos `Seconds` aplicados en s77 a `apply-profile`; `freeze→stuck`, `oscillate` promocionado a kind de primera clase, `cyclic→sine+wrapAt`, sufijos `periodSeconds`/`ratePerSecond`/`durationSeconds` aplicados en s78 al materializar FaultDef + CruisePattern.

### 10.3 · HardwareProfile reubicado bajo el nuevo marco

La decisión pendiente `HardwareProfile extends RealmProfile` que se arrastraba desde s63 como "última decisión previa a C2 fase A, ~45-60 min" **cambia de escala** bajo los postulados de s67:

* **Antes**: "absorber los stubs `HardwareCapability`/`BindingContract` de `quantities.ts`".
* **Ahora**: "declarar la proyección hardware del bucle (sensores físicos, actuadores físicos, protocolos de acoplamiento) bajo Postulado 1". Espacio de decisión ampliado.
* **Estimación de 45-60 min obsoleta.** La decisión se abordará dentro del rediseño del Realm v3 (T-S67-A1), Bloque 4 del triaje s75.

### 10.4 · Tensiones reubicadas sin cerrar

| ID previa | Cambio bajo el nuevo marco s67 |
|---|---|
| **T-S65-N3** (falta `ActuatorDef`) | Deja de ser "deuda consciente futura", se vuelve pieza central del Bloque 4 de T-S67-A1 |
| **T-S65-N1** (SimService 6/21 kinds, refinada empíricamente a 3/33 por T-AU73-6) | Resolución subordinada al Bloque 2 de T-S67-A1 — refactor del motor con plantilla probada `applyScenarioOverride` (T-AU73-8 positiva) |
| **T-S62-A4** (HardwareProfile pendiente) | Ver §10.3 arriba |
| **O2 Debug1 viewer externo** (§9.1) | Alineado con Postulado 2 como herramienta del observador externo consumiendo guías estructuradas. Gana contexto arquitectural; prioridad sin cambio |

### 10.5 · Tensiones nuevas articuladas en s78

**Ninguna**. s78 ejecutó el destilado s65 literalmente con las tres decisiones de política aprobadas explícitamente por Manuel (ruptura limpia de `FaultDef` sin compat shim, T-S77-C1 diferida por instrucción explícita, tres ajustes técnicos fine-grained aprobados individualmente). Los tres hallazgos técnicos internos (`FaultDefBaseShape` como spread por limitación de Zod 4 con uniones discriminadas, asimetría de validación de `targets`, unicidad cruzada de ids) se resolvieron en la misma sesión sin articular tensiones nuevas.

**T-S77-C1** sigue abierta desde s77 — ahora con scope ampliado: materializable también para `spike`/`ramp` de sensorFaults con `durationSeconds`. Coste sigue siendo bajo (~10-15 líneas en `superRefine`).

### 10.6 · Tensiones arquitecturales abiertas relevantes

Detalle en `LRU_MAPA` §4.2: T-S63-1 a T-S63-7/9/10 (8 de baja/mínima), T-S65-N1 a N5 (con N1 y N3 reubicadas bajo T-S67-A1 · el schema de s78 deja a N1 y N2 como brecha schema↔motor explícita), T-S67-F1 a F5 **cerradas en s68-s70**, T-S67-A1 + A2 activas (A1 en ejecución con Bloque 1 del triaje s75 **CERRADO** · A2 suspendida), T-S77-C1 abierta. **T-AU72-2 cerrada completa en s78**.

Los items metodológicos arrastrados (Firebase, testing, deploy producción, inicios con otro Claude) están registrados en `LRU_METODO` §10 como pendientes del método, no en el tech debt de ingeniería.

Detalle completo y evolución en `LRU_TECH_DEBT_s{N}.md` — consultar la versión vigente al arrancar cualquier sesión.

---

## 11 · Cómo se actualiza este documento

Siguiendo `LRU_METODO` §1 regla de actualización del núcleo — se sobreescribe cuando hay **aportación sustancial**:

* **Cambio de código que modifica cómo se entiende el proyecto.** Ejemplo: cuando C2 se complete y SimService consuma Realm en lugar de `sim-catalog`, §2.4, §6.4 y §7 se actualizan.
* **Articulación conceptual que aporta vocabulario nuevo a la arquitectura.** Ejemplo: si una sesión futura articula el motor de vistas como pieza concreta con ficheros, §3 y §6 se amplían.
* **Nuevo principio arquitectural consolidado** que el sistema ya sigue. Ejemplo: si se articula formalmente el principio 11 de dimensión formativa, §8 se amplía.

**No se actualiza por:**

* Detalles de sesión concreta (eso va en handoff).
* Cambios pequeños que no modifican comprensión (tamaños de fichero que crecen 10%, renombrados internos).
* Tensiones que se resuelven — esas van al MAPA con registro histórico; la aquí se afirma el estado nuevo sin historial.

---

*LRU Platform · Arquitectura del sistema · Documento vivo del núcleo · s58 · sincronizado s61 · sincronizado s63 · sincronizado s64 · sincronizado s65 · sincronizado s67 · sincronizado s77 · sincronizado s78 · 2026-04-22 · **anotación s91** (§8 ampliado con nota complementaria sobre Postulado 10 integrado en `LRU_FUNDAMENTOS §11quater` · P10 amplía pero no absorbe el principio 11 implícito · cerró T-S84-F1) · **anotación s99** (§8 ampliado con nota complementaria sobre Postulado 11 integrado en `LRU_FUNDAMENTOS §11quinquies` · P11 refuerza principio 1 agnosia al dominio en su dimensión estructural fina · cadena P5→§11ter→P11 · cerró T-S97-F1) · 2026-04-26 · Manuel & Claude Opus 4.7*
