# LRU · Tensiones abiertas

**Documento vivo del anillo 1.B.1 · sin sufijo de sesión · cargado por Project al arrancar · accesible vía MCP filesystem**

> **Reestructurado en s201** (ronda de poda · etapas 1+2). Antes era un *log por sesión* (76 bloques · 157 filas · 106 IDs entrelazados): el estado real de cada tensión había que deducirlo de su bloque más reciente. Ahora es un **registro deduplicado**: una fila por tensión viva (§1) + apéndice de antiguas pendientes de triaje (§2). Las 18 cerradas se archivaron en `lru_tensiones_resueltas_archivo_s201.md`. El **historial por sesión** (los 76 bloques) vive en el **git de este fichero** (commits hasta s201) — no se pierde; sale del documento vivo para que su nombre sea honesto.

> Origen: externalizado desde §4.2 del MAPA en s129 (palanca M · `lru_externalizacion_navegacional_s128.md`). Archivos de resueltas: `lru_tensiones_resueltas_archivo_s128.md` + `_s129.md` + `_s201.md`.

> **Disciplina de mantenimiento (s201)**: tensión nueva → fila nueva en §1. Tensión que cambia → se **edita su fila in-place** (no se añade bloque por sesión). Tensión que cierra → marca + se archiva en la próxima ronda. El detalle por sesión vive en los handoffs y en el git. Pipes internos se escapan `\|`.

---

## §1 · Tensiones vivas (42 · s232: R0+R2 de T-S230-RESILIENCIA-COMS ejecutadas — cutover del broker hecho)

> Una fila por tensión. Bajo la umbrella **T-S183-MODELO-UNIFICADO-SENALES** cuelgan las del hilo de señales; el resto es deuda/UX/doc suelta. Estado = última anotación conocida; puntero = handoff/alfiler con el detalle.

| ID | Articulada | Tensión · estado | Puntero |
|---|---|---|---|
| **T-S230-RESILIENCIA-COMS** | s230 | **AVANZA FUERTE · umbrella del eje resiliencia · alfiler `lru_alfiler_resiliencia_comunicaciones_s230.md` (vivo · actualizado s231+s232)** · **R0 ✔ s231** (auditoría fina · hallazgos H1-H6 en §1bis: no-failback ambas clases · GATT clásico muerto por persistencia · PWA endpoint único reintento 1 s · foto real de brokers · D-abierta-6 degradada a teórica por sonda S2) · **R2 ✔ CONSUMADA s232** (cutover ejecutado: el broker de producción es la `.102` RabbitMQ 4.2 · 3 reglas NAT re-apuntadas · TLS 15676 nativo D-s232-A · anónimo→lru_app D-s232-B · doble ciclo OTA verificado · la `.200` de respaldo real → **L1/L2 separadas por fin** · plan `lru_plan_migracion_broker_s231.md` EJECUTADO) · **R1 con input de diseño s232** (cascada 15676→443/mqtt · criterio 80/443 de Manuel) · quedan R1/R3-R6 con sus triggers (P11) · hilo hermano seguridad SIN abrir con 4 motivos acumulados · postulado destilado de Manuel s230: **«ningún fallo único de comunicaciones debe suponer una situación crítica»** + corolario BLE junto al hardware. Inventario s230: gran parte del sustrato YA existe (doble broker en AMBAS clases — mgos alterna mqtt/mqtt1 nativo · S3 `s_on_backup` tras 5 fallos s224 — · BLE GATT real en el clásico · AP de emergencia GPIO 27 · salud OTA multi-broker s230), pero falta doctrina: la **PWA no tiene failover de endpoint** (hueco gordo — si su broker cae se queda ciega con la flota sana en el backup), el BLE no tiene camino operativo, y nadie *cuenta* en qué capa opera el sistema. Taxonomía F1-F5 × escalera L0-L4 + rebanadas R0-R6 con triggers (R0 auditoría fina · R1 failover PWA · R2 broker local formalizado · R3 telemetría de degradación · R4 BLE operativo · R5 failback · R6 ensayo de fallos institucionalizado — el patrón de la prueba negativa s230). 5 D-abiertas. Hilo hermano **seguridad/autenticación** nombrado por Manuel s230 — merecerá su propio alfiler. Dialoga con TD-s228-1 (ancla TLS fechada 2027-11 = fallo single-point predecible), TD-s209-1, T-S220-FLAG-DISPONIBILIDAD (motor de vitalidad s221 como sustrato de R1/R3). | `lru_alfiler_resiliencia_comunicaciones_s230.md` + `esp_mqtt_port.cpp` (`MQTT_URI_BACKUP`/`s_on_backup`) + `mos.yml` (mqtt/mqtt1 · wifi.ap) |
| **T-S225-CAPABILITIES-S3** | s225 | **NUEVA · baja-media · contrato · trigger doble ya materializado** · la S3 no publica `from/capabilities`: sus canales se auto-registran por `from/events` con rangos INFERIDOS por nombre en la PWA — y en s225 fue la **2ª vez** que un canal nuevo exigió conocimiento hardcodeado en `registrySensorUtils` (virt1/virt2 a `PHYSICAL_RANGES [0,100]`; síntoma previo: aguja recorría ~2.4% sobre el fallback 0..4095; 1ª instancia latente: ax/az reales de la S3 son ±4g pero el default PWA es ±1.5). La vía de contrato es que **el device declare sus rangos** (`from/capabilities`, como Mus1 y el clásico) — sin hardcode por nombre. **Incluye dos huecos del lado PWA**: (a) el handler de capabilities DESCARTA sin almacenar las caps que llegan antes del primer events (el warn dice «se aplicarán cuando llegue events» pero no hay stash) — con caps retained el orden de llegada es indeterminado; (b) el cambio de partitura en caliente llega a medias (TD-s207-1 · register primero-gana). Rebanada candidata: pub_capabilities retained en la S3 (tabla CH_NAMES+CH_RANGE ya existe) + stash o re-apply en el handler + rebind en cambio de rango. NO bloqueante (el hardcode funciona). | `src/datosLru01/registryMqttHandlers.ts` (createCapabilitiesHandler · hueco caps-antes-de-events) + `src/datosLru01/registrySensorUtils.ts` (PHYSICAL_RANGES) + `C:\esp32\01_factory\components\esp_port\esp_mqtt_port.cpp` (CH_NAMES/CH_RANGE) + TD-s207-1 |
| **T-S219-ESTADO-INICIAL-SW-FRIO** | s219 | **CERRADA s220 · caso ONLINE resuelto (readback `to/actstate` + `sembrarActuador` validados on-device · siembra comando act-rx + #fb act-fw) · caso OFFLINE trasladado a T-S220 (eje flag · estado retenido)** · [hist · sintoma confirmado por Manuel s219]** · al **recargar una simulación en frío**, los SW **no reflejan el estado real del hardware** — muestran su frame por defecto (0) en vez del estado físico del actuador. **Hipótesis (pendiente de auditoría Patrón 6)**: el `#fb` que lee el SW (R-D s217) es un canal **dirigido por eventos**, no un estado-actual consultable — se escribe cuando *fluye* una orden (F1 optimista o F3 del firmware), y vive en el **DataPlane (memoria por-runtime)**. En el arranque en frío: el DataPlane nace vacío → el binding del SW lee `#fb` inexistente → cae al defecto. La verdad real está en **el firmware** (los latches GPIO de RF-3) y parcialmente en el **minilog** (`from/actlog`), pero el firmware publica `from/act` en **QoS 0 sin retain** → un suscriptor nuevo no recibe el último estado. Nadie *pregunta* al firmware su estado al montar, y `#fb` no se siembra. **Espacio de solución** (a arbitrar en s220, tras auditar cómo se inicializa hoy el binding en IB_04 y si hay alguna vía HwSim que ya siembre `#fb`): (1) **readback de estado explícito** — `to/actstate` nuevo que lea los latches GPIO actuales del firmware y siembre los `#fb` al montar un panel físico (simétrico al minilog · molde request/reply `sendActlog`/`sendQuery` ya existe) — *instinto preferido*; (2) **derivar del minilog** RF-4/RF-5 (última orden por puerto ≈ estado en digital · reutiliza lo construido pero frágil: el anillo se vacía tras reboot del MCU o evicta tras 16); (3) **`from/act` con retain** (un suscriptor nuevo recibe el último estado · ensucia el contrato por-orden). Es el cierre natural del lazo: RF-3 dio el músculo (escribir el pin) pero falta el **readback de arranque** (leer el pin para sembrar la UI). Más concreto y urgente que `applyActuatorFault`. | `src/datosLru01/InstrumentBridge_04.ts` (binding SW/`#fb`) + `src/datosLru01/registryMqttHandlers.ts` (siembra `#fb`) + firmware `…/rpc1cl/src/main.c` (readback GPIO) + `lru_eje_realimentacion_actuacion_s216.md` |
| **T-S220-FLAG-DISPONIBILIDAD** | s220 | **AVANZA s221 · umbrella del eje availability/flag/BITE · destilado `lru_eje_confianza_estado_actuacion_s220.md` (anillo 2 · regla de disputa activa)** · el **estado de una salida** = valor + procedencia + frescura + salud + validez, sobre un **núcleo neutro** del que la aviónica (**Flag/BITE/CMS**) y la industria (**OPC/SCADA**) son **proyecciones** (reencuadre arbitrado por Manuel · «anadir lo mejor de muchos mundos, reales o virtuales»). **Primer consumidor hallado on-device (s220)**: el LED online de la **animacion** (`ControlOnline1` en Cuadro01/02) no colorea online/offline — nunca cableado a disponibilidad: (a) `isOnline` vive en el ORC, no como senal bindable en el DataPlane; (b) IB_04 solo tiene bindings de **aguja** y **switch**, no de **indicador**. El LED del LruBar si va (lee `isOnline` directo). **Rebanada flag (s221)**: (a) disponibilidad-como-senal (espejar `isOnline` -> canal `{hwObjId}/#online` dir:in · cablear tambien `from/status` retenido del firmware, que hoy NO toca `isOnline`); (b) binding `indicator` en IB_04 (clip N frames via `gotoAndStop` · fuera del hot path); (c) **estado retenido** del firmware (cierra el caso OFFLINE de T-S219 · tagueado ULTIMO-CONOCIDO/uncertain); (d) autoria manifest (Manuel). **Escala procedencia->confianza**: medida (feedbackSensorId s79) / latch-vivo (to/actstate) / ultimo-conocido (retain) / comandado (F1) / desconocido. **Validez != salud** (flag != BITE · `from/health` ya es BITE-embrion). **Frontera: BITE corto plazo, CMS largo plazo VALLADO** (correlacion/root-cause via patch bay · no abrir sin trigger · registrar el porque es la barrera). Forks: 2o dominio = BITE (arbitrado) · un registro inyectado+real · calidad canal/dato (lean canal · promover `lastWriter` a enum de procedencia). **s221 · AVANZA (rebanada flag materializada · sin validar on-device aun)**: disponibilidad como **SENAL CONTINUA de vitalidad 0..1** (`disponibilidadCanal.ts` nuevo · pump 1.0 por latido + decay `v=1-elapsed/ventana` por ticker TICK_MS=150 · **ventana ADAPTATIVA** `clamp(K*intervalEst,MIN,MAX)` con `intervalEst`=EWMA(0.3) del gap inter-latido · K=3/MIN=8s/MAX=70s/DEFAULT=30s · gaps>90s ignorados como reconexion) en vez de booleano del ORC -> el clip pinta Online/Rancio/Ofline por umbral (verde>=0.66 / ambar 0.33-0.66 / rojo<0.33) + latido por frescura. **Binding de indicador** en IB_04 (rail b) + indicador **`follows`** que sigue la mesa (re-apunta a `{hwObjId}/#online` al enlazar, a -2 al desenlazar) vs **`source` estatico** (Cuadro01/02 · device fijo) · el tick lee **CRUDO** en la rama indicador (sin clamp/epsilon) para no fundir -2 Virtual y 0 Ofline en pct=0. **Sentinelas**: VIT_VIRTUAL_LOCAL=-2 / _REMOTE=-3 (reservado) / VIRTUAL=-1 + siembra Virtual local en `enableEmission`. **3 commits** (d2854951 a1 liveness+retain · 2c0c9917 a2-continua+b+vitalidad · f36ca800 C+D · build 2216 + 463/463 + tsc). **Pendiente**: validacion on-device (Manuel aplica manifests `follows` en AirSpeed1/VertSpeed1/Alt1 + rama virtual de `change1` del clip `Info1_Online`); **Virtual remoto -3** necesita tag `isFromSim` (sim remoto llega como fisico por MQTT, no separable hoy). El caso OFFLINE de T-S219 lo cubre ahora la vitalidad continua (decay -> rojo) + el estado retenido pendiente. **Frente actuador-en-mesa** desgajado a T-S221-ACTUADOR-EN-MESA. | `lru_eje_confianza_estado_actuacion_s220.md` + `src/simulation/InstrumentBridge_04.ts` (binding indicator) + `src/datosLru01/registryMqttHandlers.ts` (isOnline <- from/status) + firmware `from/status`/`from/actstate` retenido + animacion `Cuadro1Det.ib04` (manifest) |
| **T-S221-ACTUADOR-EN-MESA** | s221 | **CERRADA s222 · actuador-en-mesa end-to-end: visibilidad (from/events + dir:'out' + reconciliacion #fb · a0666325) + R1 filas (42aec212) + R2 celda direccional (01bc5269) + R3 parcheo dinamico switch->sdn MODELO B (47e90b2b · la mesa manda · auto-enlace a estaticos · '----'=sin salida) · VALIDADO on-device. La parte (c) «eximir exclusividad» = NO-OP (nada bloquea el N:1). El gate de modo destapo el frente hermano T-S222-ACTMODO (cerrado tambien s222).** · [hist s221:] **NUEVA · media · co-articulada con Manuel s221 · enabler concreto hallado (Patron 6)** · las SALIDAS (`sdn`) no son visibles ni parcheables: las listas de puertos enlazables salen de `hwObj.hw1` (`MatrixCell.tsx` L69 \| `ComponenteLru index.tsx` L704) y `hw1` se puebla SOLO con valores de `from/events` (sensores) — los `sdn` nunca viajan en `from/events` -> nunca en `hw1`. **Plan Manuel (validado)**: el firmware manda los `sdn` en el MISMO paquete `from/events` + registrarlos; un puerto de salida puede estar comandado por botones de varios swObj (**N:1**). La maquinaria YA fusiona puertos nuevos para un hwObj conocido (`actualizarhw1ToSw` crea la entrada \| `inferSensorType` mapea `sd*`->`tipo:'sd' bits:1` \| registra canal DataPlane \| sincroniza `cfg1`). **Hueco concreto (TD-s221-1)**: `buildCfg1Entry` (`registrySensorUtils.ts` L170-184) NO fija `dir` -> los `sdn` auto-registrados quedan `dir:undefined` -> la UI no los distingue de entradas; necesita `dir:'out'` para `sd`/`pwm`/`dac` (el camino de capabilities `buildCfg1FromCapability` si propaga `def.dir`, E6 s213; el de `from/events` no). **Tres reconciliaciones**: (a) **colision comando<->reportado** en `{hwObjId}/sdN` — el switch comanda (IB04Actuator) y LEE ese slot, `from/events` escribiria el REPORTADO del device; deseable (el switch mostraria el estado REAL · bucle cerrado) pero reconciliar con el eje **`#fb`** (s218 RF-2 · el reportado es la confirmacion) + comprobar que el gate per-senal del canal OUT deja entrar `mqtt-hw`; (b) **exclusividad** — la matriz impone «1 puerto, 1 canal» (sensor 1:1), las salidas son N:1 -> **eximir `dir:'out'`** de esa invariante; (c) **filtrado por direccion** en las listas (agujas -> `in/bidi` · switches -> `out/bidi`). **VER** (`sdn` en `hw1`) != **PARCHEAR** (switch->sdn dinamico por la mesa, analogo al `follows` de las agujas) — piezas separadas; el N:1 ya funciona via `actuator` estatico del manifest. Colateral s221: colision Cuadro01/Cuadro02 resuelta (dos simbolos distintos `lib.Cuadro01`/`lib.Cuadro02`). | `src/datosLru01/registrySensorUtils.ts` (`buildCfg1Entry` dir) + `src/datosLru01/registryMqttHandlers.ts` (`actualizarhw1ToSw`/auto-registro) + `src/components/EnlacesPanel/MatrixCell.tsx` (L69) + `src/components/ComponenteLru/index.tsx` (L704) + `src/simulation/enlacesActuador.ts` (exclusividad out) + firmware `from/events` (sdn) |
| **T-S222-FEEDBACK-VISUAL-SW** | s222 | **CERRADA s223 · feedback visual de switches end-to-end · VALIDADO en 3 entornos (app enlazado / app desenlazado / standalone)** · compositor A2 en el tick de IB_04 (`_composeSwitchState` · commit `4f3713a`) con **modelo OPC Good/Uncertain/Bad**: 5 estados (SW_STATE: indet -1 / off 0 / off_pend 0.25 / on_pend 0.75 / on 1) · discriminante F1/F3 = `#fb.lastWriter === ACT_FW_SOURCE` (comparar valores NO distingue: el eco F1 iguala al instante) · arbitrajes emergidos en validacion: **el hw manda** (Real/reconexion: direccion visual del `#fb`; re-comandar es del usuario) · **el modo Local arbitra la autoridad visual** (ensayo solo-sim: el comando local pleno, el hw se anota pero no se obedece) · **standalone** (sin IB04Actuator el clip degrada a toggle autonomo pleno) · regresion LruBar cazada y corregida (los sd comandan via actuar() = comando + eco F1 · camino unico de escritura de salidas) · Animate de Manuel: Sw5Det_sw1 5 frames+labels + frame_0 canonico con guard __swInit + 3 SVGs + fix test1 Cuadro01 (indice→label) · patron cristalizado en `lru_patron_simbolo_actuador_digital_s223.md` (simbolo) + `lru_panel_switches_cuadro01_s223.md` (contenedor) · [hist s222:] los switches deben reflejar enlace + confianza + indeterminado-gris con frames adicionales · spec `lru_feedback_visual_switches_s222.md` | `lru_patron_simbolo_actuador_digital_s223.md` + `lru_panel_switches_cuadro01_s223.md` + `src/simulation/InstrumentBridge_04.ts` (`_composeSwitchState`) + `src/datosLru01/ComponenteLru/index.tsx` |
| **T-S222-ACTMODO** | s222 | **CERRADA s222 · frente nuevo destapado al validar el modelo B (commit bdddfad7)** · el gate per-señal del ControlBus (arbitra que fuente ALIMENTA una lectura de ENTRADA) mudaba la escritura del switch a un canal actuador de SALIDA: `'switch'` (y `act-*`) no viven en `SIGNAL_PERMISSIONS`, asi que poner el `sdN` en Local/Real (LruBar) dejaba muda la orden del switch (Real tampoco admite `'switch'`), incluso tras revertir el countdown a Real. **Fix**: (1) `DataPlane.writeBySensorId` — un canal actuador (`dir:'out'/'bidi'`) NO se gatea por el eje modo (es COMANDADO; el modo gobierna el egress, no la escritura); solo exime SALIDAS. (2) `actuadorAdapter` — el egress `to/act` respeta el modo: **Local** no egresa (ensayo solo-sim; el SW de la sim/LruBar reflejan el canal por el DataPlane), **Real/mode-less** egresan al hw. Da «Local=solo sim, Real=hw». El countdown Local→Real ya estaba cableado. **Principio cristalizado**: el eje **actuacion** (salida, comandada) es ortogonal al eje **modo** (entrada, arbitrada); el modo solo gobierna el **egress** de una salida. VALIDADO on-device. | `src/simulation/DataPlane.ts` (`writeBySensorId` · exencion dir out/bidi) + `src/simulation/actuadorAdapter.ts` (egress respeta modo) + `src/simulation/ControlBus.ts` (`SIGNAL_PERMISSIONS`) |
| **T-S209-EGRESS-MESA** | s209 | **ESTADIFICADA s210 · media · alfiler `lru_alfiler_egress_mesa_s210.md` · E0-E3 + E4.1 CERRADAS · próxima E4.2 (UI de la arista) + E4.3 (persistencia · punto de no retorno)** · las señales deben poder viajar en las DOS direcciones por la mesa y los legos. Hoy: la mesa (EnlacesPanel) solo conecta agujas (consumidores) con señales; los **actuadores virtuales del músico** (s209: luces `led_*` 1-bit + `rgb1..3` canales 8-bit; vibrate como reflejo local cabeceo>30) son conducibles únicamente por la bidireccionalidad s206b de Mus1 (siguen el `from/events` entrante), **sin ruta por la mesa**. Alcance del frente, por capas: **(1)** contrato del adaptador de dos caras + seguridad del egress con **exclusividad atómica** como invariante (base YA articulada: destilado s183 §4+§7); **(2)** dirección en la partitura — `SensorCapabilityDef` no expresa sentido (toca core `datosLru01/types.ts` + paridad firmware `sensors_cfg.h`); **(3)** EnlacesPanel — la matriz es swObj×hwObj de entrada; el enlace señal→puerto-actuador es arista nueva (familia T-S182 señal→señal con dirección); **(4)** legos relacionados (LruBar/Mixer/Chart/HwObjPopup) — mostrar y operar puertos de salida; **(5)** eje de modos en egress — quién tiene derecho a escribir un actuador (espejo del gate per-señal de entrada). Triggers materializados que lo activan: T3 de T-S182 (s207 músico→músico) + luces s209 + vibrate s209 (reflejo local que pide su versión enrutada). Camino: alfiler materializado s210 — estadificación E0–E7 con punto de no retorno en E4 (arista persistida); arbitrado: D-s210-1 transporte `to/act` canónico (peer s206b = lazo de feedback), D-s210-2 andamiaje A provisional (se retira en E4), D-s210-3 paridad firmware dentro (rebanada E5). E0 (auditoría fina) ejecutada en s210 con 6 hallazgos incorporados (H1 HwSim placa-entera → TD-s210-1; H3 `sendActuatorCommand` s83 → D-abierta-5). E1 hecha (commit `78d4074`): `dir?` en `SensorCapabilityDef` + Mus1 declara `out` en las 12 luces. «Más adelante las desarrollaremos mucho más» (Manuel s209). **s212**: E4.1 (modelo `enlacesActuador` + `lastChannel` + adaptador, en memoria) cerrada · renombre egress->actuador (cristalizacion Manuel: el actuador convierte senales electricas en senales del mundo fisico) · proxima E4.2 (UI de la arista) + E4.3 (persistencia, punto de no retorno, clave «actuador»). **s217**: el **bloque app-only del eje de realimentación** (R-A→R-D) materializa la VUELTA que faltaba — F1 cross-instancia real en loopback (los SW sincronizan entre instancias · validado en vivo) · el lazo de la mesa (E4) y el de los SW comparten ahora el **sobre `to/act`** + el **canal de confirmación `#fb`**. La serie E (persistencia E4.3) sigue su curso; la realimentación es eje hermano ya vivo app-side. **s218**: la realimentación gana **firmware real** — F2/F3 del firmware (RF-1) cierran el lazo end-to-end (el `#fb` recibe la verdad del firmware, no solo el eco) + minilog `from/actlog` (RF-4 firmware / RF-5 pestaña Minilog en Hw1AdminPanel). El eje de la mesa (serie E) y el de los SW comparten ahora también la VUELTA con firmware. RF-3 (drive físico) es el siguiente paso. **s219**: RF-3 cerrado y **validado on-device** — el firmware conduce GPIO digital real (sd1..sd4 · pines 19/23/18/16 desde `mos.yml board.act.*`). El lazo entero está vivo con hardware: comando → `to/act` → firmware → pin físico → F3 latch real → `#fb`. La VUELTA ya no es solo eco ni solo F2/F3 tonto: F3 reporta el estado real del actuador. RF-3b (PWM pin 25 + RGB pin 26) queda como horizonte analógico sin abrir (P11). | `lru_alfiler_egress_mesa_s210.md` + `lru_destilado_modelo_unificado_senales_s183.md` §4+§7 + `LRU_HANDOFF_VIVO.md` (s210) |
| **T-S212-ACTUADOR-AUTORIZACION** | s212 | **NUEVA · horizonte · P11 · co-desarrollada con Manuel s212** · la **SALIDA** carece del análogo al ControlBus que gobierna la **ENTRADA**. Dualidad observada: **leer** una señal NO es destructivo → 1 señal → N agujas (fan-out libre, sin árbitro); **escribir** SÍ contiende → N orígenes pueden escribir UN puerto-actuador → necesita arbitraje. En ENTRADA el **ControlBus** es el árbitro per-señal (Real/Gen/Local/HwSim · «gana una»). En SALIDA no existe ese árbitro: el topic `to/act` es **sustrato abierto por diseño** (lo escriben el «Actuar» manual del HwObjPopup, otros `commsSend`, clientes MQTT externos); la arista de `enlacesActuador` (E4) solo limita el **auto-escritor continuo** (el adaptador, vía exclusividad estructural del puerto-out), NO quién más puede escribir el puerto. La **autorización** (quién/cuándo/bajo qué condiciones puede actuar un puerto) es un **nivel superior no definido**. Diferida (P11 · sin trigger de dolor); encaja con la rebanada **E7** del alfiler (modos en egress) y dialoga con T-S206-2. **s215**: 1er egreso real de SW por `to/act` — los switches escriben el puerto OUT **mode-less** (source `'switch'`), pasando el gate sin modo que casar; árbitro de salida arbitrado por Manuel como «no imprescindible» para el SW enclavado 0/1. El sustrato sigue abierto y el dolor empieza a ser tangible (egreso real sin autorización). **s216**: gana **documento de diseño** (`lru_eje_realimentacion_actuacion_s216.md`) + arbitraje **«firmware tonto ahora, autoridad después»** (el firmware ejecuta y reporta · F1+F2+F3 como realimentación pura · `auth` reservado **dormido** en el sobre). Espectro de dónde vive el árbitro articulado: (1) sin árbitro, (2) app — *bypasseable* (convención, no garantía), (3) firmware — único punto de verdad no bypasseable, (4) distribuido en el sobre. El modelo `auth` de Manuel (secreto rotable de supervisor · interlock permisivo) = **posición 4, que exige firmware listo** para activarse de verdad. Razón mecánica de que NO se difiera *indefinidamente*: F1 (estado compartido cross-instancia) fabrica el conflicto que antes no existía (las instancias no se veían) — aguanta para SW binario de 1 operador, se tensa con multi-operador o analógico (saltos de valor). Activar `auth` = abrir esta tensión como **sesión propia** cuando duela. **s217**: F1 (estado compartido cross-instancia) **ya es real** (R-C materializado y validado en vivo) — el conflicto que F1 fabrica deja de ser teórico; aguanta para SW binario de 1 operador (lo probado), se tensará con multi-operador o analógico. `auth` sigue **dormido** (no se emite · reservado en el sobre `{ver,port,val,meta}`). NO bloqueante. **s218**: el **firmware tonto ya existe** (RF-1 · `handler_mqtt_act` recibe `to/act`, parsea el sobre, ejecuta y reporta F2 `received` + F3 `confirmed`) → el arbitraje «firmware tonto ahora» de s216 está materializado. Lo diferido es la AUTORIDAD: `auth` sigue sin emitirse; «firmware listo» (validar antes de ejecutar · posición 4) es sesión propia «cuando duela». RF-3 (drive físico real) NO cambia esto — sigue sin árbitro de quién puede escribir el puerto. **s219**: RF-3 materializado — el firmware **ejecuta de verdad** (mueve GPIO real · validado on-device). Matiz del espectro: el firmware es ya **listo en ejecución** (mueve el pin) pero **tonto en autoridad** (no valida quién/cuándo antes de ejecutar · `auth` sigue sin emitirse). La posición 4 (autoridad no bypasseable en firmware) tiene ahora el músculo donde colgarse, pero el interlock sigue dormido. «Firmware listo» (validar antes de ejecutar) es sesión propia «cuando duela». | `lru_eje_realimentacion_actuacion_s216.md` (§7 Fork 2) + `lru_alfiler_egress_mesa_s210.md` (E7) + `src/simulation/enlacesActuador.ts` + `src/simulation/actuadorAdapter.ts` |
| **T-S213-CANAL-DIRECCION-SWOBJ** | s213 | **PARCIALMENTE SALDADA s215 (por otra vía) · horizonte · P11 · co-desarrollada con Manuel s213** · el **swObj deja de ser solo consumidor**: hoy declara agujas que leen señales de sensores (tipo **`in`**); pasará a declarar también canales **`out`** (salidas) y **`bidi`** (como varios de Mus1 — lee y escribe el mismo canal). Cristalización Manuel s213: «**swObj produce un valor propio que se publica como señal nueva en el DataPlane**» — el canal `out` es **emisor de primera clase**, no enruta señal ajena. Generaliza lo que YA existe: `SwObj.emitsAs` + `enableEmission` + `emit()` (Mus1 es ese caso). Consecuencia de modelo: **`dir` deja de ser solo faceta del puerto hwObj** (`SensorCapabilityDef`→`cfg1` vía E6) y pasa a ser **faceta del canal swObj** → `SwObj.canales` (hoy `string[]`) necesita dirección por canal. En la celda swObj×hwObj el mapeo empareja canal-con-dirección con puerto-con-dirección y la dirección **debe casar** (`out` swObj → `out`/`bidi` hwObj; `in` swObj ← `in`/`bidi` hwObj) → la columna `dir` adquiere **significado de validación**, no solo informativo. **ControlBus**: un canal `out` de swObj es escritor del DataPlane → entra por el arbitraje per-señal (s169) en **modo Gen**, NO es autorización nueva; la única salida sin árbitro sigue siendo la física `to/act` (T-S212-ACTUADOR-AUTORIZACION). **Modelo = dos saltos a través del DataPlane** (swObj.out→DataPlane vía `emitsAs`/Gen · señal→hwObj.out vía `enlacesActuador`); **fusionar en UI sí, en modelo no** (lección de 200 sesiones · el DataPlane desacopla productor y consumidor). Cierra la asimetría señalada en s213: `swObj.out → hwObj.out` SÍ tiene coordenada en la rejilla. Relación: E6 (`dir` en `cfg1`) · `emitsAs`/`emit` · **T-S182** (conversión señal→señal con unidades · familia hermana) · T-S209-EGRESS-MESA. **s215**: el caso de los switches (SW) se saldó **por otra vía** — el SW escribe el **canal del puerto OUT del hwObj** vía `dir:out` (escritura directa al DataPlane, source `'switch'`), NO una señal propia del swObj; el emisor de primera clase (`emitsAs`/`enableEmission`/`emit`) y la dirección en `SwObj.canales` **siguen sin construirse**. El modelo de «dos saltos por el DataPlane» NO es necesario cuando el swObj mapea directo a un puerto OUT del hwObj (el canal del puerto ES la verdad escribible compartida · da sincronía cross-panel); el emisor de primera clase queda para cuando un swObj deba publicar una señal NUEVA (no enrutar a un puerto existente). NO bloqueante (el caso directo ya en producción · el emisor sigue siendo teoría de modelo). **s217**: el **canal de confirmación `#fb` (`dir:'in'`)** del eje de realimentación (R-B) es el **1er uso real de dirección de ENTRADA en un puerto actuador** — distinto del canal de comando `dir:out` del mismo puerto; lo escribe el firmware (F3) o el eco optimista (F1). Refuerza que `dir` es faceta del canal con significado, no solo informativa. **s218**: el `#fb` recibe ya **F3 real del firmware** (RF-2 · `createFromActIngestHandler` escribe en `phase:'confirmed'` con source `act-fw`), no solo el eco optimista F1 — la dirección `dir:in` del puerto actuador tiene su **primer escritor hardware**. **s219**: con RF-3 ese F3 del firmware (source `act-fw`) lleva el **estado real del latch GPIO** (lo ejecutado), no el eco del comando — el `dir:in` del `#fb` recibe ahora la verdad física del actuador, validada on-device. Refuerza definitivamente que `dir` es faceta del canal con significado operativo real. | `LRU_HANDOFF_VIVO.md` (s213) + `src/datosLru01/types.ts` (`SwObj.canales`) + `lru_alfiler_egress_mesa_s210.md` |
| **T-S213-MESA-BANCO-DIAGNOSTICO** | s213 | **AVANZA · media · F1 del rediseño de la mesa · Rebanadas A+B CERRADAS s214** · la pantalla que se abre al pulsar las coordenadas `🔗 n/n` de la celda (editor portal de `MatrixCell`) se convierte en **banco de parcheo + diagnóstico**. **s214 · Rebanada A** (`7051c6a`): diagnóstico de solo-lectura por fila de mapeo — valor en vivo (cascada **puerto-primero**: canal `{hwObjId}/{puerto}` → señal upstream `signalRoute` → `hw1.puertoValor`) + marca de dirección desde `cfg1[puerto].dir` (E6 · `out` resaltado, `in` atenuado) + tag de modo per-señal (ControlBus) + marca de stale (`lastWriteAt`). Refresco ~5fps (force-update vanilla). **Cierra la mitad-UI de TD-s211-2** (la celda consume `cfg1.dir`). **s214 · Rebanada B** (`e697c1c`): inyector de test **cara-entrada** por fila con canal en el DataPlane — input de valor (default medio rango de `cfg1`) + botón de pulso + anillo `SignalCountdownRing`; mirror del Mixer (`handleChangeSliderhw1Sw`): fija modo **`Local`** + `startSignalCountdown` (10s · retorno a `Real`) + `writeBySensorId('slider')`. En `Local` el gate per-señal bloquea las otras fuentes → el valor se mantiene 10s. **Solo entrada** (escribe la señal del puerto, NO `to/act`). Validado runtime (aguja sigue el pulso · modo vira a `Local`). Fix intermedio (`5e8ca36`): reposición del editor por tamaño real medido (`useLayoutEffect`) — las filas ensanchadas recortaban por el borde derecho. **Pendiente**: **Rebanada C** (inyector cara-**salida** · pulso a `to/act` · NO mirror directo de B · requiere arbitrar antes la autorización T-S212 · el timer 10s = límite de seguridad interino) · **banco bidireccional** unificado en la celda (depende de T-S213-CANAL-DIRECCION-SWOBJ que dé coordenada a `swObj.out → hwObj.out`). Decisiones de diseño vigentes: lista única con marca de dirección (no pestañas) · entrada vía ControlBus (`Local`/Gen) / salida vía `to/act`. Se irá definiendo mejor a medida que se construyan simulaciones tipo Mus1 de sistemas reales. **s215**: el sustrato de egreso que la Rebanada C necesita **ya existe** (`dir:out` en `ChannelMeta` + `actuadorAdapter` 2º disparador + `window.IB04Actuator.actuar`) — la Rebanada C puede construirse sobre él (arbitrando antes T-S212). NO bloqueante. | `src/components/EnlacesPanel/MatrixCell.tsx` + `src/components/EnlacesPanel/hw/HwObjPopup.tsx` + `LRU_HANDOFF_VIVO.md` (s214) |
| **T-S214-BINDING-MANUAL-SIGNALROUTE** | s214 | **NUEVA · informativa · familia s169/s182 · destapada por la Rebanada A s214** · dos divergencias del binding/arbitraje que el diagnóstico de la celda hizo visibles: **(1)** `signalRoute.signalForPort` **solo se puebla en auto-enlace** (desde el manifest IB_04), NO en los enlaces creados a mano en la celda → en un puerto re-parcheado manualmente la aguja puede seguir leyendo la **señal upstream** y no el canal del puerto; el inyector de la Rebanada B lo revela en vivo (si la aguja no sigue al pulso del puerto, el binding apunta a otra señal). **(2)** al enlazar un puerto de músico el ORC **siembra el modo `Real`** (R3.1) y el gate per-señal (`SIGNAL_PERMISSIONS`) bloquea entonces la fuente del músico si no casa → la señal se congela (el «congelado hace Ns» que A anota). En `pc_1ngoi0/virt1` el binding resultó correcto (la aguja siguió al puerto), pero la divergencia es real cuando `signalRoute` apunta a otra señal o cuando modo↔fuente no casan. Dialoga con **TD-s206-2** (semilla `Real` de músicos dinámicos). NO bloqueante (diagnóstico hecho legible · sin código aún) · candidata a una sesión de binding/arbitraje. | `src/simulation/signalRoute.ts` (`signalForPort` solo auto-enlace) + `src/datosLru01/registryMqttHandlers.ts` (semilla Real) + `src/simulation/ControlBus.ts` (`SIGNAL_PERMISSIONS`) + `LRU_HANDOFF_VIVO.md` (s214) |
| **T-S213-VISORES-NO-ANIMATE** | s213 | **NUEVA · horizonte · P11 · co-desarrollada con Manuel s213 · F2+F3 (F3 depende de F2)** · instrumentos de simulación más allá del canvas de Adobe Animate. **F2** (visor HTML tipo Mus1 generalizado): un **lego visor** que representa sensores y actuadores múltiples, lee del DataPlane y también **emite/recibe** como Mus1 — como lego reutilizable, no como página suelta · integrable en el lego visor actual o uno nuevo · evolución natural de la línea Mus1 + el visor. **F3** (visor SVG): gráficos vectoriales generados por Claude (o complementados con otras herramientas), tercer tipo de instrumento además del canvas y el HTML — la plataforma-como-realm produciendo contenido de instrumento. Depende de que F2 defina cómo un visor no-Animate se ata a las señales. Convive con las animaciones canvas existentes (T-S177 cerrada · multi-instancia). Horizonte, sin disparador aún (P11). Relación: T-S213-CANAL-DIRECCION-SWOBJ (el swObj emisor da el modelo para que un visor emita) · T-S177 (canvas Animate) · línea Mus1 (T-S117-N4). | `LRU_HANDOFF_VIVO.md` (s213) + `src/pages/Mus1/Mus1.tsx` (referencia del patron emisor) |
| **T-S208-SPY-EN-ANALYZER** | s208 | **NUEVA · baja · diferida CON destino explícito** · pregunta de Manuel: ¿podría Analyzer1 absorber las funciones del DataPlane Spy y ser más intuitivo? Análisis s208: técnicamente viable — el contrato `ProtocolAdapter` reserva `encode(fields)` para Fases 5-6 (intervención activa · T-S55-C1) y el sub-patrón "vista hermana adaptativa" (T-S152-N1) permitiría una vista "DataPlane Bench" (productor + consumidor + aliasing + cardinalidad) sin tocar componentes existentes; `DataPlaneSpyService` quedaría intacto como sustrato (pieza de la trinidad s157), solo se replegaría la UI. PERO: el Spy como PiP ligero es más ágil para diagnóstico puntual que el orquestador completo de Analyzer1, y la convergencia de miradas sobre señales es exactamente el frente Observatorio (s155-s157). **Destino: arbitrar DENTRO del frente Observatorio, no antes** (P11 · sin trigger de dolor: el solape actual no cuesta nada). Contexto: en s208 el menú PiP se extrajo a lego compartido (PipMenuModal) y se arregló el bug latente s152 de lazy-sin-Suspense en PipWindow que colgaba la app al abrir el Spy en frío. | `LRU_HANDOFF_VIVO.md` (s208) + `lru_analyzer1_referencia_operativa_s152.md §2.2` + `lru_alfiler_observatorio_reformulado_s156.md` |
| **T-S202-DISPLAY-LABEL-ID** | s202 | **NUEVA · cosmética · P11** · un mismo sensor se muestra a veces por `id` (`LOC_Dev`, con guion bajo) y a veces por `label` (`LOC dev`, con espacio) según el lego — parece duplicidad pero es **id-vs-label** del catálogo (correcto por diseño, `sim-catalog.ts` L464). El `id` no lleva espacios (token de emparejamiento); el `label` es display. Propuesta de coherencia: que todos los legos muestren el `label`. Decisión + propagación a los legos pendiente; no abrir hasta que moleste (P11). Se arregla solo en los legos, nunca tocando catálogo ni animaciones. NO confundir con T-S150-N1 (aquel era prefijo basura; esto es display legítimo). | `lru_handoff_s202_s203.md` + `LRU_TECH_DEBT_s202.md` |
| **T-S193-ROUTERMODECHIP-RENAME** | s193 | **NUEVA · baja · cosmética** · `src/simulation/RouterModeChip.tsx` sigue vivo y correcto (es per-señal: lee `HwObjsContext` + `useSignalModes` + `signalRoute`, NO el tipo/campo retirado) pero arrastra el nombre del eje demolido. Montado ×2 en `Sim1Header`. Candidato a rename (p.ej. `SignalModeChip`). Sin urgencia (P11). | `LRU_TECH_DEBT_s193.md` |
| **T-S183-MODELO-UNIFICADO-SENALES** | s183 | **AVANZA · umbrella** · **R5.4 COMPLETO**: R5.4.1 gate fuera de `updateHwObjSensors` (único llamador externo vivo; siempre true por construcción — neutral) + R5.4.2 `cb.init(getMode)` fuera + idempotencia por flag de módulo en `controlBusInit` + R5.4.3 gate per-hwObj entero fuera de `ControlBus.ts` (eje per-señal intacto) + R5.4.4 ORC desacoplado (mirror/register/unregister/resetAll) + R5.4.5 auto-registro `rs.register` fuera de `registryMqttHandlers`. El RouterService ya **no tiene llamador externo** (solo `routerServiceInit`/`RouterServiceReact`/`App.tsx` init). Queda **R5.5** (borrar el servicio). | `lru_handoff_s189_s190.md` |
| **T-S184-FW-INHIB-PER-CANAL** | s184 | **NUEVA · horizonte · NO bloqueante** · el contrato `to/sim` del firmware es **whole-board** (`g_sim_inhibited` · detiene todo `from/events`); no existe inhibición per-canal. El control per-entrada que importa ya existe app-side (gate 2 per-señal: una señal HwSim ignora `mqtt-hw` y acepta `mqtt-sim` inyectado); la inhibición de firmware es solo la capa física gruesa. Falta extender `to/sim` a **máscara de canal**. Encaja con el eje egress/actuadores del destilado s183. **Cierra D-s182-FW de facto** (parte app hecha; granularidad firmware diferida). | `lru_handoff_s184_s185.md §3` + `src/datosLru01/types.ts` (`mqttTopicTo.sim`) + `RouterService._handleInhibition` |
| **T-S182-ENLACE-SENAL-SENAL** | s182 | **NUEVA · horizonte · P11 · encuadrada s202 → diferido CON disparo · triggers reales s207+s209** · la señal virtual (p.ej. `N1_L`) y la física (`ble_xxx/adcN`) son **ambas ciudadanas de primer orden**; un enlace futuro señal→señal con conversión (binario crudo → unidades) las acoplaría. No existe aún; diferido. **Tripwires de reactivación (encuadre s202):** (T1 · MATERIALIZADO s209) que se toque actuación/egress → entra el contrato del adaptador de dos caras (destilado s183 §4 + seguridad del egress §7) — las luces/vibrate de Mus1 s209 lo disparan; el frente egress queda articulado aparte en **T-S209-EGRESS-MESA**; (T2) que aparezca una 4ª instancia del patrón groupBy-restringido-a-un-set (tech-debt s201); **(T3 · MATERIALIZADO s207)** cruce músico-a-músico: Manuel quiso que el slider de un músico moviera el puerto de otro (`phone_yyy/virt1` → `pc_xxx/virt1`); hoy la mesa solo conecta agujas con señales, no señal con señal — diferido en s207 sin abrir (P11), pero el trigger ya es real, no hipotético. Conecta con `lru_alfiler_dos_universos_nomenclatura_s151.md` + Realm v3 (T-S67-A1) + T-S179-CHAIN-SENALES (misma familia). | `lru_mapa_unificacion_modos_universal_s182.md` §9 + `LRU_HANDOFF_VIVO.md` (s209) |
| **T-S180-PROSA-MODO** | s180 | **NUEVA · baja · NO bloqueante** · R0 renombró identificadores de código, no la **prosa** de comentarios: los docstrings de los 8 ficheros del eje modo siguen diciendo "per-sensor"/"sensorId"/"SensorMode". Pase no-funcional de alineación de prosa pendiente (cosmético). | `LRU_TECH_DEBT_s180.md` |
| **T-S179-CHART-FUENTE** | s179 | **NUEVA · abierta · MEDIA** · el Chart debe leer la autoridad (DataPlane) SIEMPRE. Camino A hecho (D-s179-CHART-1, motor corriendo). Camino B diferido (4 piezas B-1..B-4: eje de tiempo coherente con la cadencia · verdad con motor parado · hogar de la historia · selector de fuente). | `lru_alfiler_chart_fuente_dataplane_s179.md` |
| **T-S179-CHAIN-SENALES** | s179 | **NUEVA · diferida · P11** · encadenamiento señal→señal con conversión (escalado/unidades como enlace de primera clase · hoy lo hace implícito el vertido R1 sin escala). **Absorbe T-S174-VERTIDO-SIN-ESCALA**. Entronca con Cat.6 (s161) + Realm v3 (`namedFormulas`). No hace falta para unificar el modo. | `lru_alfiler_unificacion_modos_signal_s179.md` |
| **T-S177-ANIMATE-INSTANCIA-UNICA** | s177 | **CERRADA s208 · por materialización + validación runtime de Manuel** · NO era limitación del runtime de Adobe Animate (hipótesis s177 errónea): era el **dedupe por swObjId en `IB_04.registerClip`** (skip silencioso → los clipRefs del 2º canvas nunca entraban en ningún binding → mudos; agravante: cerrar el 1er canvas borraba el binding y el gemelo quedaba mudo para siempre). Fix s208 (Opción B, commit `3c7a6c0b`): `NeedleBinding.clipRefs[]` etiquetados por stage + fan-out de `change1()` + merge con dedupe por clipRef concreto + limpieza per-stage (el binding y la identidad ORC viven hasta la última instancia). **Validado runtime s208**: A1+B1 mismo instrumento con agujas vivas en ambos · cierre del primero sin matar al gemelo · enlace de mesa moviendo los dos. Semántica: una identidad ORC, N vistas. El caso "señales distintas por instancia" queda fuera (familia T-S182). | `src/simulation/InstrumentBridge_04.ts` (s208) |
| **T-S173-IAS-PUERTO-MUDO** | s173 | **NUEVA · informativa · no bloqueante** · la señal `IAS` enlazada en `Real` tiene su puerto del A92418 sin emitir (en la foto runtime, `temp1/pres1/hum1/lux1/trk1*/mag1*` en `catalog_init` stale; solo `adc*`/IMU emitían). Resultado correcto de "Real = solo dato real": motor cortado + sin dato real → la aguja se queda en su valor de arranque. Revela un puerto silencioso. Acción s174: confirmar a qué puerto está mapeada `IAS` | `LRU_TECH_DEBT_s173.md` + `lru_handoff_s173_s174.md §6` |
| **T-S164-DUAL-WRITE-MASIVO** | s164 · localizada s169 | **DISUELTA en su raíz de vertido** (D-s170-1, cura B) · el motor ya no vuelca el display `hw` al DataPlane · validación de Manuel OK (shadow deja de subir sobre adc1/adc2 con motor corriendo). Residual: el motor sigue *generando* esos valores en el tick (churn, no vertido) → se pliega al paso 4 | `LRU_TECH_DEBT_s170.md` + `src/simulation/SimService.ts §pushToRegistry` |
| **T-S163-RUIDO-DERIVADOS-ISA** | s163 | **DISUELTA** con el paso 2 pleno ("unificar = optimizar") | `LRU_TECH_DEBT_s170.md` |
| **T-S167-N1** | s167 | **A VERIFICAR tras cura B** · su observación era `'sim1'` sobre canales hw con el motor **parado**; `pushToRegistry` solo corre con el motor en marcha → la cura B no la cubre necesariamente. Re-comprobar con `liveWriters()` motor parado; si persiste, hay otro productor `'sim1'` por localizar | `LRU_TECH_DEBT_s170.md` |
| **T-S168-N1** | s168 | **NUEVA · severidad informativa (diagnóstica) · falso positivo de `diagnoseSensor` con `catalog_init`** · destapado al volcar `SourceMapDebug.sourceMap()` tras F5: casi todos los sensores sim salen `conflict:true` con `mode:'Gen'` y `lastWriter:'catalog_init'`, mientras los que el motor ya pisó (VMO/TAS/SAT/TAT) salen `conflict:false` · causa: `diagnoseSensor` calcula `conflict` cruzando `lastWriter` contra `SENSOR_PERMISSIONS[mode]`, pero **no contempla `UNGATED_SOURCES`** (`'sim1'`, `'catalog_init'`) que el gate real SÍ deja pasar siempre · la **lente de diagnóstico miente**, el gate real (`allowsSensor`) no se ve afectado (no consume este cálculo) · NO bloqueante · fix candidato: que `diagnoseSensor` trate `catalog_init` (y `sim1` bajo `Gen`) como no-conflicto, o exponer `UNGATED_SOURCES` desde ControlBus para que la lente lo respete · trigger natural: cuando el observatorio/Analyzer consuma `conflicts()` para alarmas y el ruido de falsos positivos moleste | `src/simulation/sourceConflictDetector.ts §diagnoseSensor` + `src/simulation/ControlBus.ts §UNGATED_SOURCES` |
| **T-S166-CONTRATO-LEGO-INLINE** | s166 | Menor/informativa · Nésima instancia del patrón "cambio de contrato del lego deja atrás a un consumidor inline": el contrato Mixer creció en s165 (botones Sensores/Displays), Sim1/Sim4 wrappers se actualizaron pero Sim2 construye el contrato *inline* (`buildMixerData`) y se quedó atrás; `tsc` lo destapó en s166 (validación s165 fue runtime, vite dev sin typecheck). Resuelto cableando en Sim2 (paridad cross-host). **Sub-nota metodológica candidata**: para cambios de contrato de lego exigir `npm run build` (con `tsc`) antes de declarar build verde. Formalizar si emerge 2ª instancia. Prioridad baja | `src/pages/Sim2/Sim2.tsx` `buildMixerData` + `LRU_TECH_DEBT_s166.md` |
| **T-S162-ENLACES-SUSTRATO** | s162 | **NUEVA · severidad informativa · naturaleza dual `enlaces` como sustrato vs estado de UI** · articulada en alfiler s162 como respuesta arquitectural diferida a la pregunta D5 · hoy `enlaces: MatrizEnlaces` vive como React state en el ORC con persistencia localStorage · funciona operativamente · pero la voz fundacional de Manuel s162 inclinó la **naturaleza** del objeto hacia sustrato (define el camino de las señales · cardinalidad baja · alcance global · cruza Universo A↔B) sin que la **localización** (Context vs Singleton) deba cambiar hoy · Cat.6 materializada con D5=A inyección · promoción a singleton `getEnlacesPlane()` (análogo a `getDataPlane()`) diferida hasta emerger trigger empírico T1 (segundo lector síncrono externo de `enlaces`) o T2 (necesidad de lectura síncrona fuera de React tree) · refactor A→B1 trivial (1 línea en `facetEnlace.ts`) · hereda dialogo con T-S161-DOC-1 (si B1 se materializa, T-S161-DOC-1 reformulada de "doble representación redundante" a "tres representaciones con disciplina") · NO bloqueante · 11ª aplicación operativa disciplina alfiler s125 | `lru_alfiler_enlaces_sustrato_s162.md` (alfiler dedicado · 14.55 KB · 10 secciones) + `src/realm/queryFacets/facetEnlace.ts` (cabecera comenta el alfiler como salvaguarda) |
| **T-S162-VITEST-ALIAS** | s162 | **NUEVA · severidad informativa · vitest.config.ts no configura resolve.alias `@/*`** · deuda preexistente revelada al cargar `registryEnlaces.ts` desde tests Cat.6 (importa `dbg as bus` de `@/debug/DebugBus`) · build vite resuelve aliases vía `vite.config.ts` pero vitest.config.ts no los hereda · solución s162: `vi.mock('@/debug/DebugBus', …)` en el test con `Proxy` de métodos no-op (alcance local sin tocar infra) · vías de resolución eventual: (a) añadir `resolve.alias` a `vitest.config.ts` (~10 líneas · afecta futuros tests) · (b) extraer helpers puros como `matrizEnlazar/matrizDesenlazar/clonarMatriz` a fichero sin imports `@/*` (refactor mínimo si emerge presión) · NO bloqueante · trigger natural cierre: siguiente test que importe transitivamente de `registryEnlaces.ts` o de cualquier módulo con alias `@/*` no resuelto | `vitest.config.ts` + `src/realm/queryFacets/queryFacets.test.ts §vi.mock` |
| **T-S161-DOC-1** | s161 | **NUEVA · severidad informativa · doble representación redundante del cableado** · el cableado vive simultáneamente en `enlaces.porSwObj`/`enlaces.porHwObj` (matriz bidireccional canónica del ORC · `MatrizEnlaces`) y en `swObjs[id].enlaces[]` (array dentro del SwObj) · el ORC sincroniza ambas representaciones en cada `enlazar/desenlazar/reenlazar` · deuda técnica conocida que precede a s161 · implicación para Cat.6: el adaptador `facetEnlace` lee canónicamente de la matriz (no consulta `swObjs[id].enlaces`) · decisión deliberada documentada en strawman del destilado s161 §6 · NO bloqueante · resolución eventual cuando emerja sesión arquitectural mayor que aborde la unificación | `lru_destilado_facetas_cat6_enlace_s161.md §9.1` + `src/datosLru01/ObjectRegistryContext.tsx` (sincronización en `enlazar`/`desenlazar`/`reenlazar`) |
| **T-S160-CAT3-A** | s160 | **NUEVA · severidad baja · Cat.3 hwObj v1 no proyecta sensores Universo A con `puertoHwObj?` declarado** · split `sensorId` con `/` opera limpio sobre sensores Universo B (formato `hwObjId/puerto`) · sensores Universo A sin barra devuelven `[]` · refinamiento pendiente cuando `SensorDef.puertoHwObj` evolucione a tipo más rico (ej: `{ hwObjId, puerto }`) o cuando emerja consumidor que pida el cruce A→B vía declaración · NO bloqueante · v1 funciona en producto vivo | `lru_destilado_api_consultora_materializada_s160.md §4.4` + `src/realm/queryFacets/facetHwObj.ts` |
| **T-S158-P13-CAND** | s158 | **NUEVA · candidato cristalización fundacional 8ª · formulación refinada respecto al alfiler s157** · "P13 · Catalogación facetada · Las entidades funcionales del sistema (sensores · actuadores · señales) son el sustrato unificado sobre el que pueden definirse N catalogaciones declarativas ortogonales. Cada catalogación es una proyección consultable del conjunto de entidades sin pretensión de jerarquía maestra. Una API consultora uniforme (registry de facetas + función query) permite consultar el sistema por cualquier faceta o intersección de facetas sin elegir una primaria. El DataPlane es el portador de los valores en runtime, no el sustrato de las catalogaciones." · refinamiento clave respecto a §5 del alfiler s157: sustrato = entidades funcionales (no DataPlane) · confirmado por audit empírico s158 (4 de 5 catalogaciones no requieren DataPlane para definirse) · 3 razones explícitas para no cristalizar en s158 (decisión arquitectural mayor pertenece a Manuel + falta 1-2 instancias de aplicación operativa + verificación cruzada con §11ter agnóstico + N realms pendiente) · cierre por materialización de la API consultora (1ª aplicación) + sesión de cristalización dedicada · NO bloqueante (la API consultora opera con o sin postulado integrado) | `lru_destilado_facetas_multivista_s158.md §5` (formulación refinada + 3 razones) |
| **T-S157-CAND-1** | s157 | **Desacoplar tick SignalEngine de SignalLab montado · prioridad media** · hallazgo empírico s157: SignalEngine procesa solo cuando SignalLab está montado en DOM (el tick vive en el componente) · trinidad universal (IB_04 pull + DataPlaneSpy subscribe + SignalEngine procesa) requiere que el procesador sea infraestructura independiente del consumidor · trigger natural: cuando emerja necesidad de procesamiento continuo sin SignalLab montado (ej: observatorio o alarmas en background) | `lru_ampliacion_signalengine_trinidad_s157.md §8` |
| **T-S157-CAND-2** | s157 | **4 modos RouterService como dimensión causal del cruce · vista panorámica · prioridad media** · RouterService.ts opera en 4 modos (Loc/Rnd/HwReal/HwSim) que determinan si `valuesRef` y `hwObjs.hw1.puertoValor` están acoplados o desacoplados · sin observabilidad simultánea de modo + estado contenedores, la asimetría T-S151-N2 es opaca · vista panorámica candidata como herramienta diagnóstica del observatorio · trigger natural: arbitraje sesión dedicada observatorio | `lru_geografia_desconexion_uso_real_s157.md §10.7` |
| **T-S156-N1** | s156 | **Sub-patrón meta-metodológico observado · 1ª instancia formalizada** · candidato a anotación en `LRU_PRINCIPIOS_OPERATIVOS_VIVO §3.3` si emerge 2ª instancia · Claude tendió durante 5 turnos consecutivos en s156 a articular narrativas estructurales en abstracto ("cuádruple encarnación", "SP6 cristalización", "vista hermana adaptativa") cuando el problema concreto articulado por Manuel era una desconexión real y conocida · Manuel reformuló en turno 6 con cita gold operativa · Claude reconoció patrón en turno 7 y reformuló · **lección operativa documentada**: *"Cuando Manuel articula el problema en términos operativos concretos (desconexión real · pérdida de conexiones · reconectar), conviene aceptar como gold antes que continuar la articulación arquitectónica abstracta. La articulación abstracta tiene valor pero NO sustituye al diagnóstico empírico del problema concreto que Manuel observa en su uso real del producto."* · extensión del sub-patrón ya documentado en `lru_alfiler_dos_universos_nomenclatura_s151.md §8bis.6` (Manuel propone prueba empírica vs Claude articula narrativa) · 2ª instancia del mismo patrón documentada → formalización candidata firme | `lru_alfiler_observatorio_reformulado_s156.md §7` + `lru_handoff_s156_s157.md §5` |
| **T-S152-VALIDACION** | s152 | **4 fases NO validadas empíricamente al cierre s152 · bloqueante para frentes superiores** · Fase 1.5 (Export JSON 3 modos) + Fase 3 (TraceView cross-protocol heurístico) + Fase 2b (DataPlaneConstellation SIM_CATALOG) + Fase 2c (DataPlaneStatsView por source) materializadas en cascada constructiva sin pausa de validación intermedia (decisión arbitral Manuel implícita: *"continuar"* tras cada fase) · SOLO Fase 2 (Scope) validada empíricamente por Manuel (*"Todo funciona ✓"*) · **Plan Paso 1 s153 obligatorio** (30-45 min): activar DataPlane Sim + DataPlane Real + MQTT Sim + Internal Sim · acumular 30s · probar las 4 fases end-to-end · detalle en `lru_handoff_s152_s153.md §4.A` · cierre esperado: validación empirica completa · cierre posible alternativo: bugs detectados → sesión s153 dedicada a fix | `LRU_TECH_DEBT_s152.md §1` + `lru_alfiler_dataplane_viewer_s152.md §6.A` |
| **T-S152-N1** | s152 | **Sub-patrón "vista hermana adaptativa por protocolo" cualifica firme con 3 aplicaciones formales en sesión única** · candidato a destilado Aníllo 2 · 1. `WaveformView` (ARINC429) → `DataPlaneScopeView` (Fase 2) · 2. `TopicConstellation` (MQTT) → `DataPlaneConstellation` (Fase 2b) · 3. `StatsView` (por proto) → `DataPlaneStatsView` (por source · Fase 2c) · mecánica: NO modificar componente original · crear hermano con sufijo `DataPlane*` · mismo prop shape · selección en switch case por `protocol === 'dataplane' \|\|(!hasMqtt && hasDataPlane)` · trigger natural cristalización: 4ª aplicación (candidatos `DataPlaneTimelineView` o `DataPlaneLatencyAnalyzer`) · paridad conceptual con "lego neutro + wrapper per-host" pero aplicado a vistas adaptativas dentro de un orquestador | `LRU_TECH_DEBT_s152.md §1` + `lru_alfiler_dataplane_viewer_s152.md §3` |
| **T-S152-N2** | s152 | **Acoplamiento `DataPlaneConstellation` ↔ `useSimCatalog`** · deuda documental relevante para Realm v2 · antes de s152 Analyzer1 era completamente agnóstico al catálogo · a partir de Fase 2b importa `useSimCatalog` de `'../../../simulation/useSimCatalog'` para construir el árbol completo displays→systems→sensors · consecuencia para Realm v2: cuando el catálogo migre a Firestore (Fase 4 articulada en cabecera de `sim-catalog.ts`), la vista puede no funcionar idénticamente si hay diferencias estructurales (systems sin sensors enumerables · lazy loading) · mitigación inmediata: ninguna · deuda documental para tener presente cuando T-S67-A1+A2 se desbloqueen | `LRU_TECH_DEBT_s152.md §1` + `src/pages/Analyzer1/bottom/DataPlaneConstellation.tsx` |
| **T-S152-N3** | s152 | **Heurística Fase 3 con tokens cortos genera ruido** · candidato refinamiento si emerge fricción · `computeTrace` empareja frames buscando `identityTokens` del origen dentro del `searchableContent` de candidatos · ARINC429 labels son 1-3 dígitos decimales y pueden generar matches espúreos en buffers grandes · DataPlane `index` del slot es buscable con peso bajo (0.3) pero potencialmente ruidoso · **mitigación documentada**: usuario sube umbral a "estricto" (0.5) o "fuerte" (0.7) cuando trabaja con ARINC429 origen (UI ya expone control) · mitigación posible si emerge fricción: filtrar tokens <3 chars para protocolos con etiquetas cortas · penalización extra a tokens numéricos puros · boost si token aparece junto a otros tokens del mismo origen | `LRU_TECH_DEBT_s152.md §1` + `src/pages/Analyzer1/correlation/traceEngine.ts` |
| **T-S152-N4** | s152 | **`DataPlaneStatsView` no permite drill-down** · refinamiento futuro si emerge necesidad · click en source de la leyenda (Col 1) NO filtra frames mostrados · NO filtra métricas (Col 2) · NO filtra Top 5 (Col 3) · es vista de overview puro · refinamiento posible: callback `onFilterBySource(source: string)` propagado al panel padre + filtro adicional en `Analyzer1.tsx protoFilters` · trigger natural: usuario quiere ver "solo writes de sim-engine" desde el donut · coste estimado: ~30 min si se materializa | `LRU_TECH_DEBT_s152.md §1` + `src/pages/Analyzer1/bottom/DataPlaneStatsView.tsx` |
| **T-S150-N1** | s150 | **Prefijo legacy `sim1_` en hwObjs/swObjs · namespace deliberado nunca renombrado al llegar Sim4** · fuente única identificada: `SimService.ts:148` constante `SIM_HWOBJ_PREFIX = 'sim1_'` · usada en una sola línea (437): `` `${SIM_HWOBJ_PREFIX}${systemId}` `` · se propaga a todos los consumers vía `RegistryContext.hwObjs/swObjs` poblado por `SimServiceReact.ts` · manifestación visible: usuarios ven `sim1_eng`, `sim1_bleed`, etc. en EnlacesPanel desde Sim4 (sin razón aparente) · SystemsPanel muestra IDs neutros (`eng`, `bleed`) por usar fuente distinta (`sim-catalog.ts`) · hallazgo importante comentario línea 1102: `// s28: solo registrar hwObjs virtuales sim1_* — los ble_* los registra el ORC` revela que `sim1_` no es deuda accidental sino **namespace deliberado** para distinguir hwObjs de simulación (`sim1_*`) de hwObjs reales bluetooth (`ble_*`) · lo que NO se hizo al llegar Sim4: crear namespace `sim4_*` paralelo O renombrar `sim1_*` a `sim_*` genérico · **4 opciones articulatorias pendientes** (D-s15N-NAMING): (1) renombrar a `sim_` neutro, (2) renombrar a `obj_` semántico, (3) mantener prefijo + capa label cosmética en EnlacesPanel, (4) eliminar prefijo + migrador localStorage v1→v2 · **dependencias a auditar antes de decidir**: localStorage persistido con IDs prefijados · topics MQTT del routing exterior · consumers que asumen prefijo (`HwObjPopup.tsx`) · interacción con namespace hermano `ble_*` · **acoplada con T-S149-SEMANTICA-SENALES** · candidato a sesión articulatoria conjunta · prioridad media · no bloqueante · alta visibilidad al usuario · **RESUELTA-EN-CÓDIGO-DEL-REPO (s202 · commit 1879dd3)**: ambos productores (`SimService.hwObjIdForSystem` + `InstrumentBridge_04:715`) aplanan a `systemId` limpio; gate por `displayId==='hw'`; migrador localStorage v1→v2 en `registryEnlaces`. Queda **cola de dato externo NO commiteable**: `defaultHwObj.hwObjId` con `sim1_` horneado en los manifests `.ib04` de las animaciones (fuera del repo) → limpiar en autoría + barrido de localStorage (6 enlaces persistidos duplicados). Limpieza de código restante = Slice 2 (TD-s202-1). | `LRU_TECH_DEBT_s202.md` + commit 1879dd3 |
| **T-S201-BITACORA-COMPRESION-III** | s201 | **NUEVA · baja · higiene documental** · las dos bitácoras vivas (SESIONES + HANDOFFS) pesan mucho: logs append-only nunca podados + entradas verbosas (200-400 palabras/sesión) + el resumen por sesión vive 4 veces (línea 5 del MAPA · pie de ARRANQUE · las dos bitácoras). Objetivo recomendado: **SESIONES primero** (lista uniforme una viñeta por sesión · máxima redundancia con el MAPA L5 + el handoff individual + git · plantilla ya conocida: Compresión I s128 / II s157 → III). Movimiento: archivar entradas anteriores a ~s190 a `lru_bitacora_sesiones_archivo_s201.md` (mismo patrón que la poda de tensiones s201) · dejar las últimas ~10 en verbose · git como historia profunda · backfill s199/s200/s201 en el mismo pase. HANDOFFS después y aparte (mezcla dos estilos de lista + el índice escueto «consumido por sN» no se aplana igual · más capas de archivo). Hermana de §3 (anti-reacumulación) y de la poda de tensiones s201. | `LRU_TECH_DEBT_s201.md` (HEREDADO) + `lru_handoff_s201_s202.md` |

---

## §2 · Apéndice · pendiente de poda mayor (60) — SEMBRADO

> Tensiones **antiguas sin triar** (última mención < s150; el enjambre s67–s149). Muchas estarán **superadas, ya hechas sin marcar, o sin sentido** en el estado actual — requieren **juicio per-ID de Manuel** (viva / cerrada-sin-marcar / caducada). Es la **etapa 3** de la poda, sembrada: una sesión futura las recorre, archiva las muertas y promueve a §1 las que sigan vivas. Mientras tanto, listadas compactas (no se pierden) pero **fuera del seguimiento activo**.

> Formato: `` `ID` `` (última mención ≈ sN; `pre-s130` = previo a las sub-cabeceras por sesión).

- `T-S149-MODAL-CANONICO` (≈ s149)
- `T-S149-NINTH-LEGO` (≈ s149)
- `T-S149-SEMANTICA-SENALES` (≈ s149)
- `T-S148-CALLBACK-REF` (≈ s148)
- `T-S148-CROMATICA-NAVEGACION` (≈ s148)
- `T-S148-LISTAS-VERTICALES` (≈ s148)
- `T-S148-PORTAL-OVERFLOW` (≈ s148)
- `T-S144-N1` (≈ s144)
- `T-S144-N2` (≈ s144)
- `T-S144-N3` (≈ s144)
- `T-S138-HTMLS-DEUDA-CANONICA` (≈ s138)
- `T-S138-SIM4-VISUAL-VIVO` (≈ s138)
- `T-S134-DEUDA-1` (≈ s134)
- `T-S134-DOC-1` (≈ s134)
- `T-S134-DOC-2` (≈ s134)
- `T-S134-N10` (≈ s134)
- `T-S134-N11` (≈ s134)
- `T-S134-N12` (≈ s134)
- `T-S134-N13` (≈ s134)
- `T-S134-N14` (≈ s134)
- `T-S134-N6` (≈ s134)
- `T-S134-N7` (≈ s134)
- `T-S134-N9` (≈ s134)
- `T-S128-N1` (≈ s128)
- `T-S127-N1` (≈ s127)
- `T-S127-N2` (≈ s127)
- `T-S127-N3` (≈ s127)
- `T-S126-N1` (≈ s126)
- `T-S125-N2` (≈ s125)
- `T-S125-N3` (≈ s125)
- `T-S124-1` (≈ s124)
- `T-S122-OSC` (≈ s122)
- `T-S122-X` (≈ s122)
- `T-S122-densidad` (≈ s122)
- `T-S122-grep-glob` (≈ s122)
- `T-S117-N1` (≈ s117)
- `T-S117-N2` (≈ s117)
- `T-S117-N3` (≈ s117)
- `T-S117-N4` (≈ s117)
- `T-S84-A5` (≈ s84)
- `T-AU73-8` (≈ pre-s130)
- `T-AU73-9` (≈ pre-s130)
- `T-S117-M1` (≈ pre-s130)
- `T-S117-N5` (≈ pre-s130)
- `T-S117-N6` (≈ pre-s130)
- `T-S67-A1` (≈ pre-s130)
- `T-S67-A2` (≈ pre-s130)
- `T-S77-C1` (≈ pre-s130)
- `T-S80-N1` (≈ pre-s130)
- `T-S83-N1` (≈ pre-s130)
- `T-S83-N2` (≈ pre-s130)
- `T-S84-A2` (≈ pre-s130)
- `T-S84-A3` (≈ pre-s130)
- `T-S84-A4` (≈ pre-s130)
- `T-S84-A6` (≈ pre-s130)
- `T-S84-A7` (≈ pre-s130)
- `T-S85-A1` (≈ pre-s130)
- `T-S85-A2` (≈ pre-s130)
- `T-S85-A4` (≈ pre-s130)
- `T-S85-A5` (≈ pre-s130)

---

## §3 · Disciplina anti-reacumulación (etapa 4 · pendiente de formalizar en LRU_METODO)

Sembrado, no formalizado aún:
- **Edición in-place**: cada cierre edita la fila de §1 de las tensiones tocadas; **no** se añade bloque por sesión (causa raíz del log de 76 bloques).
- **Cadencia de archivado**: ronda de poda cuando §1 supere un umbral o cada N sesiones (la anterior fue s129 → s201, ~70 de demora).
- **Regla de caducidad** (candidata): tensión sin tocar > N sesiones y prioridad baja → al apéndice §2, no al seguimiento activo.

*Fin de LRU_TENSIONES_ABIERTAS_VIVO. Historia por sesión: git de este fichero. Cerradas: `lru_tensiones_resueltas_archivo_s201.md`.*
