# LRU · Bitácora de sesiones — histórico estable

**Documento vivo del anillo 1.B.2 · sin sufijo de sesión · accesible vía MCP filesystem**

Externalizado del bloque "Sesiones previas" del MAPA en s129 (palanca P · etapa 3). Cuando una sesión deja de ser relevante operativamente, se condensa aquí en rondas posteriores. Frontera flexible · arbitrada por Manuel cuando lo justifique la presión documental.

**Régimen desde s204 (D-s204-B)**: el registro hacia adelante es **mecánico** — al cierre de cada sesión se añade UNA línea copiada de `LRU_ESTADO.md` (qué se hizo · build · tensiones movidas), sin prosa nueva. Este fichero es el único registro vivo por-sesión: la bitácora de handoffs quedó **congelada en s204** (D-s204-C). El backfill s199→s203 murió por declaración (D-s204-A · ver §B).

Estado tras s162: 1 activa en MAPA (Última cerrada s162) + 61 condensadas aquí (s89→s161) + sesiones pre-s89 accesibles vía git + handoffs individuales en `public/docs-app/v1/doc/`.

---

### §B · Registro mecánico desde s204 (una línea por sesión · copiada de `LRU_ESTADO.md` al cierre · D-s204-B)

* **s243 · 2026-07-26** — **sesión de banco de memoria (rebanada 0 de medición + rebanada (a) del frente A) · `s3_lcd35` → 0.243.3** · **Mem.Top permanente en 3 capas** (método del dispatcher: heap interna/PSRAM + hwm de stack por tarea vía `uxTaskGetSystemState` · TRACE_FACILITY on · array en PSRAM · botón CONTROL de la PWA · `scripts/memtop-s243.mjs`) · **panic real cazado y muerto (0.243.2)**: stack overflow de `mqtt_task` en el 2º Ble.Off por to/rpc — el camino dispatcher→NimBLE necesita ~6,2 KB > 6.144 del default de esp-mqtt (bomba armada desde s242) → stack explícito 8.192, hwm medido después 2.084 · **TD-s242-2 CLASIFICADA y muerta**: leak real ~0,6-0,8 KB/ciclo BLE (escalera de reposos 51,3→48,4 en 4 ciclos; los 3,4 KB de s242 eran medición temprana — ~9 KB de recuperación diferida tras el Off) · coste despertar 34-36 KB estable · **rebanada (a) verificada (0.243.3)**: búferes WiFi estáticos TX 16→8 RX 10→6 → **+18,9 KB medidos** (predicción +19) · reposo boot fresco **51,3→70,2 KB** (el doble que al cierre s242) · `wifi_scan` hwm 5.288/6.144 (lazy confirmada · nota TD-s241-3) · catálogo tile RPC al día (16/16 = tope) · núcleo: NO · TD-s243-1/2/3 NUEVAS + notas TD-s238-1/s241-3/s229-1 · commits firmware host-side pendientes · frente s244 = rebanada (b) (+15 KB · línea base 70,2) | (c)/(d) | portal AP (diseño) | banco audio TD-s242-1 | frentes previos
* **s242 · 2026-07-23** — **sesión de memoria interna S3 (análisis + firmware + PWA) · `s3_lcd35` → 0.242.5** · análisis DRAM con cifras de linker (estático ~156 KB DIRAM · hallazgos: audio I2S 24/7 sin uso ~16 KB · netif AP fantasma · esp32-camera 8,2 KB .bss sin usarse · buffers RPC 12,7 KB .bss) · **Tier 1 config** (WiFi fuera de IRAM + -Os → pool 297→326 KB) · **audio DORMIDO** (monta→loopback→desmonta one-shot · banco pendiente TD-s242-1) · **TD-s241-1 MUERTA**: primera OTA HTTPS completa de la S3 (63 s · sha readback · valid en otalog) y reutilizada para 0.242.4 · **BLE DORMIDO (D-s242)**: OFF al boot · despierta por tile o `Ble.On`/`Ble.Off` del dispatcher · desmontaje diferido one-shot · buffers a PSRAM bajo demanda · coste medido 34,6 KB despierto/devueltos · **estado BLE observable** (campo `ble` en from/status retained, 0.242.5) · **PWA**: `mqttTopicTo.rpc()` + fila BLE remoto en CONTROL con chip confirmado por el device — **verificado en banco** · interna en reposo 7→57 KB libres · bloque 3→28 KB · D-s242: 3 anillos de actualización (OTA remota · portal AP subida local [sustituye BLE-OTA streaming] · BLE bajo demanda) · móvil: nRF ve la S3, Web Bluetooth exige HTTPS (Android sí · iOS no) · destilado `lru_dormidos_memoria_s242.md` · núcleo: NO · TD-s242-1/2 NUEVAS · commits firmware host-side pendientes · frente s243 = optimizaciones anotadas (+19/+15/+8/LVGL→PSRAM) | portal AP | bancos pendientes · ⚠ líneas s239-s241 de este registro ausentes (hueco mecánico detectado en s242)

* **s238 · 2026-07-18** — **cierre del banco GATT (TD-s237-1) + destilado del eje s237 + saneado de la PoC BLE + desbloqueo del arranque S3 (cámara↔BLE en DRAM) + versionado git del firmware · 0 código pwa6 (host-side vía MCP/puente)** · **TD-s237-1 CERRADA en banco**: 8 métodos del espejo LRU ok por GATT (Sys.GetInfo 776B · Status.Get 779B · ActState.Get 210B · OtaLog.Get 279B · Ui.Shot {url,panel} · Ui.Nav {col}) · los clásicos Config.Get/FS.List/RPC.List dan 404 correcto (la S3 habla espejo LRU, no built-ins mgos) · **DRAM reposo ~19-20 K sin fuga** (el DRAM 3K de s237 era transitorio del encode JPEG) → **P11 decidido: LVGL se queda /16** · **reboot-loop curado**: `assert es8311_test_semaphore == NULL` + `wifi:m f null` = DRAM interna a cero; la cura NimBLE-host-a-PSRAM de s237 seguía puesta, la culpable era la **cámara** (`esp_camera_port_init` OV5640 · 307K PSRAM + ~30K DMA interna · **sin uso**, la captura es `lv_snapshot` de pantalla) → comentada en main.cpp · **PoC saneada**: fix serialización GATT (el firmware notifica rx_ctl ANTES del Write Response del último chunk → la lectura chocaba con el write en vuelo · cura diferir a sendDone/pendingLen) + fila de botones S3 (espejo LRU) sin tocar los clásicos · **destilado** `lru_eje_plano_mando_unificado_s237.md` (dispatcher común + RPC/BLE + observabilidad + captura · VIGENTE, código validado en placa · Project `claude/` + disco + catálogo) · **versionado git**: `01_factory` → **`s3_lcd35`** (robocopy sin build/managed_components/_to_delete + git init + .gitignore + commit `47f311b` · 364 ficheros · sdkconfig versionado) · identidad git global `Manuel <contact@boxdin.com>` · núcleo: NO · **TD-s237-1 CERRADA** + **TD-s238-1 NUEVA** (cámara↔BLE en DRAM interna · palancas WIFI_LWIP-a-PSRAM / cámara-lazy / recortes) · git firmware árbol principal SALDADO (quedan rpc1cl/rpc1/blink1) · frente s239 = **seguir con s3_lcd35** (comprobar acceso MCP + `idf.py build` verde → jubilar 01_factory) | versionar los otros árboles | apuntar MCP git a C:\esp32 | TD-s238-1 | cliente RPC PWA (Candidato D) | flota (0.237.x + TD-s235-1) | sobre LRU-RPC | hilo seguridad (8 motivos)

* **s237 · 2026-07-18** — **sesión de firmware S3 (`01_factory` · SIN git · ~20 ficheros + 2 componentes nuevos) — paridad RPC/BLE + plano de mando observable + captura/navegación remota · 0 código pwa6** · D-s236-C ejecutada en 5 rebanadas (0.233.1→0.237.5, bump antes de CADA build): **(a)** dispatcher común `lru_cmd_dispatch` en esp_port (composición separada de publicación · contrato MQTT intacto · naming D-s237-A: espejo propio + alias Sys.GetInfo) · **(b)** `esp_ble_rpc_port` NUEVO (mOS-RPC-GATT sobre NimBLE · UUIDs/framing BE32 del clásico → un cliente para ambas familias · advertising s3_ siempre-on D-s237-B) · **(c)** tiles **BLE** (col 3) y **RPC** (col 4: monitor del plano de mando unificado con contadores por método/transporte + **self-test loopback** 733 B en 2,5 ms = germen del twin §7ter.2) · **(d)** saneado warnings + `temp_c` VUELVE a from/status (dueño único del sensor · mudo desde s234) · **(e)** captura+navegación remota (`Ui.Nav/Goto/Shot` + `to/ui` + `esp_shot_port` httpd :80 con panel Capturar/descargar · snapshot LVGL→JPEG) · **banco DRAM en 2 vueltas**: NimBLE interno 63 KB ahogó LVGL/WiFi → LVGL /16 + orden UI-primero + **host NimBLE a PSRAM** (MEM_ALLOC_MODE_EXTERNAL) → coste 34 KB / ~52 KB libres · EAGAIN de la captura cazado (power-save WiFi pierde ACKs → PS_NONE durante envío + chunked) · Manuel subió las primeras capturas de la S3 a la sesión · la referencia S3 ya dice la verdad (TD-s234-1 reformulada: solo queda publicar) · núcleo: NO · **TD-s237-1 NUEVA** (frames RPC por GATT sin ejercitar `ble 0` + DRAM reposo por confirmar + coexistencia sostenida) + notas TD-s231-1 (**8º motivo**: HTTP :80 sin auth) / s229-1 / s225-2 / s217-1 / s218-1 · git firmware CRECE FUERTE (01_factory s224..s237) · frente s238 = **cerrar TD-s237-1** | destilado anillo 2 s237 | banco PoC clásico (TD-s236-1) | cliente RPC PWA | flota (publicar 0.237.x + TD-s235-1) | sobre LRU-RPC | hilo seguridad (8 motivos)

* **s236 · 2026-07-17** — **sesión de análisis/cristalización: conectividad BLE PWA↔placa como canal de resiliencia + el patrón RPC como pieza transversal · 0 código src** · patrón s235 reeditado (*antes de portar, mirar qué trae ya la plataforma*): el clásico **YA habla RPC por BLE** — rpc-gatts 2.20.0 + bt-common/NimBLE compilados y habilitados en el 0.235.1 desplegado (frame 4096 · MTU 256 · ~56 KB DRAM ya pagados · el prefijo `ble_` de la flota ES el advertising) · matiz que ordena: `bt.keep_enabled=false` → BT se apaga al conectar WiFi · **corrección de coherencia al cierre (H3 s231)**: en la flota provisionada el apagado es PERSISTENTE (`bt.enable=false` en conf9) → la huérfana del aula NO anuncia hoy — nivel 0 de fábrica solo en placas sin provisionar; flota = barrido de reactivación BLE por config remota (revivir = config, no reflash) · siempre-on = 1 línea mos.yml por OTA con banco previo (coexistencia TDM radio única + free_heap) · el hueco es el **cliente PWA** (BleAdapter actual = UART-style; protocolo mOS = 3 características + framing BE32, ~100 líneas Web Bluetooth · Chromium/HTTPS · iOS no) · modelo cristalizado: *«BLE es canal de mando y rescate, no de transferencia masiva — la placa huérfana se repara la WiFi por BLE y se OTAa sola por HTTP»* · **D-s236-C**: la S3 entra como frente firme — mismo servicio mOS-RPC-GATT sobre NimBLE en 01_factory como transporte fino hacia los handlers MQTT existentes (`handle_command(json)` común · un cliente PWA para ambas familias) · **D-s236-D**: el patrón RPC = **pieza transversal abierta y reutilizable** (8 aprovechamientos §7ter: RpcClient genérico sobre CommsManager · twin por loopback → disolvería TD-s218-1 · Caps.Get para T-S225/TD-s207-1 · tap rpc Analyzer · ver+ACL día 0 · paginación · Web Serial · lengua franca encarnaciones) · guardarraíl: incremental, flota mantiene prioridad · `rpc.gatts.sec_level=0` = **7º motivo hilo seguridad** · entregables: destilado `lru_analisis_ble_pwa_s236.md` + PoC `lru_poc_ble_rpc_s236.html` (picker ble_*/s3_*) · verificado contra build real + README del lib, **sin banco todavía** → **TD-s236-1 NUEVA** (dispara antes del cliente PWA · 1er sospechoso: `"src"` en el frame) + notas TD-s231-1/s218-1/s217-1/s207-1/s225-2/s205-1 · núcleo: NO · 4 commits docs vía MCP · index.md actualizado (index.html deuda) · frente s237 = **banco PoC BLE** | cliente RPC PWA | **flota S3 (TD-s234-1)** | **OTA clásica 0.235.x (TD-s235-1)** | sobre LRU-RPC (sesión dedicada) | hilo seguridad

* **s235 · 2026-07-17** — **sesión de firmware clásico (`rpc1cl` · SIN git · 2 ficheros: mos.yml + main.c) — porte de la resiliencia s234 al clásico · 0 código pwa6** · hallazgo central: **mgos 2.20 YA TRAE la mitad, apagada o sin observar** — multi-red nativa `wifi.sta/sta1/sta2` con rotación por `sta_connect_timeout` 15 s (confirmada EN LOG: `mgos_wifi_sta.c cfg 0 att 1`), SNTP ya activo (demo-bundle · resync 7200 s · prueba primero el NTP del DHCP) y task-WDT ya armado (30 s) → el trabajo real fue **encender + observar + cubrir el hueco** (muerte de conectividad sin cuelgue) · D-s235: (A) rotación = nativa del lib, no gestor propio (asimetría deliberada con la S3: mismo postulado T-S230, distinto mecanismo); (B) reserva de aula como default de firmware en mos.yml — **viaja a la flota por OTA sin claves por MQTT** (esquiva el 5º motivo del hilo seguridad · placeholder deshabilitado hasta que Manuel elija red · sin homecoming); (C) **net-vigilante**: sin broker ≥ `net_wdt.timeout_s` (600 · knob config · 0=off) → marca `netwdt.flg` + reboot → `reboot_reason:NET_WDT` · gated con `ota1_is_active()` (trial-boot s230 gana siempre); (D) hora observable: `MGOS_EVENT_TIME_CHANGED` → `epoch`/`time_synced`/`sntp_age_s` en from/status + republicación retained a la 1ª sync (criterio otalog: epoch>1e9); (E) **fix ssid veraz**: status/health pasan de la config primaria a la red CONECTADA (`mgos_wifi_get_connected_ssid` — bug latente que habría mentido al rotar) + `wifi_disc`/`wifi_nets` · **bump 0.235.1 ANTES del build** (regla s226 — contraejemplo de TD-s234-1) · **VALIDADO arranque en placa** (`ble_A92418` por cable): vigilante armado · máquina multi-config viva · SNTP del NTP del DHCP local a ~8 s · sin publicar (manifest en 0.233.1) · núcleo: NO (0 pwa6) · **TD-s235-1 NUEVA** (rotación + NET_WDT pendientes de banco · dispara antes de OTAr la flota clásica) + notas TD-s231-1/s234-1/s230-1 · arrastre git firmware crece (rpc1cl s227..s235) · frente s236 = **flota escalonada S3 (gate TD-s234-1)** | **OTA flota clásica 0.235.x (gates TD-s235-1 + reserva sta1 + publicar)** | R1 failover PWA | hilo seguridad

* **s234 · 2026-07-17** — **sesión de firmware S3 (`C:\esp32\01_factory` · SIN git · ~22 ficheros) — expansión mayor de UI + WiFi resiliente + RTC/hora + watchdog de vitalidad · 0 código pwa6** · orden de pantallas LRUs-primero con LRU_05 portada (cols 0..12 · D-s234-A) · BOOT navega (click avanza / doble retrocede, cíclico vía scroll_x — tile_act vacío hasta el 1er scroll, mordió) + touch_test de arranque RETIRADO (D-s234-B · heredera: LRU_06) · **LRU_06 NUEVA** (pizarra táctil canvas PSRAM · trazo continuo R/V/A + Borrar · SCROLL_CHAIN fuera) · **LRU_05 rev4** (4 gauges 74×74 anatomía LRU_04 repintado on-change + reloj + compactado) · **gestor WiFi completo** (D-s234-C · `esp_wifi_port` reescrito: 6 conocidas NVS + semilla-solo-1ª-vez + ROTACIÓN cíclica a las 3 caídas — extiende T-S230 al plano WiFi — + user_off respetado + clave encapsulada · tile reescrita: estado/barras/IP/MAC + escaneo por señal + conectar por tap con teclado + Guardadas con promover/borrar en rebuild DIFERIDO) · **RTC/hora** (D-s234-D · PCF85063 guarda UTC + TZ POSIX NVS defecto Madrid + SNTP reescribe RTC en diferido + petición-en-vuelo observable con veredicto · **LRU_07 RELOJ NUEVA** con ajuste por RODILLOS — día-de-semana en vivo, día reconstruido por mes/año — + teclado alternativo + 5 zonas + POSIX custom · el otalog S3 ganará fecha real) · **estabilidad: 2 cuelgues reales → causa raíz cazada con log** (`Failed to allocate priv TX buffer`: buferes LVGL en PSRAM → bufer de rebote DMA POR FLUSH → fallo de alloc = flushing atascado) → **fix raíz buff_dma interno (D-s234-F)** + TWDT PÁNICO 10 s + **vigilante de vitalidad ui_wdt (D-s234-E · VALIDADO EN VIVO: cuelgue→reinicio limpio 10 s)** + pila LVGL 4→10 KB + RTC-write fuera del cb SNTP · **barra estado-navegación `lru_bar` NUEVA** (D-s234-G · layer_top · ⌂/WiFi-teñido/reloj-vivo/✎/⚙ + resaltado de tile activa · overlays la ocultan · LRU_03/system/LRU_06/WIFI/RELOJ compactadas) · colaterales: casts enum C++20 + Kconfig fósil → `_to_delete\` · build 01_factory LIMPIO · núcleo: NO (0 pwa6) · **VALIDADO por Manuel en placa** (ciclo iterativo ≈10 builds por cable) · ⚠ referencia con 0.233.1-contenido-s234 SIN bump → **TD-s234-1 NUEVA (alta: bump 0.234.x antes de OTAr)** + TD-s234-2/3/4 nuevas + notas TD-s229-1/s230-1 · arrastre git firmware CRECE FUERTE · frente s235 = **bump + flota escalonada (#3, ahora con salud+rollback+otalog+UI+wifi+watchdog de una tacada)** | **portar al clásico (`ble_*`) multi-red+vigilante (+hora en pantalla · siembra remota vía to/cfg → 5º motivo hilo seguridad)** | R1 failover PWA | hilo seguridad

* **s233 · 2026-07-16** — **arrastre s229..s232 SALDADO (commit `ff478482` · con fix eslint: 2 no-unused-expressions en registryMqttHandlers bloqueaban Husky) + OTALOG entregado y VALIDADO end-to-end en ambas clases** · contrato D-s233-A: `to/otalog {}` → `from/otalog {id,type,cap:12,count,entries:[{ev,ver,err,ts,up}]}` cronológico (ev flash/swap/valid/rollback · ts epoch s con fallback up uptime) · **persistencia NVS en AMBAS clases (D-s233-B)** — ni RAM (muere en el reboot: inútil por construcción) ni fs (viaja con copy_fs · razón D-s230-A) · **cada firmware registra lo que él mismo ejecuta (D-s233-C)**: el `valid` lo escribe la imagen nueva al validar la prueba de salud → la primera OTA a 0.233 ya deja huella · rollback escrito ANTES del restart en el clásico · dedup por-boot en la S3 (el hermano sigue ABORTED en cada boot) · **PWA**: types + `sendOtalog` (ORC) + `useHwOtalog` NUEVO (gateado a físicos) + sección «Historial de actualizaciones» en TabFirmware (fecha real si ts>1e9, si no up · fallo suave «¿firmware anterior a 0.233?») · **S3**: anillo NVS ns `otalog` en esp_ota_port + rama en dispatcher · **clásico**: anillo NVS `ota1/olog` en ota1.c + handler/sub main.c · bump 0.233.1 ambas clases (sintaxis real `-BumpS3 <ver|auto>` — mordió en sesión) + publicación con poda + cerrojo del gen-manifest demostrado otra vez · **VALIDADO por Manuel**: OTA sha a las 2 referencias · clásico con FECHA REAL (SNTP) · S3 ancla up · experimento swap ida-y-vuelta a 0.230.4 → **3/12 `valid→swap→valid`** clavando la predicción (el anillo sobrevive 2 swaps + 3 reboots + tramo mudo de firmware viejo) · **corrección de vivos**: `C:\lru\docker` SÍ tiene backup automático diario/semanal/mensual (el «URGENTE sin copia» de s232 era falso · rebajado · matiz: los backups arrastran la clave en claro → hilo seguridad) · núcleo: **SÍ** (types/ORC/useHwOtalog NUEVO/TabFirmware + firmware ambas clases) · verde formal completo · TD-s233-1 NUEVA (carrera benigna push/pub) · notas en TD-s218-1/s229-2/s230-1/s208-1/s205-1 · ítem «otalog» del backlog OTA v2 (s226) CERRADO · frente s234 = **flota escalonada (#3, sin bloqueos)** | R1 failover PWA | hilo seguridad | capabilities S3

* **s232 · 2026-07-16** — **CUTOVER DEL BROKER EJECUTADO — el broker de producción es la `.102`** (RabbitMQ 4.2 docker) · **R2 del alfiler de resiliencia CONSUMADA** · checklist §3bis completo en el día: re-verificación (T1-T6 en 21883 + wss) → captura NAT → 3 IPs `.200`→`.102` (`mqtt_2`/`mqtt_ws`/`mqtt_wss`) → PWA reconectada → placas de referencia aterrizadas → **doble ciclo OTA verificado** (S3 `OTA OK → v0.230.4` · ble reflasheada 0.230.3) · **D-s232-A: TLS del web-mqtt 15676 NATIVO en RabbitMQ** (revierte D-s231-C tras arbitraje de Manuel: la mensajería y su TLS son del broker, el web de Caddy · `web_mqtt.ssl.*` + certs `C:\web_cert26` montados · renovación = `ssl:clear_pem_cache()` sin reinicio · ejecutado pre-cutover con 0 conexiones = recreate gratis) · **D-s232-B: login anónimo → `lru_app`** (el hueco invisible del gemelo: la PWA conecta SIN credenciales · el viejo lo aceptaba por config invisible a definitions · RabbitMQ 4 mapea anónimo a `anonymous_login_user` — `mqtt.default_user/pass` ya no existen · síntoma: ws 101 OK pero 0 conexiones Web MQTT + reintentos 1 s H4 · **lección: un gemelo se completa con config de auth, no solo usuarios**) · **criterio 80/443 de Manuel registrado** (443 único puerto garantizado en red restrictiva → `/mqtt` del 443 documentado como respaldo estratégico · input R1: cascada 15676→443/mqtt · input D-abierta-4: la S3 tiene wss gratis en esp_mqtt) · camino de flota verificado con `--host boxdin.ddns.net --port 8080` (hairpin real, no cubierto por los ensayos localhost s231) · la `.200` ENCENDIDA de respaldo → **L1/L2 separadas por primera vez** · alfiler actualizado (cabecera s232 + R2 ✔ + §3 + inputs) · plan s231 marcado EJECUTADO · PWA **CERO cambios src/** · núcleo: NO · TD-s232-1 NUEVA (labels fósiles scripts) · TD-s231-1 AMPLIADA a media (anónimo abierto + clave en claro en conf → hilo seguridad con 4 motivos) · TD-s228-1 nota (certs con 2 consumidores: Caddy reload + clear_pem_cache) · verde formal + commit pwa6 PENDIENTES (arrastre s229..s232) · **`C:\lru\docker` URGENTE** (config del broker de producción + secretos, sin versionar) · frente s233 = **verde formal + commit** | flota escalonada (#3, de paso aterriza la flota en el broker nuevo) | R1 failover PWA | hilo seguridad

* **s231 · 2026-07-16** — **R0 del alfiler de resiliencia EJECUTADA (hallazgos H1-H6 en §1bis del alfiler) + migración del broker a la `.102` PREPARADA AL COMPLETO Y ENSAYADA** · **H1/H2 no-failback en ambas clases** (clásico alterna al agotar backoff 2→60 s ~122 s/lado · S3 cada 5 desconexiones ~50 s/lado · ping-pong perpetuo · donde conectan SE QUEDAN · S3 con URIs/credenciales en `#define`) · **H3 GATT clásico MUERTO por persistencia** (NimBLE+rpc-gatts+7 métodos compilados pero `bt.keep_enabled=false` → `bt.enable=false` + `save_cfg` al primer IP → L3 muerta en toda la flota · revivir = config, no reflash) · **H4 PWA** reintento 1 s endpoint único + publish descarta · **H5 foto real**: broker vivo = `.200` RabbitMQ 3.13.3 nativo SOLO broker · router traduce WAN 8080→LAN 21883 (primario y respaldo = mismo listener) · Node-RED `.102` sin tocar la `.200` (flows auditados) · **H6→sonda S2**: el viejo TAMPOCO entrega retained a `#` → D-abierta-6 TEÓRICA, el cutover no degrada · **R2 ejecutada**: `definitions.json` 6 usuarios hasheados + BOM fuera (D-s231-A claves viven en el fichero, la UI las pierde) · espejo `21883:1883` compose (D-s231-B) · gemelo wss = Caddy catch-all `:15676` TLS→web-mqtt (D-s231-C · certs del 443) · **ensayos Manuel TODOS VERDES** (T1-T6 en 1883 y 21883 + wss + mgmt como `manuel`) · PWA producción verificada en DevTools `wss://boxdin.ddns.net:15676/ws` — no cambia ni un byte · **destilado NUEVO `lru_plan_migracion_broker_s231.md`** con §3bis checklist ejecutable (3 cambios de IP NAT + empujón flota por no-failback + prueba OTA + `.200` queda de respaldo → L1/L2 separadas D-s231-D + rollback en minutos) · 3 scripts nuevos `pwa6/scripts/` (semilla R6) · PWA **CERO cambios src/** · núcleo: NO · TD-s231-1 NUEVA (claves en chat → hilo seguridad) · TD-s229-1 confirmada 3ª vez (definitions truncado en sandbox) · TD-s203-1 gana reverso (BOM veneno en JSON de config) · verde formal + commit pwa6 PENDIENTES (arrastre s229/s230 + s231) · `C:\lru\docker` sin versionar CRECE · frente s232 = **CUTOVER (checklist listo)** | flota escalonada (#3) | capabilities S3

* **s230 · 2026-07-15** — **criterio de salud + rollback automático (#5) en AMBAS clases · VALIDADO en placa con prueba negativa real · + alfiler de resiliencia de comunicaciones** · **rpc1cl 0.230.1**: prueba de salud app-level en `ota1.c` (~150 lin · marcador **NVS** namespace ota1 `trial/tver/boots/rbver` · D-s230-A copia única inmune a copy_fs) · arma en `ota1_finish` Y `ota1_swap_boot` (D-s230-C) · `ota1_trial_boot_check()` desde app_init (boots≥2 → rollback inmediato · D-s230-B) · timer 120 s → `ota1_rollback_now()` (set_boot al hermano SIN copia fs · sin hermano → prueba abandonada, mejor sin validar que ladrillo) · CONNACK diferido 1 s limpia o publica `rolled_back` pendiente (rbver sobrevive doble reboot) · app-level porque bootloader mgos 2.20 sin APP_ROLLBACK_ENABLE (crash pre-init mitigado por esp_ota_end+sha s229) · **0.230.2 QUEMADA** = prueba negativa (broker 10.255.255.1 + mqtt1 off · **D-s230-D: bin FUERA de convención `rpc1cl-test.bin`, el manifiesto nunca ofrece veneno** · flasheo por URL manual) · **0.230.3** limpio publicado · **S3 0.230.4**: auditoría reveló rollback nativo s226 casi completo — hueco único = imagen ZOMBI (sana sin broker → PENDING_VERIFY corriendo para siempre, el bootloader solo actúa en el siguiente reset) → `esp_ota_port_health_arm()` + timer 120 s → `mark_app_invalid_rollback_and_reboot()` + **detección ampliada ABORTED‖INVALID** (D-s230-F, sin ella el rolled_back del watchdog no se publicaba) · swap cubierto gratis (NEW→pending-verify) · **VALIDADO por Manuel**: `ble_A92418` ping-pong con prueba superada + **negativa completa** (backoff → rollback solo a 120 s → 0.230.1 publica rolled_back → PWA lo pinta ↩) + 0.230.3 final · `s3_AC89D4` 0.227.3→0.230.4 con watchdog armado pre-wifi y validación al broker (primera OTA ya lo ejercita, sin transicional) · **alfiler NUEVO** `lru_alfiler_resiliencia_comunicaciones_s230.md` (postulado «ningún fallo único de comunicaciones debe suponer una situación crítica» · sustrato ya existente: doble broker AMBAS clases + BLE GATT clásico + AP emergencia · huecos: PWA sin failover + BLE sin camino + capa no observable · F1-F5 × L0-L4 · R0-R6 con triggers · 5 D-abiertas) + **T-S230-RESILIENCIA-COMS** umbrella · hilo seguridad nombrado sin abrir · PWA **CERO cambios src/** · núcleo: NO · versiones quemadas 0.230.1..4 (regla intacta) · TD-s230-1 NUEVA (transicionales flota pre-s230 → motivo para #3) + TD-s229-2 ampliada (rollback también mal etiquetado) · verde formal + commit pwa6 PENDIENTES (arrastre s229 + firmware/docs s230) · git firmware CRECE (ambos árboles suman s230) · frente s231 = **flota escalonada (#3)** | R0 alfiler resiliencia | capabilities S3

* **s229 · 2026-07-14** — **paridad OTA total clásico↔S3 (sha256-readback + verifying + app_size) + ciclo OTA observable en la PWA** · firmware rpc1cl ×3 rebanadas aditivas: **0.229.1** `ota1_start(url,version,sha256)` (sha opcional 64-hex · malformado rechaza · D-s229-A) + `ota1_sha_slot_matches()` readback mbedtls 4K del slot escrito tras esp_ota_end, ANTES de conmutar (mismatch -> error sin conmutar + cache hermano invalidada — el hueco TD-s226-1 de la S3 nace cubierto) · **0.229.2** estado `verifying` 97% (paridad S3, la PWA ya lo pintaba) · **0.229.3** `app_size`+`parti_other_app_size` via esp_image_verify SILENT (activo cacheado/boot · hermano con su identidad) -> barras bin/slot en ambas clases · **PWA**: `otaTrace.ts` NUEVO (ciclos por device alimentados desde `registryMqttHandlers` — hallazgo: el clásico bloquea el loop de mongoose en finish y los from/ota salen en RÁFAGA; muestrear hwObj.ota en render los pierde · D-s229-B · seed con timeout 10s «sin respuesta») · TabFirmware remodelado (tarjeta de ciclo con veredicto persistente cerrado por reconexión status-fresco+uptime<300 · LOG por FASES + cierre rico con desglose de tiempos · botón flashear SIEMPRE junto a Comprobar con ↻ Reflashear al día = ping-pong legítimo D-s229-D · autoversión desde nombre del bin, mata «vunknown» · Verificar respaldo/Consultar estado con ciclo acuse→resultado en LOG→timeout 5s) · **lección de entorno D-s229-C**: la cache del mount del sandbox Cowork TRUNCA ficheros crecidos (mordió 2 veces: fw.zip ilegible + TabFirmware.tsx con errores fantasma de tsc) -> verde formal/builds/commits SIEMPRE host-side · **VALIDADO por Manuel en placa real `ble_A92418`**: OTA de manifiesto 0.229.2 (ciclo 24s contado entero) → **prueba negativa sha adulterado** (descarga completa → verifying → ✕ mismatch SIN conmutar · respaldo VALID con imagen escrita) → 0.229.3 con barras en ambos slots (1.28/1.50 MB · 85%) · aditiva sin sha OK · versiones quemadas 0.229.1..3 (regla intacta) · núcleo: **SÍ** (`registryMqttHandlers` + `otaTrace.ts` NUEVO en datosLru01) · build: tsc verde host · **verde formal + commit PENDIENTES** (D-s229-C) · TD-s229-1 NUEVA (regla cache-sandbox · candidata PRINCIPIOS) + TD-s229-2 NUEVA (swap etiquetado «Flasheo» · cosmética) · TD-s218-1 gana precedente (patrón acuse/timeout) · arrastre git firmware CRECE (rpc1cl suma s229) · frente s230 = **salud + rollback automático (#5)** | flota escalonada + badge (#3) | capabilities S3 | NVS #4 sin trigger

* **s228 · 2026-07-11** — **eje OTA v2 del clasico entregado: OTA propia (`ota1.c`) sin las libs con candado de licencia + diagnostico de particiones + swap/verify + publicacion en bin crudo + trust store propio** · el «atasco HTTPS» de s227 era un **bug de tres capas** desmontado con evidencia serie: (1) `mg_rpc_callf` sin dst -> frame OTA.Update perdido en el canal MQTT («Cannot reply... no dst») -> fix RPC.LOCAL (via loopback OK) · (2) **candado de licencia** de ota-http-client («Free version can only perform OTA from mDash» · lib PRECOMPILADA — «Successfully fetched prebuilt binary» — fork imposible · arbitraje: descartadas licencia Cesanta y mDash) · (3) ca.pem 2021 no valida 2026 (`x509 -9984`) + boxdin.com:443 lo sirve EL HOSTING con **DigiCert EE-DV-G2 y cadena incompleta** (SslStream PowerShell · mbedtls no completa por AIA) · **hallazgo herramienta**: el console del mos tool esta ROTO (read err EOF sistemico · probado con blink1 minimo NUEVO y 2 ordenadores) -> **D-s228-D TeraTerm/PuTTY monitor serie estandar** · **`ota1.c/h` NUEVO** (~350 lin · rpc1cl): descarga streaming HTTP/1.1 manual mg_connect_opt+TLS(SNI+CA fichero) -> esp_ota_begin/write/end del mbuf -> **copia fs_N->fs_N** (mgos empareja fs con slot · evidencia SPIFFS @ fs_1) -> set_boot_partition -> reboot · progreso REAL por Content-Length (10..90/95/100) · watchdog inactividad 30s · cancel real · sin rollback (D-s228-F · salud=backlog #5) · **`fs/ota-ca.pem`** (D-s228-C · intermedio DigiCert hasta 2027-11 + raiz G2 + ISRG X1/X2) + **`filesystem: [fs]`** en mos.yml (clave ausente — el fs/ no se empaquetaba, costo un ciclo) · **particiones + acciones** (contrato s226 aditivo · PWA CERO cambios): printer %M de frozen en from/status (parti_running/size + flash_size + parti_other + state/version/build) · `/ota1_slots.json` persiste version de flota por slot (D-s228-E · esp_app_desc de mgos lleva 2.20.0 — inutil · slots USB = «?») · swap (salvaguarda+copia fs+conmutacion) + verify (relee hermano + republica retained) · **gen-manifest**: rpc1cl publica el **BIN DE APP CRUDO** del fw.zip (D-s228-B · esp_ota no come zips) con cerrojo sha256 vs cs_sha256 del manifest · colaterales: rpc1 compilable de nuevo (4 fixes -Werror) + blink1 NUEVO aislador · **VALIDADO por Manuel**: pruebas `ble_A931D4` ping-pong app_0<->app_1 x2 (TLS limpio · 1337312 bytes · esp_image valida · fs identico used 62248 · config/id intactos) · real `ble_A92418` **v0.228.7 por OTA DE MANIFIESTO** con panel de particiones vivo (respaldo VALID «v?» por diseno) · versiones quemadas 0.228.1..6 (regla intacta — precio del bug de 3 capas) · nucleo: NO (ni src de pwa6) · TD-s228-1 NUEVA (caducidad ancla ota-ca.pem 2027-11) + TD-s228-2 NUEVA (copia fs = snapshot vivo) · arrastre git firmware CRECE a 4 arboles · frente s229 = **sha256-readback en el clasico** (unico hueco de paridad con la S3 · la PWA ya manda el hash) | salud/rollback #5 | capabilities S3 | flota #3 | NVS #4

* **s227 · 2026-07-10** — **eje OTA v2 entregado y validado en hardware (items 1+2+6 del backlog s226) + publicacion de firmware industrializada + version de flota unificada** · rebanada A (`faa3bc4`): `scripts/gen-manifest-s227.ps1` publica S3 (build/factory.bin) y clasico (build/fw.zip) en `public/firmware/` con convencion `<clase>-<x_y_z>.bin` (s3_lcd35-/rpc1cl-) · version EXTRAIDA del artefacto compilado (esp_app_desc en 0x30 con magic byte a byte — literal hex de 8 digitos en PS 5.1 parsea Int32 NEGATIVO, cazado en vivo · manifest.json de dentro del zip) · cerrojo de incoherencia S3 (throw «recompila», demostrado 2 veces en vivo · 0.227.2 quemada sin reutilizar) · -BumpS3/-BumpRpc editan fuentes y SALEN · poda automatica (ultima version por clase · historial en git) · firmware.json con sha256 UTF-8 sin BOM · `LRU_FIRMWARE_PUBLICACION_VIVO.md` NUEVO · **D-s227-A** bins DENTRO del repo -> same-origin, CORS desaparece, bin+manifiesto+codigo mismo commit · rebanadas B+D firmware S3 (01_factory sin git): sha256 opcional en to/ota + verificacion por READBACK del slot (mbedtls 4K chunks sobre image_len_read · mismatch -> abort + error "sha256 mismatch" sin conmutar) + `{"action":"verify"}` -> esp_mqtt_port_refresh_other_slot() NUEVO (invalida cache identidad hermano + republica from/status retained) · rebanada C PWA (`50fce1b`): base canonica pwa·https = boxdin.com/pwa6/firmware · TabFirmware seccion Actualizaciones (Comprobar -> fetch manifiesto no-store+cache-buster -> cmpVer -> ⬆ disponible/al dia/downgrade ambar -> Flashear ✓sha manda {url,version,sha256}) + boton ↻ Verificar respaldo · CERO cambios types/ingesta (aditivo puro) · **unificacion version clasico**: conflicto de 3 versiones diagnosticado (APP_VERSION "2.2.0" manual vs mos.yml 1.0 vs nombres 2.1.0) -> APP_VERSION = mgos_sys_ro_vars_get_fw_version() · fuente unica mos.yml · **convencion de flota 0.<sesion>.<slice> en AMBOS proyectos** · **VALIDADO por Manuel**: manifiesto detecta -> OTA 0.226.7->0.227.1 (vieja ignora sha=aditividad) -> verify x2 -> OTA verificada por readback ->0.227.3 -> **camino negativo** (json del hosting corrompido a mano -> descarga entera -> ✕ sha256 mismatch -> arranque intacto -> verify pinta slot sucio con identidad fresca) · clasico v0.227.1 en placa · **TD-s226-1 CERRADA en su escenario real** · evidencia extra: OTA del clasico SE ATASCA contra HTTPS (frente s228 dimensionado con dato) · nucleo: NO (hw1Shared/TabFirmware solo) · 2 commits codigo + cierre · build verde (2216 + tsc + 463/463) · frente s228 = **eje OTA v2 en el clasico sin perder Mongoose** (HTTPS la incognita mayor · opcion B Caddy gana trigger · particiones/sha/swap/verify) | capabilities S3 | disponibilidad on-device | backlog OTA v2 (#3/#4/#5)

* **s226 · 2026-07-08** — sesion del eje Hw1: **la S3 gana un gestor de firmware de pleno derecho** · hallazgo inversor: la pagina Hw1 tenia TODO el contrato construido desde s43-s44 (diagnostics/health/OTA con barra/reboot) alimentado solo por el clasico — el corto era el firmware S3 · rebanadas: **A diagnostico pleno** (version/build via PROJECT_VER `0.<sesion>.<slice>` + min_heap + rssi/ssid + reboot_reason + ota_state + from/health 20s con pub_dropped + to/reboot diferido) · **B energia** (getters AXP2101 batt/mv/charging/vbus · campos opcionales · gauge Bat + seccion Energia) · **C OTA dual-bank REAL** (particiones 2x6M + otadata · UN erase-flash por cable · esp_ota_port NUEVO: esp_https_ota con progreso from/ota + cancelacion + crt_bundle TLS + ROLLBACK con criterio de salud «llego al broker» + rolled_back + idle linea base · lru_ota_overlay NUEVO: panel lv_layer_top con barra en la pantalla fisica) · **E plataforma** (parti_running/size + app_size real por esp_image_verify cacheado + flash + PSRAM + temp die + microSD · seccion Plataforma S3 con UsageBar + gauge Temp) · **s226b** (identidad del slot hermano version/build/tamano por esp_ota_get_partition_description · seccion Particiones OTA en TabFirmware con tarjeta activa+respaldo · **swap manual** {"action":"swap"} con salvaguardas y boton ⇄ = A/B de firmware desde la app · chips de base ddns·http/com·https · fmtB+UsageBar a hw1Shared · fix alpha porcentaje) · **fix carrera de arranque**: retained from/status se replica ANTES de que el hwObj dinamico nazca (primer from/events) -> descartado -> TabStatus a medias hasta pulsar Estado · remedio **cold-seed** to/status al primer events por device+sesion (hermano de sembrarActuador) · **averias con moraleja cerradas en sesion**: auto-degradacion por OTA (el hosting servia bin GEMELO viejo sin TLS con la misma version -> regla: version nueva ANTES de todo build publicado, nunca reutilizar numero · cazada por monitor serie) + el «puente http» no existia (Caddy nunca sirvio /firmware + redirect 80->443 -> canal OTA oficial = **https://boxdin.com/firmware** hosting externo · opcion B ruta LAN anotada sin trigger) · erase-otadata flash = vuelta al cable canonica · **backlog eje OTA v2 anotado sin abrir** (P11): manifiesto de versiones + SHA-256 e2e (pareja estrella) · flota escalonada · otalog NVS · salud endurecida · verify del respaldo · descartados delta y secure boot · nucleo: **SI** (types + registryMqttHandlers) · **2 commits codigo** (fd6d4fe + d5c6a57) + cierre · 01_factory ~16 ficheros + 2 componentes nuevos **SIN git** (~37 acumulados) · build pwa6 verde (npm build + tsc + 463/463 · cierra ademas el check formal pendiente s225) · **VALIDADO por Manuel**: multiples ciclos OTA https reales + swap ida y vuelta + overlay + todo el diagnostico pintando · TD-s226-1 nueva (hermano stale tras OTA fallida a medias · cierra OTA v2 #6) · frente s227 = OTA v2 (1+2) | capabilities S3 (T-S225) | disponibilidad on-device | opcion B Caddy /firmware | hermanos

* **s225 · 2026-07-04** — sesion de expansion UI de la S3 + comando local + reconciliacion PWA · la S3 gana **5 tiles LRU** (LRU_01 renombre · LRU_02 gauges lv_meter ±1g/±64dps doble escala · LRU_03 switches que COMANDAN + teclado con doble via clave=valor->cfg local / texto->from/kb1 · LRU_04 gauges FIELES al widget_gauge1 de Lcd2 por primitivas lv_canvas en PSRAM · LRU_05 panel de actuacion: LEDs + palancas bat-handle fieles a los DIF sw0/sw1 decodificados + deslizantes, todos comandando + sliders virt1/virt2) + **lru_nav** (flechas ambar semitransparentes estilo ep_nav_arrow · arbitraje: nivel de titulo) · **esp_mqtt_port**: apply_cfg_local + **set_sd_local** (F3 confirmed seq=-1 **actor local** + events inmediato · primera actuacion que nace en el device) + pub_act con actor + **virt1/virt2 como canales** (N_CH 4->6 · heredan AutoDelta/heartbeat/monitor1 gratis · patron Mus1) + fix desborde get_sensor_values · **arqueologia DIF** (formato DIF\1+w/h BE+RGB565 decodificado · sw0/sw1 = palanca aeronautica) · **pwa6 commit 652a1b5** (2 curas de integracion): F3 no solicitado (actor local/seq<0) -> **sembrarActuador** (reconcilia slot de comando huerfano · el LruSwitch no seguia la actuacion local mientras el clip si · refina destilado s222 · mismo patron que la siembra en frio, sin egreso) + **PHYSICAL_RANGES virt1/virt2 [0,100]** (la aguja recorria ~2.4% sobre el fallback 4095) · nucleo: **SI** (registryMqttHandlers + registrySensorUtils) · 1 commit codigo + cierre docs · build 01_factory verde (1 iteracion format-truncation) + **VALIDADO EN PLACA** · pwa6 validado en runtime (verde formal npm build+tsc = check arranque s226) · **VALIDADO por Manuel**: ciclo S3<->app en ambas direcciones + agujas virt a escala completa · **T-S225-CAPABILITIES-S3 nueva** (2a vez hardcode por nombre · caps de la S3 + hueco caps-antes-de-events) · TD-s225-1/2 nuevas · 01_factory: 13 ficheros mas SIN git (21 acumulados) · frente s226 = verde formal pwa6 \| eje capabilities S3 \| validacion on-device disponibilidad (heredado s221) \| expansion S3 (AXP2101/micro/backlight analogico) \| C3 en placa \| hermanos de siempre

* **s224 · 2026-07-04** — sesion de expansion de hardware: primer dispositivo LRU sobre ESP-IDF · la ESP32-S3 (Waveshare 3.5" tactil, C:\esp32\01_factory) se incorpora a la plataforma con el contrato completo · destilado GPIO (censo pin a pin · conflictos C1-C4 · commit e1f0ca1) · **esp_mqtt_port nuevo** (id s3_XXXXXX por MAC · net unificado ble D-s224 · boxdin+respaldo · LWT · auto-registro en la mesa via from/events v2 plano · regla dura: topics 4 segmentos EXACTOS · AutoDelta hyst por canal + heartbeat + rate-limit 80ms · to/cfg completo · monitor1 · from/status retained) · **eje de actuacion tonto-con-pantalla D-s224-SD** (sd1..sd4 VIRTUALES como LEDs en la lru_tile nueva · to/act sobre s216 -> F2/F3 con eco de seq · to/actstate -> siembra #fb · sd en events) · fixes: reconexion WiFi perpetua + CMakeLists a lista explicita (GLOB no recoge .cpp nuevos · CONFIGURE_DEPENDS ilegal en script-mode IDF) · destilado del puerto (lru_puerto_s3_espidf_s224.md) · pwa6 sin cambios de codigo (solo doc · commits e1f0ca1 + destilados + cierre) · **VALIDADO EN PLACA por Manuel** de punta a punta (auto-registro · aguja sigue el gesto · monitor1 · switch Cuadro01->LED de pantalla con ciclo F2/F3) · tension NET-EGRESS disuelta por D-s224 antes de registrarse · TD-s224-1/2 nuevas · 01_factory: 8 ficheros sin git · frente s225 = libre (P11) · candidatos: validacion on-device disponibilidad \| expansion S3 \| C3 en placa \| hermanos

* **s223 · 2026-07-03** — sesion de codigo + autoria Animate · **cierre end-to-end del feedback visual de switches (T-S222-FEEDBACK-VISUAL-SW)** · el switch de la sim gana **5 estados** (indet gris / off / off_pend / on_pend / on) compuestos por IB_04 en el tick (**esquema A2** · clip tonto, semantica en TS) sobre el **modelo de calidad OPC Good/Uncertain/Bad** (Good=pleno · Uncertain=pending: orden en vuelo O offline-ultimo-valor, desambiguado por el LED online · Bad=indet: desenlazado/sin dato, click bloqueado) · **4f3713a** (codigo): SW_STATE exportado (-1/0/0.25/0.75/1) + rama isSwitch del tick (change1(v,v) · skip lastRaw · desenlazado->INDET) + _composeSwitchState (sin parche->INDET · **modo Local del comando->pleno hacia el comando** · sin #fb->INDET · offline #online==0->pending hacia ultimo #fb · confirmado lastWriter==ACT_FW_SOURCE o virtual vit<0->pleno hacia #fb · eco F1->pending) + ACT_FW_SOURCE exportada (discriminante F1/F3 = lastWriter del #fb; comparar VALORES no distingue, el eco iguala al instante) + fix _mergeClipInstance lastRaw=NaN (refresco instancias fusionadas s221) + **fix LruBar** (la fila sd comanda via actuar() = comando+eco F1, no writeBySensorId 'slider' — regresion cazada en validacion; camino unico de escritura de salidas) · **arbitrajes D-s223**: modelo OPC sin mas estados · **el hw manda en reconexion** (from/events s222 reescribe #fb con act-fw -> el sw se sincroniza al estado real; re-comandar es del usuario) · **el modo arbitra la autoridad visual** (Local=comando local pleno, el hw se anota en #fb pero no se obedece · Real=#fb) · **degradacion standalone** (sin IB04Actuator -> toggle autonomo pleno; mismo codigo, tres mundos) · **autoria Animate Manuel (no git, verificada)**: Sw5Det_sw1 5 frames+labels + frame_0 canonico (guard __swInit · gotoAndStop('indet') inicial · change1 por rangos · Click1 toggle desde _estado + standalone + pending optimista solo con estado conocido) + 3 SVGs (pending ambar On?/Off? + indet gris) + **fix test1 Cuadro01** (gotoAndStop por indice->por label; el 1 era off_pend tras los 5 frames) · **3007fee** doc patron simbolo (`lru_patron_simbolo_actuador_digital_s223.md`: OPC + SW_STATE + dos canales + compositor + contrato clip + checklist + anti-patrones) · **1a0a1c3** doc modelo contenedor (`lru_panel_switches_cuadro01_s223.md`: anatomia + manifest ib04 + ciclo de vida + Debug1 live cmd/fb + test sweep + receta panel nuevo SIN TS) · nucleo: **SI** (app InstrumentBridge_04/actuadorWrite/ComponenteLru) · **4 commits pwa6** (4f3713a + 3007fee + 1a0a1c3 + cierre docs) · build verde 2216 + 463/463 + tsc limpio · **VALIDADO en 3 entornos** (app enlazado · app desenlazado · standalone) · **T-S222-FEEDBACK-VISUAL-SW CERRADA** · **T-S220-FLAG** sin cambios (validacion on-device disponibilidad sigue pendiente · Animate Manuel) · **TD-s223-1** nueva (lag repintado tras test sweep · baja P11) · frente s224 = validacion on-device disponibilidad (heredado s221) + Virtual remoto -3 (tag isFromSim) + LruSwitch estados visuales (sin trigger) | F3<->applyActuatorFault (s79) | RF-3b (PWM/RGB) | migrar mesa al sobre (TD-s217-1) | gatear auto-consulta (TD-s218-1)

* **s222 · 2026-07-01** — sesion de codigo · **cierre end-to-end del actuador-en-mesa (T-S221) + eje nuevo actuador×modo (T-S222-ACTMODO)** · los sdN (salidas del ESP32) pasan a puertos de primera clase: visibles en hw1, parcheables por la mesa, comandados por los switches con **modelo B** (la mesa es la autoridad · '----'=sin salida) · **firmware v2.2.0** (Manuel · no git · main.c/main.h): los sd1..sd4 viajan en el mismo from/events que los sensores (decl act_pin_map/s_act_pins arriba + buffer 560->640 · APP_VERSION 2.1.0->2.2.0) · **a0666325** (visibilidad+#fb): registrySensorUtils (esTipoSalida + buildCfg1Entry fija dir:'out' a sd*/pwm/dac · excluye botones sw*/swid* que son sd pero ENTRADAS) + registryMqttHandlers (salidas reconciliadas al canal #fb via confirmarActuador act-fw, no al slot de comando) · **42aec212** (R1): useIB04Registry · panel solo-switches entra al ORC (revierte skip s215) con swN (C010..C013) como canales -> fila en la mesa · **01bc5269** (R2): SwObj.linkDir + MatrixCell/helpers filtran puertos por cfg1.dir vs linkDir del swObj + auto-map posicional swN->sdN · exclusividad N:1 verificada sin codigo (parte c = no-op) · **47e90b2b** (R3 modelo B): parcheo dinamico switch->sdn · el panel nace auto-enlazado a sus puertos estaticos · '----'=sin salida · actuarSwitch(swObj,switchName,val) escribe solo el puerto parcheado (sin fallback estatico) · rebindFromEnlace re-apunta o desconecta la lectura · _switchPatch por (swObj,switchName) · wiring DI setSwitchActuar sin ciclo · clip Animate mousedown -> IB04Actuator.actuarSwitch(this.parent.ib04.swObj, this.name, nuevoValor) · **bdddfad7** (T-S222-ACTMODO): DataPlane.writeBySensorId no gatea por el eje modo un canal actuador (dir out/bidi · es comandado, el modo gobierna el egress no la escritura) + actuadorAdapter suprime el egress to/act en modo Local (ensayo solo-sim) · Real/mode-less egresan · countdown Local->Real ya cableado · principio: eje actuacion (salida, comandada) ortogonal al eje modo (entrada, arbitrada) · **feedback visual de switches** documentado (lru_feedback_visual_switches_s222.md · para s223) · nucleo: **SI** (firmware main.c/main.h + app registrySensorUtils/registryMqttHandlers/useIB04Registry/InstrumentBridge_04/actuadorWrite/types/MatrixCell/helpers/DataPlane/actuadorAdapter) · **5 commits pwa6** (a0666325 + 42aec212 + 01bc5269 + 47e90b2b + bdddfad7) + firmware · build verde 2216 + 463/463 + tsc limpio · **VALIDADO on-device** (switches comandan sdN · '----'=off · Local=sim/Real=hw · countdown revierte) · **T-S221-ACTUADOR-EN-MESA CERRADA** · **T-S222-ACTMODO cerrada** · **T-S222-FEEDBACK-VISUAL-SW nueva** (documentada) · **T-S220-FLAG** sin cambios (sigue pendiente validacion on-device del eje disponibilidad · Manuel Animate) · frente s223 = feedback visual switches (spec) + validacion on-device disponibilidad (heredado) + Virtual remoto -3 (tag isFromSim) | F3<->applyActuatorFault (s79) | RF-3b (PWM/RGB) | migrar mesa al sobre (TD-s217-1) | gatear auto-consulta a fisicos (TD-s218-1)
* **s221 · 2026-06-30** — sesion de codigo (eje disponibilidad/flag · T-S220-FLAG-DISPONIBILIDAD avanzada) + analisis (sin codigo) del eje actuador-en-mesa + resolucion de la colision Cuadro01/Cuadro02 (Animate) · **rebanada flag materializada**: la disponibilidad deja de ser booleano del ORC y pasa a **senal continua de vitalidad 0..1** bindable en el DataPlane · **a1** (`d2854951`): liveness booleana por latido + retain de comms · **a2-continua + b + vitalidad** (`2c0c9917`): `disponibilidadCanal.ts` NUEVO (motor de vitalidad · canal `{hwObjId}/#online` dir:in 0..1 · 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 · gaps>90s ignorados como reconexion · K=3/MIN=8s/MAX=70s/DEFAULT=30s) + `registryMqttHandlers` (timer offline con `ventanaDe` adaptativa en vez del fijo 40s) + `ObjectRegistryContext` (retira el espejo booleano · motor = autoridad unica del #online) + `InstrumentBridge_04` (binding de indicador · rail b · indicadores entran como agujas read-only) · **C+D** (`f36ca800`): indicador online dinamico — `IndicatorManifest.follows?` (source opcional) · el bind distingue **follows** (arranca virtual dpIndex -1 valor -2 · `rebindFromEnlace` re-apunta a `{hwObjId}/#online` al enlazar y a -2 al desenlazar · sigue la mesa como las agujas) de **source estatico** (Cuadro01/02) · el tick lee **CRUDO** en la rama indicador (sin clamp/epsilon · skip por crudo) para no fundir -2 Virtual y 0 Ofline en pct=0 · sentinelas VIT_VIRTUAL_LOCAL=-2 / _REMOTE=-3 (reservado) / VIRTUAL=-1 + `sembrarVirtualLocal` · `ObjectRegistryContext.enableEmission` siembra -2 (Virtual local) en el #online del hwObj virtual · **contrato de sentinelas**: val 0..1 vitalidad real (verde>=0.66 / ambar 0.33-0.66 / rojo<0.33 -> Online/Rancio/Ofline + latido) · val=-1 Virtual / -2 Virtual local / -3 Virtual remoto (RESERVADO · sim remoto llega como fisico por MQTT, no separable hoy sin tag `isFromSim`) · **resolucion colateral** colision Cuadro01/Cuadro02: eran ambos `new lib.Cuadro1()` compartiendo `lib.Cuadro1Det` -> un solo swObj (fan-out merge s208) · Manuel creo **dos simbolos distintos** (`lib.Cuadro01`/`lib.Cuadro02` con Det propio y swObj propio) -> dos swObj independientes · ambos a `ble_A92418/sd1..sd4` (N:1 intencionado · un puerto de salida comandado por botones de varios swObj) · una primera vuelta solo volteo la colision (desaparecia Cuadro01) · Claude lo cazo · **analisis (sin codigo) actuador-en-mesa**: las listas de puertos salen de `hwObj.hw1` (MatrixCell L69 · ComponenteLru L704) que se puebla con `from/events`; los `sdn` (salidas) no van en `from/events` -> invisibles/no parcheables · plan Manuel (validado): firmware manda los `sdn` en el mismo `from/events` + registrarlos (la maquinaria YA fusiona puertos nuevos: `actualizarhw1ToSw` crea entrada · `inferSensorType` mapea sd*->sd/1bit · registra canal DataPlane · sincroniza cfg1) · **hueco concreto**: `buildCfg1Entry` (registrySensorUtils L170-184) NO fija dir -> sdn quedan dir:undefined · necesita dir:'out' para sd/pwm/dac (TD-s221-1) · nucleo: **SI** (app · disponibilidadCanal NUEVO + registryMqttHandlers + ObjectRegistryContext + InstrumentBridge_04) · **3 commits pwa6** (d2854951 + 2c0c9917 + f36ca800) · build verde 2216 + 463/463 + tsc limpio · **grueso del eje SIN validar on-device** (pendiente: Manuel aplica manifests `follows` en agujas + rama virtual de `change1` del clip Info1_Online) · **T-S220-FLAG** AVANZA · **T-S221-ACTUADOR-EN-MESA** nueva (salidas en hw1 / switch->sdn / buildCfg1Entry dir) · **TD-s221-1** (buildCfg1Entry dir) + **TD-s221-2** (perillas vitalidad sin calibrar) nuevas · frente s222 = buildCfg1Entry dir:'out' + frente actuador-en-mesa (salidas visibles via from/events + parcheo dinamico switch->sdn + eximir exclusividad out + filtro por direccion) + validacion on-device + Virtual remoto -3 (tag isFromSim) | reconciliar F3<->applyActuatorFault (s79) | RF-3b (PWM/RGB) | migrar mesa al sobre (TD-s217-1) | gatear auto-consulta a fisicos (TD-s218-1)
* **s220 · 2026-06-27** — sesion mixta (codigo + diseno + validacion on-device) · **T-S219-ESTADO-INICIAL-SW-FRIO cerrada (caso online) + destilado del eje confianza/flag + paridad debug del panel de switches** · **readback de estado en frio `to/actstate`** (mecanismo A arbitrado · siembra AMBOS canales: comando `act-rx` + `#fb` `act-fw`): firmware (`main.c`) campo `double state` en `struct act_pin_map` (sombra del ultimo `val_exec`) + `mqtt_pub_actstate`/`handler_mqtt_actstate` (`to/actstate`->`from/actstate` QoS0 sin retain · molde actlog) + sub · **NO `mgos_gpio_read`** (pad de salida ESP32 con input deshabilitado -> sombra software, ademas semantica T-S219 = «el latch que escribimos», no medida independiente = el `feedbackSensorId` hermano) · app 3 ficheros: `types.ts` (topics + `HwObjActstateResponse`/`ActstatePort` · ediciones ASCII puro por mojibake) + `actuadorWrite.sembrarActuador` (comando `act-rx` suprimido por el adaptador + `#fb` `act-fw` · sin egreso · expuesto en `IB04Actuator.sembrar`) + `ObjectRegistryContext.sendActstate` (molde `sendActlog` request/reply · sin sub permanente) + efecto de siembra en el **edge offline->online de hwObjs fisicos** · `mos build` verde (fix `-Werror=missing-field-initializers`: init de los 4 literales `s_act_pins[]` con `state=0.0`) · app verde 2215 + 463/463 + tsc limpio · **VALIDADO ON-DEVICE (online)**: SW reflejan el latch real tras recarga en frio con hw online; con hw apagado retienen el ultimo valor en memoria · **hueco abierto: caso OFFLINE** (sin firmware vivo no hay readback · la sombra es RAM del MCU) -> rebanada de estado retenido (s221) · **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 **nucleo neutro** del que la avionica (Flag/BITE/CMS) y la industria (OPC/SCADA) son **proyecciones** (reencuadre Manuel · «anadir lo mejor de muchos mundos, reales o virtuales») · escala procedencia->confianza (medida/latch-vivo/ultimo-conocido/comandado/desconocido) · validez!=salud (flag!=BITE · `from/health` ya es BITE-embrion) · frontera **BITE corto plazo / CMS largo plazo VALLADO** (correlacion patch-bay sin trigger) · 2o dominio de contraste = BITE (arbitrado) · un registro inyectado+real · calidad canal/dato (lean canal) · frases de Manuel verbatim (Patron 3) · **hallazgo on-device**: el LED online del LruBar va bien (lee `isOnline` del ORC) pero el de la **animacion** (`ControlOnline1` en Cuadro01/02) no — nunca cableado a disponibilidad (IB_04 solo tiene agujas+switches · no binding de indicador) -> **T-S220-FLAG-DISPONIBILIDAD** nueva (umbrella del eje availability/flag) · paridad debug del panel de switches: `frame_0` de `Cuadro1Det` con menu + datos en vivo (`leer`/`leerFb`) + test visual de switches (el `test1` compartido `Info1_id` es needle-only · `Object.keys(needles)` peta con switches -> se deja inerte sin `_instrumento`) -> `Cuadro1Det_frame0.js` (autoria Manuel · despliegue+datos validados) · nucleo: **SI** (app types/actuadorWrite/ObjectRegistryContext + firmware main.c) · **2 commits pwa6** (codigo del eje + doc del destilado/living-docs) + firmware por via de Manuel · **T-S219 cerrada (online)** · **T-S220-FLAG** nueva · frente s221 = **rebanada flag** (disponibilidad-senal `{hwObjId}/#online` + cablear `from/status`->isOnline + binding indicator IB_04 + estado retenido firmware cierra offline + manifest Manuel) | reconciliar F3<->`applyActuatorFault` (s79 · feedbackSensorId = nivel MEDIDA) | RF-3b (PWM 25 + RGB 26) | migrar mesa al sobre (TD-s217-1) | gatear auto-consulta a fisicos (TD-s218-1) | getSwitchInfo + test1 switch-aware
* **s219 · 2026-06-26** — sesión de código (firmware) · **RF-3 · drive físico real del eje de actuación** — el firmware mueve **salidas digitales GPIO reales** al recibir `to/act` (deja de ser tonto en ejecución · sigue tonto en autoridad) · **VALIDADO ON-DEVICE** (1ª vez que el eje se cierra con hardware real · los 4 puertos conmutan al mover los SW) · **mos.yml**: bloque `board.act.sd1..sd4` (`pin` 19/23/18/16 + `active_high` true por puerto · `pin=-1` deshabilita · patrón `board.led1`/`board.btn2` — redefinible/deshabilitable sin recompilar) · **main.c tabla**: `struct act_pin_map{port,pin,active_high}` + `s_act_pins[4]` + `act_entry_for` (lookup `strcmp`) · **main.c drive** (en `handler_mqtt_act`): `mgos_gpio_write(pin, active==active_high)` (la igualdad invierte el nivel eléctrico para relés active-low sin ramas) · F2 = comandado · **F3 = estado lógico ejecutado** (`val_exec` 0.0/1.0 · latch real) · **main.c init**: relleno desde getters `mgos_sys_config_get_board_act_sdN_*` + `mgos_gpio_setup_output(pin, !active_high)` (reposo inicial según polaridad — los active-low no arrancan activados) · mapeo Manuel: VSPI clásico libre (SPI activo en 14/17/12/13) · **pin 22 evitado** (era `board.led1.pin` heartbeat · Patrón 6 auditoría empírica) · **sd4 5→16** (16 sin strapping vs 5 strapping pin) · decisiones: alcance v1 **solo digital** (PWM pin 25 + RGB pin 26 → **RF-3b** sin abrir · P11) · pin+polaridad en config no hardcoded · F3 lógico no eléctrico · `mos build` verde · **app SIN tocar** (contrato `from/act`→`#fb` de s218 intacto · RF-3 solo cambia *qué valor* lleva F3) → **sesión firmware-only · sin commit pwa6 · cierre solo-documentación** · **T-S212** firmware listo en ejecución, tonto en autoridad (auth dormido) · **T-S213-CANAL-DIRECCION-SWOBJ** `#fb` recibe F3 con latch real · **TD-s217-1** aprieta a baja→media (la mesa en plano no mueve hardware real · el firmware ejecuta solo el sobre) · firmware fuera del git de pwa6 (versionado por Manuel) · frente s220 = **reconciliar F3↔`applyActuatorFault`** (s79 · `feedbackSensorId` · necesita un sensor de realimentación independiente — F3 digital lee el latch, no mide) \| **RF-3b** analógico (PWM/RGB) \| migrar mesa al sobre (TD-s217-1) \| gatear auto-consulta a físicos (TD-s218-1) \| T-S214-BINDING \| Rebanada C salida \| T-S213-VISORES \| T-S182 \| T-S183 R5.5
* **s218 · 2026-06-26** — sesión de código · **bloque FIRMWARE del eje de realimentación + cierre del lazo F2/F3 end-to-end + minilog `from/actlog` (firmware y consumidor app)** · hallazgo: el firmware **no escuchaba `to/act`** (el eje s211→s217 era app-only) · **nuevo acceso MCP** `…/rpc1cl` (firmware Mongoose OS · C · fuera del git de pwa6) · **RF-1** firmware (`main.c` · `mos build` verde): `handler_mqtt_act` (sub `ble/<id>/to/act` · parsea el sobre · emite `from/act` F2 `received`/F3 `confirmed` tonto · Fork 2) + `mqtt_pub_act` (ecoa `seq`) · **RF-2** app (`ce682a9` · 2213 · 463/463): `parseFromActTopic` + `createFromActIngestHandler` (escribe `#fb` en F3 · source `act-fw` · corrige el eco optimista F1) + 7ª suscripción ORC · **RF-4** firmware (`main.c` · `mos build` verde · **cierra Fork 3**): ring `s_act_log[16]` + `act_log_push` + `mqtt_pub_actlog`/`handler_mqtt_actlog` (`to/actlog`→`from/actlog` · anillo entero cronológico) · Fork 3 arbitrado N=16 · entrada `{port,val,ts=meta.ts double,seq,actor}` · **RF-5** app (`75335ac` · 2215 · 463/463): pestaña **Minilog** en Hw1AdminPanel · `sendActlog` request/reply (molde `sendQuery`) + `useHwActlog` + `TabActlog` + topics/tipos + `Tab`/`TABS` · núcleo: **SÍ** (app types/registryMqttHandlers/ObjectRegistryContext/Hw1AdminPanel + firmware main.c) → core 3ª consecutiva · **NO validado on-device** (falta flashear) · **T-S212** firmware tonto materializado (auth dormido) · **T-S213-CANAL-DIRECCION-SWOBJ** `#fb` recibe F3 real del firmware · **T-S209-EGRESS-MESA** la vuelta gana firmware · **TD-s217-1** reforzada (firmware nace parseando el sobre · ignora plano con warn) · **TD-s218-1** nueva (auto-consulta Minilog/Status timeout en virtuales) · frente s219 = **RF-3 drive físico real** (necesita pines de Manuel) + reconciliar F3↔`applyActuatorFault` (s79) \| migrar mesa al sobre (TD-s217-1) \| gatear auto-consulta a físicos (TD-s218-1) \| T-S214-BINDING \| Rebanada C salida \| T-S213-VISORES \| T-S182 \| T-S183 R5.5
* **s217 · 2026-06-23** — sesión de código · **materialización del bloque app-only del eje de realimentación de actuación (R-A→R-D · destilado s216)** · cierra el síntoma de s215 (los SW no sincronizan **entre instancias**) en loopback, **sin firmware** · **validado en vivo (cross-pestaña · los estados se transmiten)** · **R-A** (`61ecc94`): `actuadorAdapter._emitAct` envuelve `to/act` en el sobre `{ver:1,port,val,meta:{ts,seq,kind,actor:{id}}}` (§3.1 · `auth` no se emite) + `getActorId`/`getDeviceType` en `identity.ts` · **Mus1** (`61c0110`): `extractPortValue` lee el sobre (consumidor de R-A · `to/act` dejó de ser plano) · **R-B** (`ccd6084`): canal de confirmación `{hwObjId}/{puerto}#fb` `dir:in` (no egresa · §6) + API `confirmar`/`ensureFb`/`leerFb` · **R-C** (`8dc433e`): `createActIngestHandler` suscrito a `to/act` en el ORC — salta eco propio por `meta.actor.id`, ingiere (`ingerirOrden`: comando `act-rx` → mueve la aguja-switch + confirmación F1) · `actuadorAdapter` **suprime** el re-egreso de `act-rx` (sin ping-pong) → **cierra el síntoma de s215** · **R-D** (`f59b626`): `SwitchManifest.status?` → IB_04 ata una **aguja lectora de `#fb`** (deriva la clave del `actuator` · cero cambios en el tick) · **actuar #fb** (`2aeb82c`): `#fb` optimista en el originador (simetría con los peers) · **actor per-pestaña** (`8705d2e`): `getActorId` per-runtime in-memory (no per-localStorage · destilado §3.2 «esta pestaña concreta») — sin esto dos pestañas del mismo navegador compartían `actor.id` y no sincronizaban · build verde **2213** + **463/463** + tsc limpio (cada rebanada) · núcleo: **SÍ** (identity/actuadorAdapter/actuadorWrite/registryMqttHandlers/ObjectRegistryContext/InstrumentBridge_04 + Mus1) → core 2ª tras el paréntesis de diseño s216 · **T-S209-EGRESS-MESA** lazo de realimentación cerrado app-side · **T-S212** F1 cross-instancia ya real (auth dormido) · **T-S213-CANAL-DIRECCION-SWOBJ** canal `#fb` `dir:in` (1er uso real de entrada en actuador) · **TD-s217-1** nueva (`to/act` formato dual sobre+plano) · frente s218 = **bloque firmware** (`from/act`+`phase` F2/F3 · `from/actlog` · `applyActuatorFault`) \| T-S214-BINDING \| Rebanada C salida \| T-S213-VISORES F2 \| T-S182 \| T-S183 R5.5
* **s216 · 2026-06-20** *(repuesta en s217 · el cierre s216 omitió el alta mecánica en §B)* — sesión de diseño (articulatoria, sin código) · **diseño completo del eje de realimentación de actuación** (cierra el lazo abierto del eje de actuación de s215) · destilado anillo 2 `lru_eje_realimentacion_actuacion_s216.md` · el **sobre** (payload + metadatos `ts`/`seq`/`kind`/`actor`/`auth`) · **4 fases** (F0 emitida→F1 compartida→F2 ack→F3 confirmada) · topics `to/act`/`from/act`+`phase`/`from/actlog` · **4 precedentes que EXTIENDE** (sobre de `from/events` · lazo monitor1 · `feedbackSensorId` s79 · telemetría `from/status`/`health`) · forks arbitrados (1 por-orden · 4·A aguja de estatus · 2/T-S212 «firmware tonto ahora, autoridad después» · 3 minilog abierto) · §6 comandado≠confirmado (canales distintos · `register` primero-gana) · build verde heredado **2213** + **463/463** + tsc limpio · 0 código (1 `.md` nuevo) · **T-S212** gana documento de diseño + arbitraje «firmware tonto ahora» · **T-S209-EGRESS-MESA** + **T-S213-CANAL-DIRECCION-SWOBJ** nutridas · frente s217 = materializar bloque app-only R-A→R-D
* **s215 · 2026-06-20** — sesión de código · **eje de actuación de switches (SW) para IB_04** · los SW de un clip Adobe Animate (despachaban al RouterService retirado en s190 R5.5) pasan a **escribir actuadores** por el eje vivo — complemento de las agujas que leen sensores · modelo cristalizado: **un SW es una aguja que además escribe** · la verdad escribible es el **canal del puerto actuador `{hwObjId}/{puerto}`** (no una señal por-swObj · da sincronía cross-panel) · **A** (`99858e3`): `ChannelMeta.dir?:'in'|'out'|'bidi'` (core aditivo) + `actuadorAdapter` 2º disparador (emite `to/act` por `dir:out`, además de las aristas) · **B** (`c98ff1b`): fichero nuevo `actuadorWrite.ts` (`ensureActuadorCanal` registra el canal OUT `dir:out` 0..1 · `actuar` source `'switch'`) + `window.IB04Actuator` cableado en el boot del ORC · validado runtime (`to/act {"sd1":1}` en Analyzer1 · offline OK) · **C** (`b7806b9`): `ClipManifest.switches?` + `needles?` opcional · `_createBinding` enlaza cada SW como **lector** de su canal actuador (en `needles[]` → tick sin cambios) → sincronía cross-panel por mismo `dpIndex` · `useIB04Registry` salta el ORC para paneles solo-switches (invisible) · **D** (sin commit al repo): migración del clip `Cuadro1Det` en Animate (`_this.ib04.switches` + `IB04Actuator.actuar` en mousedown · `change1` autosuficiente) + guía HTML rev2 (deliverable suelto) · 2 bugs del clip cazados (binding del mousedown comentado + `change1` con vars muertas) → **TD-s215-1** (las guías de migración marcan qué líneas SE CONSERVAN) · validado end-to-end (clic mueve SW · `to/act` sale · read-back sincroniza) · build verde **2213** (+1 `actuadorWrite`) + **463/463** + tsc limpio · núcleo: SÍ (DataPlane/actuadorAdapter/actuadorWrite[nuevo]/ObjectRegistryContext/InstrumentBridge_04/useIB04Registry) → **corta la racha solo-sync-lines de s214** (TD-s205-1) · **T-S213-CANAL-DIRECCION-SWOBJ** parcialmente saldada por otra vía (canal de puerto OUT, no señal de swObj · emisor de primera clase sin construir) · **T-S212-ACTUADOR-AUTORIZACION** confirmada/diferida (`to/act` de SW mode-less sin árbitro) · **T-S213-MESA-BANCO-DIAGNOSTICO**: sustrato de la Rebanada C ya existe · **TD-s215-1** nueva + 2º disparador anotado en **TD-s212-1** · frente s216 = T-S214-BINDING \| T-S212 \| Rebanada C salida \| T-S213-CANAL-DIRECCION-SWOBJ \| T-S213-VISORES F2 \| E4.3 \| T-S182 \| E5 firmware \| T-S183 R5.5 \| TD-s205-1
* **s214 · 2026-06-17** — sesión de código · **Frente 1 de T-S213-MESA-BANCO-DIAGNOSTICO** (la celda `🔗 n/n` del EnlacesPanel como banco de parcheo + diagnóstico) · todo en `src/components/EnlacesPanel/MatrixCell.tsx` (0 núcleo) · **Rebanada A** (`7051c6a`): diagnóstico de solo-lectura por fila del editor portal — valor en vivo (cascada **puerto-primero**: canal `{hwObjId}/{puerto}` → señal upstream `signalRoute` → `hw1.puertoValor`) + marca de dirección `cfg1[puerto].dir` (E6 · `out` resaltado, `in` atenuado) + tag de modo per-señal (ControlBus) + marca de stale (`lastWriteAt`) · refresco ~5fps `useReducer` vanilla · causa-raíz del «congelado» diagnosticada (semilla `Real` al enlazar + gate bloquea la fuente del músico) · cascada invertida a puerto-primero tras feedback runtime · **cierra mitad-UI de TD-s211-2** · **fix reposición** (`5e8ca36`): `useLayoutEffect` mide el rect real del editor y reajusta dentro del viewport (las filas ensanchadas recortaban por el borde derecho) + `EDITOR_W` 280→320 · **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` (lego compartido) · mirror del Mixer (`handleChangeSliderhw1Sw`): fija modo **`Local`** + `startSignalCountdown` (10s→`Real`) + `writeBySensorId('slider')` · en `Local` el gate bloquea las otras fuentes → el valor se mantiene 10s · **solo entrada** (NO `to/act`) · validado runtime (aguja sigue el pulso · modo vira a `Local`) · build verde **2212** + **463/463** + tsc limpio (×3 · chunk nuevo `SignalCountdownRing`) · núcleo: NO (solo `MatrixCell.tsx`) → **racha de core s210-s213 cortada** (solo-sync-lines · TD-s205-1) · **T-S213-MESA-BANCO-DIAGNOSTICO** Rebanadas A+B cerradas (C salida diferida atada a T-S212 · banco bidireccional depende de T-S213-CANAL-DIRECCION-SWOBJ) · **TD-s211-2 mitad-UI CERRADA** · **T-S214-BINDING-MANUAL-SIGNALROUTE** nueva (informativa · `signalRoute` solo auto-enlace + semilla `Real` muda señal de músico) · **TD-s214-1** nueva (estilos inline de la celda → CSS) · frente s215 = Rebanada C \| T-S214-BINDING \| T-S213-CANAL-DIRECCION-SWOBJ \| E4.3 \| T-S182 \| E5 firmware \| T-S183 R5.5 \| TD-s205-1
* **s213 · 2026-06-16** — sesión de código + diseño · **E4.2 del eje de actuación** (mini-mesa) + **E6 fontanería** (`dir`→`cfg1`) + ronda de diseño de la UX de la mesa (3 frentes) + cristalización del swObj emisor · **E4.2** (`03650e4a`): tab «Actuar» del HwObjPopup (physical-only) reescrito como **mini-mesa** — fila por puerto con selector de señal fuente (`listChannels` sim/hw) + valor en vivo (~5fps) + desenlace · `enlazar`/`relevar`/`desenlazar` sobre `enlacesActuador` (E4.1a) → `actuadorAdapter` (E4.1b) → `to/act` · relevo **last-write-wins + toast** (sin modal) · pulso manual s211 baja a secundario · SIN persistencia (E4.3=no-retorno) · valor CRUDO (T-S182) · refresco vía `useReducer` (vanilla) · validado runtime (N1_L mueve leds del músico) · 1 fichero + CSS · **E6 fontanería** (`b58fbd19`): `CanalConfig.dir?` en types.ts (core aditivo · ASCII por mojibake) + `buildCfg1FromCapability` propaga `dir` (spread condicional) → `dir` sobrevive al cfg1 = **mitad-datos de TD-s211-2 CERRADA** · consumo UI plegado al Frente 1 · **diseño** (sin código): edición de enlaces se mueve a la pantalla de la celda `🔗 n/n` (editor portal ya existe) + 3 frentes · decisiones: lista única con filtro de dirección (no pestañas) · inyector de test con auto-stop 10s = límite de seguridad interino de la autorización permisiva · entrada vía ControlBus / salida vía `to/act` · **cristalización** Manuel «swObj produce un valor propio que se publica como señal nueva en el DataPlane» (emisor de primera clase vía `emitsAs`/Gen · `dir` faceta del canal swObj) · build verde **2212** + **463/463** + tsc limpio (×2) · núcleo: types.ts (`CanalConfig.dir` aditivo) + registrySensorUtils (`buildCfg1FromCapability`) · 4ª sesión consecutiva de core (TD-s205-1) · **T-S209-EGRESS-MESA**: E4.2 cerrada + E6 mitad-datos · próxima E4.3 (no-retorno) · **T-S213-CANAL-DIRECCION-SWOBJ** nueva (swObj emisor) + **T-S213-MESA-BANCO-DIAGNOSTICO** nueva (F1) + **T-S213-VISORES-NO-ANIMATE** nueva (F2+F3) · **TD-s211-2** mitad-datos cerrada/mitad-UI a F1 · **TD-s213-1** nueva (pulir UI mesa) · frente s214 = E4.3 \| T-S213-MESA-BANCO-DIAGNOSTICO \| T-S213-CANAL-DIRECCION-SWOBJ \| T-S182 \| E5 firmware \| T-S183 R5.5 \| TD-s205-1
* **s212 · 2026-06-15** — sesión larga: regen del índice (lean) + **E4.1 del eje de actuación** (1ª materialización del eje · alfiler s210 · 3 átomos) + **renombre egress→actuador** + barrido docs · **E4.1a** (`91592b2`): modelo `enlacesActuador` (singleton hermano de signalRoute · `_porPuertoOut` 1:1 exclusividad estructural=inv.1 + `_porSenal` 1:N fan-out · `enlazar` last-write-wins devuelve desplazada · +10 tests · D-abierta-2=B estructura hermana) · **E4.1c** (`f3bd33a`): `HwObj.lastChannel` (core types.ts aditivo) + handler from/events lo guarda → **cierra prerequisito TD-s211-1** · **E4.1b** (`26f73c0`): `actuadorAdapter` (singleton suscrito a DataPlane '*' · push on-change 20fps · D-abierta-3=sí → `to/act` por lastChannel · valor CRUDO · `window.ActuadorDebug`) + wiring ORC (resolver de canal inyectado + start/stop + reset) · **renombre** (`8c67bc4`): cristalización Manuel «el actuador convierte señales eléctricas en señales del mundo físico» · enlacesEgress→enlacesActuador · egressAdapter→actuadorAdapter · EgressDebug→ActuadorDebug · «eje egress»→«eje de actuación» · to/act sin tocar · renombre puro · **docs** (`7de1389` índice término+regen + `38449308` alfiler nota de renombre) · build verde **2212** + **463/463** + tsc limpio · núcleo: types.ts (`HwObj.lastChannel` aditivo) + registryMqttHandlers + ObjectRegistryContext (wiring) · **T-S209-EGRESS-MESA**: E4.1 (modelo+lastChannel+adaptador en memoria) cerrada · próxima E4.2 UI + E4.3 persistencia (no retorno) · renombrada a actuación · **TD-s211-1 CERRADA** (lastChannel) · **T-S212-ACTUADOR-AUTORIZACION** nueva (la salida sin árbitro tipo ControlBus · to/act sustrato abierto) + **TD-s212-1** nueva (throttle 20fps adaptador) · frente s213 = E4.2 \| E4.3 \| E5 firmware \| T-S212-ACTUADOR-AUTORIZACION \| T-S202
* **s211 · 2026-06-13** — sesión de código sobre el eje egress (alfiler s210 · 2 rebanadas + cierre) · **E2 andamiaje** (`a1cc228`): tab «⚡ Actuar» en HwObjPopup (solo physical · selector de puerto + slider con rango de cfg1 + payload quirúrgico `{puerto:valor}+src` vía ruta peer `sim/from/events` s206b) → **validado punta a punta en vivo** (móvil enciende su luz) · hallazgo **E2-H1**: `dir` no sobrevive a `buildCfg1FromCapability` (solo min/max/bits/tipo entran en cfg1) → filtrar puertos out es consumo de faceta = E6 · **E3 canal `to/act`** (`309dc00`): `mqttTopicTo.act` + `parseActTopic` (core types.ts) + handler de suscripción `'to/act'` en Mus1 (aplica al puerto · NO suprime eco · re-publica from/events = feedback de ejecutado · `publicandoRef` para handler estable) + `sendActuar` reapuntado de la ruta peer a `to/act` sin src (retira el andamiaje E2 · la UI del tab sobrevive) + nota en `sendActuatorCommand` (DORMIDO · `to/act` por dispositivo lo supersede · D-abierta-5) → **validado punta a punta en vivo** · arbitrado: **D-abierta-1** payload plano espejo de from/events + **D-abierta-5** dormir sendActuatorCommand (P11) + alcance E3 mínimo (handler + reapuntado) · hallazgo **E3-H1**: sin channelId explicito (E0-H2 sin saldar) — el registry no guarda el canal de última escucha por hwObj → enrutado por `CommsEvent.channel` (E0-H4) queda para rebanada futura (toca el modelo del registry) · build verde **2210** + **453/453** + tsc limpio · núcleo: `types.ts` (aditivo: mqttTopicTo.act + parseActTopic) · **T-S209-EGRESS-MESA**: E2+E3 cerradas (E1-E3 hechas · próxima **E4** punto de no retorno) · TD-s211-1 (E3-H1) + TD-s211-2 (E2-H1) nuevas · frente s212 = E4 \| E5 firmware \| T-S202 \| TD-s205-1 \| T-S201
* **s210 · 2026-06-12** — sesión de arquitectura+código sobre el eje egress · **alfiler `lru_alfiler_egress_mesa_s210.md` materializado** (estadificación E0–E7 · punto de no retorno E4 · 5 invariantes · 5 D-abiertas) · arbitrado: **D-s210-1 `to/act` canónico** (peer s206b recolocado como lazo de feedback · exclusividad estructural) + D-s210-2 andamiaje provisional + D-s210-3 firmware dentro (E5) · **E0 auditoría fina ejecutada** (Camino Y · 6 hallazgos incorporados: H1 HwSim placa-entera sin src/channelId → **TD-s210-1** · H3 `sendActuatorCommand` s83 latente → D-abierta-5 · H4 `CommsEvent.channel` habilita enrutado por canal de última escucha · H5 s206b confirmado byte a byte · H6 SIGNAL_PERMISSIONS+sombra para E7) · **E1 hecha** (`78d4074`): `dir?: 'in'|'out'|'bidi'` opcional en `SensorCapabilityDef` (core · default 'in' retrocompatible) + Mus1 declara `out` en las 12 luces — la dirección ya viaja en `from/capabilities` · nota operativa: `types.ts` se lee con mojibake vía MCP → edits ASCII-only (TD-s203-1) · build verde **2210** + **453/453** + tsc limpio · núcleo: `types.ts` (aditivo) · **T-S209-EGRESS-MESA → ESTADIFICADA** (E0+E1 cerradas · próxima E2) · frente s211 = E2 \| E3 \| T-S202 \| TD-s205-1 \| T-S201
* **s209 · 2026-06-12** — sesión de código sobre el frente del músico · **Mus1 evoluciona a consola completa (F2.1→F2.5)** · switch Manual de orientación + **canal dual de telemetría mqtt/wss** (canal `ws1` nuevo · 1er consumidor real del `WebSocketAdapter` · flow Node-RED relay broadcast `/lru-telemetria-ws` · keepalive gated re-armable) → **VALIDADO MULTICAMINO** (Mus1↔Mus1 por wss + mqtt + agujas simultáneo · modelo transport-agnóstico de punta a punta) · **luces = primeros ACTUADORES VIRTUALES** (`led_*` 1-bit + `rgb1..3` 8-bit · heredan bidireccionalidad s206b · selector RGB con 3 sliders compartidos solo-UI) · sensores por pestañas: devicemotion (acel+giro) + GPS (`velocidad`/`heading`/`altitud` · lat/lon NO se publica) + micro (`mic_nivel` + 3 bandas FFT) + vibrate reflejo local cabeceo>30 con histéresis · **partitura 32 puertos** · fix Manual acotado a ori · análisis broker-vs-relay (Web STOMP anotada 3ª vía) · **T-S209-EGRESS-MESA NUEVA** (señales en las DOS direcciones por la mesa y los legos · 5 capas · alfiler propio antes de código) + T-S182 editada (T1 materializado) · commits `5c126823`+`3eacc6fd` · build verde **2210** + **453/453** + tsc limpio · núcleo: solo sync-lines (racha TD-s205-1 reiniciada) · TD-s209-1 nueva + TD-s208-1/2 plegadas al VIVO · frente s210 = alfiler T-S209-EGRESS-MESA \| T-S182 \| T-S202 \| TD-s205-1 \| T-S201
* **s208 · 2026-06-12** — sesión de código multi-slice · **lego `PipMenu` compartido** (11ª aplicación patrón canónico · 2º modal Anillo 2 sobre TechModal) + botón PiP primero en cabecera Sim4 (`dataplane-spy` en `sim4Panels`) + Sim1 migrado al lego · **2 bugs latentes cazados**: lazy-sin-Suspense en `PipWindow` (s152 · colgaba la app con chunk frío) + **dedupe por swObjId en `IB_04.registerClip`** (s177 · 2º canvas mudo) · **Opción B multi-instancia en IB_04**: `clipRefs[]` por stage + fan-out `change1()` + merge + limpieza per-stage → **T-S177 CERRADA** (validada runtime: A1+B1 vivas · gemelo sobrevive · enlace mueve ambos · diagnóstico corregido: era nuestro, no de Animate) · scroll horizontal cabecera Sim4 (patrón presetbar s149) · análisis Spy vs Analyzer1 → **T-S208-SPY-EN-ANALYZER** nueva (diferida con destino Observatorio) · commits `6676f67d`+`3c7a6c0b`+`429d555d` · build verde **2210** (+3) + **453/453** + tsc limpio · núcleo: `InstrumentBridge_04.ts` (1er toque real en 4 sesiones, justificado) + sync-lines · TD-s208-1/2 nuevas · frente s209 = T-S182 | T-S202 | TD-s205-1 | T-S201
* **s207 · 2026-06-11** — sesión de código sobre el frente del músico (T-S117-N4 · F1.2+F1.3 completas) · **F1.2 capabilities = la partitura** (`PORT_DEFS` fuente única · secuencia de activación ORDENADA latido→caps→valores · re-publicación ~10s · `virt2` bipolar validada) · sliders familia LRU en Mus1 (MUI retirado) · **F1.3a switches** (`PORT_SPECS.kind` · `sw1`/`sw2` 1-bit vía `LruSwitch`) + **F1.3b orientación** (primer sensor real: `rumbo`/`cabeceo`/`alabeo` ± 45 · permiso iOS en gesto · trío un payload 10Hz) · **validado end-to-end con el teléfono** (agujas moviéndose con la inclinación) · aclaración arquitectural: músicos de distinto tipo NO se cruzan en Mus1 (por diseño) → **primer trigger real T-S182** registrado sin abrir (P11) · commits `1482c786`+`a64e80b5`+`e2bbeb07` · build verde 2207 + 453/453 + tsc limpio · núcleo: solo sync-lines (3ª consecutiva → fricción TD-s205-1) · T-S117-N4 **TRAMO F1 COMPLETO** · TD-s207-1 nueva · frente s208 = T-S182 | T-S202 | TD-s205-1 · *(línea repuesta en s208: el cierre s207 omitió el alta mecánica en §B)*
* **s206 · 2026-06-10** — sesión mixta (arbitraje de frente + auditoría F0 + código F1.1 + 3 fixes de modelo en cadena) · FASE C diferida con razón (P11 · conversión vive en la aguja, verificado) → reencuadre a **T-S117-N4** (músico teléfono/PC como hwObj productor · Plano B símil orquesta) · F0: camino de ingestión 100% abierto (parser net-agnóstico + auto-registro + vocabulario IMU + capabilities) · **F1.1**: página `Mus1` (`/Mus1` · identidad `<tipo>_<sufijo getInstanceId>` correlada con Debug1 · puertos `virt1`/`virt2` bidireccionales: publican al arrastrar, siguen lo recibido de otro Mus1 o del LruBar en HwSim · keepalive latido puro) (`e3704622`) · **3 huecos del modelo cerrados**: slot dinámico en DataPlane + restore de enlaces reactivo a hwObjs tardíos (`58e8ce12`) + guard C4 multi-hwObj en rebindFromEnlace, el «reconsiderar si aparece» apareció con Ils1 (`b7c7119e`) · validado runtime entero (teléfono→aguja · HwSim→Mus1 · Mus1↔Mus1) · build verde 2207 + 453/453 + tsc limpio · núcleo: solo sync-lines · T-S117-N4 AVANZA · TD-s203-2 cierra (3ª PASS) · TD-s206-1/2 nuevas · frente s207 = F1.2 capabilities | F1.3+ botones y sensores reales del teléfono | T-S202 | TD-s205-1
* **s205 · 2026-06-10** — sesión mixta (validación régimen s204 + doctrina RAG + Slice 2) · arranque cero-toque PASS (1ª prueba D-s204-F/G) + régimen ampliado (N-en-mensaje = guía a contrastar · punto 5 = informe sync Project por marcador sN) · poda del Project 105→53 · D-s205 en ARRANQUE §1 (corpus Anillo 2 inmutable SÍ al Project como sustrato RAG · enmienda doctrina s129) · Slice 2 (TD-s202-1) cerrado: ramas sim1_ muertas + deviceId muerto + hwObjIdForSystem inlinada (`571025b9` + `be1aa772`) · grep revalidación limpio · build verde 2204 + 453/453 + tsc limpio · núcleo: ARRANQUE §1 + sync-lines · sin tensiones movidas · TD-s205-1 nueva (sync-line MAPA fuerza re-subida) · frente s206 = FASE C | T-S202 | TD-s205-1
* **s204 · 2026-06-10** — sesión documental-estructural (optimización del ciclo · D-s204-A…H) · backfill s199→s203 muerto por declaración + registro mecánico de bitácora + bitácora de handoffs congelada (D-s204-A/B/C · `ccd5337d`) · `LRU_HANDOFF_VIVO.md` + `LRU_TECH_DEBT_VIVO.md` inaugurados + partición del Project por frecuencia + auto-sesión desde ESTADO + regla disco-gana (D-s204-D/E/F/G · `d5e7298`) · MAPA delega estado-reciente en ESTADO/HANDOFF_VIVO (D-s204-H) · build heredado verde 2204 + 453/453 + tsc limpio · 0 src/ · 2 cambios núcleo (METODO ×2) · sin tensiones abiertas ni cerradas · frente s205 = prueba arranque cero-toque + Slice 2 | FASE C | T-S202

* **s199→s203 · hueco declarado (D-s204-A)** — backfill muerto por decisión, no por esfuerzo (llevaba 5 sesiones como pendiente no-bloqueante). El detalle de esas sesiones vive en los handoffs individuales (`lru_handoff_s198_s199.md` … `lru_handoff_s203_s204.md`, en `doc/`) y, desde s203, en el git de `LRU_ESTADO.md` (un snapshot por cierre). Degradación declarada, no silenciosa.

### §A · Sesiones s123→s198 (entradas condensadas · migración s157 + s158 + s159 + s160 + s161 + s162 + s163 + s164 + s165 + s166 · backfill s167→s180 en saneamiento s180 · s181 + s182 + s183 + s184 + s185 al cierre · s186 + s187 + s188 en saneamiento s188 · s189 + s190 + s191 + s192 + s193 en saneamiento s193 · s194 + s195 + s196 + s197 + s198 al cierre de cada sesión)

Compresión II aplicada en s157 cubrió s123→s156. Las sesiones s157…s166 añadidas conservando estructura. **Backfill s167→s180** volcado en el saneamiento documental de s180 (las 14 sesiones que el rezago había dejado fuera).

* **s198 · 2026-06-07** — sesión mixta (articulatoria P7 + 1 rebanada de código) · **reencuadre del frente + Fase A del colapso de nomenclatura (eje NOMBRE)** · el frente U2.5 de T-S194 se reveló premisa falsa (Chart no es lista, EnlacesPanel matriz 2D, «filtrar-por-valor» otra familia); Manuel reencuadró el objetivo = **una sola lista común de NOMBRES** de donde agrupar por conceptos, y «después agrupar» · hallazgo: el modelo de señales ya se unificó en **datos** (T-S190 s192 / T-S189 s193) pero el **nombre** sigue partido — el `DataPlane` mete el mundo de origen en el prefijo (`sim/N1_L` vs `hw/ble_A92418/adc1`); la frase gold de s151 («todo funciona pero no tienen un namespace común») sigue viva solo en la capa de nombre · decisión de alcance **(X) aplanar el prefijo**: el mundo `sim/hw` baja de prefijo-en-el-nombre a **faceta `origin`** · **A1** (`3386faa`): `createOriginFacet` (5ª firme del registry · índice estático desde `SIM_CATALOG` · `display.id==='hw'→'hw'`, resto→`'sim'`) + `ChannelMeta.origin` derivado de `displayId` + bloque de tests origin (+5) · aditivo, nombre/lente/SensorBrowser intactos · build verde **2201** + **438/438** + `tsc` limpio · 0 núcleo · **hoja de ruta unificada de 3 fases cristalizada** (`lru_alfiler_namespace_unico_s198.md`): **Fase A = T-S198-NAMESPACE-UNICO** (A1 hecho / A2 SensorBrowser+Analyzer1 a `meta.origin` / A3 aplanar `name=sensorId` · cierra T-S151-N2 residuo-nombre + U3 de T-S194) · **Fase B = T-S194 reducida** a agrupación (B0 `createDisplayFacet` / B1 `groupBy` / B2 vocabulario lente `flat|facetId` / B3 migrar inline · cierra T-S194 + absorbe T-S196) · **Fase C = T-S182-ENLACE-SENAL-SENAL** diferida (colapso de identidad, el «(Y)») · **T-S151-N2** reanotada «RESUELTA datos s192 / residuo-nombre → Fase A» · etiquetado **A1/A2/A3** (no «R», choca con R1-R5 per-señal) · greps de blast radius (delegados a Manuel): `useDataPlane` robusto, solo acoplan Analyzer1 + `watchAll` · `PREFIX_MQTT/BLE` vestigiales · D-s198 vía `ask_user_input_v0` · pendiente en la máquina de Manuel: A2 (frente s199) + A3 + Fase B + heredado (runtime C1+C3 s192 + backfill bitácoras desde s180 + regen del índice) · frente s199 = **A2** (migrar `groupByPrefix` + `extractors.ts` a `meta.origin`)

* **s197 · 2026-06-07** — sesión de código · **frente U2.4 (T-S194-DOS-REGISTROS-FACETA · «agrupación como faceta» · umbrella T-S183): store de lente compartida + sync cross-lego** · espejo del lote `signalModes` de s181 llevado a la lente · **U2.4.1** (`8df79d3`): `src/components/panels/FacetLensBar/lensStore.ts` singleton vanilla (no importa React) = **una** lente compartida canónica `'flat' | 'system'` + `subscribe`/`notify` (notifica solo si cambia, idiom del `ControlBus`) + puente React `useSharedLens.ts` (espejo de `useSignalMode`); vocabulario **canónico neutro** (D-s197): cada host mapea sus ids locales ↔ canónico (`placa`/`plana`→flat · `funcion`→system), el almacén/lego no los conocen · `lensStore.test.ts` +8 · aditivo, sin consumidores · **U2.4.2** (`9dd77f4`): `FacetLensBar` gana sync **props-driven** (`synced`/`onToggleSync`/`syncTitle` · botón de enlace tras el select, `is-synced` con acento · `stopPropagation` en el click) · presentacional y store-agnóstico · **U2.4.3** (`05b7e77` · 1er consumidor): `MixerPanel` cablea sync (mapea local↔canónico · `synced` local · sincronizado **lee** la compartida vía `useSharedLens` y al cambiarla **escribe** la compartida → propaga · `effLens` gobierna el agrupado) · **U2.4.4** (`ca2207a` · 2º consumidor): LruBar sync per-tarjeta (`lensSynced[hwObjKey]`) — **supersedido** · **U2.4.5** (`c10a488`): giro pedido por Manuel en sesión — el LruBar pasa a lente **única panel-level** con **una** `FacetLensBar` en cabecera nueva sobre el grid (`.lru_lens_toolbar`) + un solo toggle de sync para todo el panel; retira el `Record` per-tarjeta (`lensMode`/`lensSynced`/`getLensMode`/`isLensSynced`/`effLensMode`) → escalares `lens`/`lensSynced`/`effLens` (código neto más simple) · **D-s197** (sync=prop del lego · canónico neutro `'flat'|'system'` · **D-s197-U2.4.5** lente del LruBar panel-level **SUPERSEDE** el alcance per-tarjeta de D-s195) · hallazgo: el sync **adopta** la lente compartida (no publica la local); el espejo real del lote s181 es **panel-level**, no per-fila · **T-S194 avanza** de «faceta centralizada + barra cross-lego (U2.1-U2.3)» a «store de lente + sync cross-lego (U2.4)» · **T-S196-CONVERGENCIA-LRUBAR-GRUPO** acercada (LruBar gana cabecera de panel) · 5 commits build verde **2200** + **433/433** (+8) · `tsc` limpio · 0 núcleo · pendiente en la máquina de Manuel: runtime de la cabecera nueva del LruBar (barra única `placa`/`funcion`) + el sync moviendo LruBar↔Mixer juntos + heredado de s192 (runtime C1+C3) + regen del índice + bitácoras en rezago desde s180 · frente s198 = **U2.5** (Chart/EnlacesPanel) o **T-S182-ENLACE-SENAL-SENAL**

* **s196 · 2026-06-07** — sesión de código · **frente U2 (T-S194-DOS-REGISTROS-FACETA · «agrupación como faceta» · umbrella T-S183): faceta `system` centralizada + barra compartida cross-lego** · **U2.1** (`4ce735e`): nueva faceta `system` funcional (`createSystemFacet`, 4ª firme del registry de `queryFacets`) que centraliza el cruce que U1.2 horneó inline en `ComponenteLru` — sim→`systemId` de catálogo directo (índice estático cacheado, patrón de `facetAta`) / hw (`{hwObjId}/{port}`)→cruce vivo `signalForPort`→señal sim→`systemId` (sin caché, P11); display `'hw'` excluido de `values()`/índice; sin ruta → «Sin funcion» · `facetSystem.test.ts` nuevo (+11) + 2 tests de conteo 3→4 firmes · + (`92449c3`) `ComponenteLru.funcSystemOfPort` delega en `createSystemFacet().facetOf(...)`, retira el import de `getSignalRoute` (comportamiento idéntico) · **U2.2** (`b67ddd4`): lego nuevo `src/components/panels/FacetLensBar/` = TOOLBAR contenedor presentacional host-neutral (props `options`/`value`/`onChange` + slot `children` para controles futuros; lente = `<select>` compacto theme-aware, idiom de `SignalModeSelect`); LruBar sustituye el botón `f` ad-hoc por `<FacetLensBar>` per-tarjeta (Plana/Por sistema · `toggleLensMode`→`setLens` · default placa = comportamiento actual) · **U2.3** (`0442055` · 1er reúso cross-lego): `MixerPanel` monta `FacetLensBar` a nivel de panel en la `mp-toolbar` (alineada a la derecha · `plana`/`funcion`) y agrupa los sensores por la faceta `system` cuando `funcion` (sub-cabeceras nombre·ATA del catálogo + cubo «Sin funcion» · CSS `.mp-lens`/`.mp-group-header`) · ids de lente **locales al host** (`placa` en LruBar / `plana` en Mixer) ⇒ host-neutralidad validada · **D-s196** (forma de la faceta=funcional uniforme · barra=toolbar contenedor que resuelve chips-vs-select · enfoque U2.2=lente sola/Opción 1 · U2.3=Mixer con agrupado real) · hallazgo (Patrón 6 + Camino Y): «los 4 paneles» NO es uniforme — Mixer/Chart ya viven en el eje funcional, EnlacesPanel es matriz sw×hw sin eje único permutable → el compartible real = faceta `system` centralizada + lego `FacetLensBar`; de los «4 botones» que propuso Manuel, mode+countdown son per-señal (filas) y Displays/Sensores son selección de catálogo nativa de Mixer · **T-S194 avanza de «PoC (U1)» a «faceta centralizada + barra cross-lego (U2.1-U2.3)»** · **T-S196-CONVERGENCIA-LRUBAR-GRUPO** nueva (horizonte · baja: LruBar con grupo activo de catálogo / más lentes → confluye con Mixer) · 4 commits build verde **2198** + **425/425** (+11) · `tsc` limpio · 0 núcleo · pendiente en la máquina de Manuel: vistazo runtime al `<select>` de lente en LruBar + agrupado «Por sistema» en Mixer + heredado de s192 (runtime C1+C3) + regen del índice + bitácoras en rezago desde s180 · frente s197 = **U2.4** (store de lente + toggle 🔗 sync entre legos) o **U2.5** (Chart/EnlacesPanel) o T-S182-ENLACE-SENAL-SENAL

* **s195 · 2026-06-07** — sesión de código · **frente U1 (T-S194-DOS-REGISTROS-FACETA · «agrupación como faceta» · umbrella T-S183): PoC materializado en LruBar** · **U1.0 audit** (lectura empírica `DataPlane`/`sim-catalog`/`signalRoute` + los 2 paneles candidatos · D-s111-1): hallazgo decisivo = el display `'hw'` del catálogo define sus *systems* como los propios dispositivos (`systems: HW_DEVICE_IDS.map(...)`), así que el `systemId` de catálogo de una señal hw **es el hwObjId** → agrupar puertos hw «por system del catálogo» = agrupar por placa (degenerado, sin contraste físico↔funcional) · el system **funcional** de un puerto se alcanza cruzando el enlace `signalForPort` → señal sim → `systemId` · EnlacesPanel descartado como sede (matriz swObj×hwObj, sin eje único permutable) → **LruBar** elegido · **U1.1** (`e6bd1b5`): `ChannelMeta` enriquecido con `displayId`/`systemId`/`ata` (poblado en `register`/`_registerCatalog` · `allSensors()` ahora propaga `ata` desde `SystemDef`) + `DataPlane.test.ts` **nuevo** (3 casos: señal sim lleva system funcional `eng`/ata `72`; señal hw lleva device) · aditivo, 0 comportamiento · **U1.2** (`52d2b80`): lente per-tarjeta `placa ↔ función` en `ComponenteLru` (botón `f` en la status bar) · en modo función re-agrupa los puertos de la tarjeta por system funcional cruzando `signalForPort` → `getMetaBySensorId().systemId`, cabecera de sub-grupo con nombre+ATA del catálogo (`SYSTEM_INFO`), cubo «Sin funcion» con puertos sin ruta + bloque IMU · modo placa intacto (default) · filas `LruSensorRow`/`ImuGroupPanel` reutilizadas · 0 CSS nuevo · **D-s195** (panel=LruBar · mecanismo=enriquecer `ChannelMeta` · UX=selector de lente · «por system»=funcional via enlace · alcance toggle=per-tarjeta · sin-ruta=cubo visible · etiqueta=nombre del catálogo) · 2 commits build verde **2194** + **414/414** (+3) · `tsc` limpio · 0 núcleo · **T-S194 avanza de «auditada (U0)» a «PoC materializado (U1)»** · pendiente en la máquina de Manuel: vistazo runtime al toggle `f` + heredado de s192 (verificación runtime C1+C3) · frente s196 = **U2** (generalizar la faceta a los 4 paneles vía `queryFacets`) o T-S182-ENLACE-SENAL-SENAL

* **s194 · 2026-06-06** — sesión mixta (código UX + auditoría/articulación) · **3 fixes UX build-verde + apertura de T-S194-DOS-REGISTROS-FACETA + U0 completa** · **FIX1** (`6b1d1d5`): el slider del LruBar ahora sigue el slot del DataPlane igual que el del Mixer (componentes per-fila `LruSensorRow`/`LruImuAxis` con `useDataPlaneValue` · espejo del `MixerRow` s178) → los dos sentidos sincronizan · **FIX2** (`2ca3fbc`): dropdown de modo `SignalModeSelect` theme-aware (CSS-only · fin del popup claro sobre tema oscuro · Mixer + LruBar) · **FIX3** (`b3ab9a3`): sliders con seguimiento externo suave (`left 0.04s linear` en `.sl_thumb`) + fin del clavado al arrastrar rápido (`draggingRef` suspende el re-posicionamiento durante el arrastre · lego compartido LruBar + IMU) · **apertura T-S194** (`667ec63`): por qué los 4 paneles agrupan distinto → auditados (Mixer/Chart desde el catálogo `displays`/`systems`; LruBar/EnlacesPanel desde el ObjectRegistry `hwObjs`) → alfiler `lru_alfiler_dos_registros_agrupacion_faceta_s194.md` (Patrón 7 · agrupación como faceta · invariantes I1-I4 · Q1-Q5 · estadificación U0-U3) + **T-S194-DOS-REGISTROS-FACETA** nueva (bajo umbrella T-S183 · hermana de T-S182-ENLACE-SENAL-SENAL) · **U0 auditoría profunda COMPLETA** (`24fa814` + `b6ba1ff`): hallazgo central = identidad/valor/modo **ya unificados** en el DataPlane (sensorId único: sim `N1_L` / hw `{hwObjId}/{port}` · 205 señales = 127 sim + 78 hw por `buildHwSensors` sobre 3 BLE · modo keyado por el mismo sensorId · `queryFacets.allSensors()` ya enumera `listChannels()`) → lo único partido es la enumeración en los 4 paneles · **3 residuos cerrados** (R-U0-a drift confirmado, fuentes distintas catálogo/ObjectRegistry · R-U0-b `hwObjsForSignal` deriva device · R-U0-c `swObj`=faceta consumidor) + modelo de facetas para U1 (la taxonomía display/system/ata no vive en `ChannelMeta` · `queryFacets` cruza por `sensorId` con `SIM_CATALOG`) · D-s194 (FIX1=Mecanismo B + alcance total · resto de fixes directos · U1 diferida a Manuel, P11) · 6 commits TODOS doc/UX · 0 núcleo · build heredado verde **2194** + **411/411** · pendiente en la máquina de Manuel: verificación runtime C1+C3 (heredada de s192, sin correr) · frente s195 = U1 (faceta por system en un panel · PoC aditivo/reversible · ejercita el cruce sensorId↔SIM_CATALOG) o T-S182-ENLACE-SENAL-SENAL estructural
* **s193 · 2026-06-05** — sesión de código · **cierre del eje router per-hwObj (T-S189) = última cola de R5** · **R5.6.1** (`b4188ae`): el toggle Sim/HwReal de QuickActions (`TabStatus`) lee `status.simInhibited` en vez del `routerMode` vestigial (clavado, siempre false) → corrige el toggle atascado que nunca reanudaba HwReal · **R5.6.2** (`9908003`): borrados los dos chips cosméticos que mostraban el `routerMode` muerto (Row "Router" de `TabStatus` + badge `mode` de `DeviceBadge` en `hw1Shared`) → **ningún componente lee ya `hwObj.routerMode`** · **R5.6.3** (`502c5ee` · punto de no retorno): retirado de `types.ts` el eje router entero (`RouterMode`/`ROUTER_MODES`/`DEFAULT_ROUTER_MODE`/`ROUTER_LABELS` + campo `HwObj.routerMode`) + sus escritores (`createDefaultHwObj` · bloque reset de `ObjectRegistryContext`) + imports muertos (`Detalle01` · `glossary`) → **R5 (alcance B) completo del todo** · hallazgo (salvaguarda del grep): la superficie real era mayor que los "dos lectores" enumerados (escritor ORC + import muerto Detalle01 + re-export glossary) · `tsc` limpio = 0 consumidores colgados · `RouterModeChip` queda vivo e intacto (ya per-señal) → **T-S193-ROUTERMODECHIP-RENAME** nueva (cosmética · baja) · **🟢 T-S189-DISPLAY-PER-SENAL CERRADA** · D-s193 (frente=T-S189 · chips=borrar · toggle=`simInhibited`) · 3 commits build verde **2194** + **411/411** · `tsc` limpio · 0 núcleo · cierre `c30ee5e` · pendiente en la máquina de Manuel: verificación runtime C1+C3 (heredada de s192) · frente s194 = nuevo frente bajo umbrella T-S183
* **s192 · 2026-06-05** — sesión de código · **COLAPSO PUERTO→SEÑAL COMPLETO (C0-C4)** del frente colapso (umbrella T-S183) · **C3** (`bb17bfb` · punto de no retorno): retirado el dual-write `signalForPort→writeBySensorId(signalId)` de `createSensorDataHandler` (`registryMqttHandlers.ts`) → el dato hw escribe **solo** el slot del puerto (imports `getSignalRoute`/`sr` local retirados) · **C4** limpieza (`ed96cf3` + `d12206d`): C4.1 tripwire R3/R4 Modo B fuera de `rebindFromEnlace` + C4.2 `_createBinding` Modo A a la señal de casa vía `indexOfSensorId` (tentativa exacta `hw/<deviceId>/<sensorId>` demostrada muerta, caía siempre al fallback) + C4.3 **D-s192-C4.3=conservar** `signalForPort` (grep 0 llamadores de producción tras C3, pero accesor de lectura de `_route` + 6 tests) · **RESUELVE T-S151-N2 + CIERRA T-S174 + SALDA T-S190-COLAPSO-PORT-SENAL** · un solo universo de señales: la aguja lee del DataPlane directo, re-apuntada por el enlace, cero copia · hallazgos: path del handoff s191 desviado (`src/simulation`→real `src/datosLru01`) · `_route` sigue viva (`signalsForHwObj` R5.2 + `hwObjsForSignal` R5.3) · 4 commits build verde **2194** + **411/411** · `tsc` limpio · 0 núcleo · frente s193 = verificación runtime C1+C3 + definir nuevo frente bajo T-S183
* **s191 · 2026-06-05** — sesión de código + auditoría · **colapso C1 + C2** · **C1** (`3abba2c` · re-apuntado ON): `rebindFromEnlace` re-apunta la aguja Modo A al slot del puerto + normaliza contra el rango de entrada del cfg1 (`inputRanges` ADC 0..4095, idéntico al slider) + desenlace simétrico que devuelve la aguja a casa con su rango de catálogo vía `_needleHomeRange` → **RESUELVE T-S174-VERTIDO-SIN-ESCALA** (la aguja deja de clavarse · hallazgo RAG: el hogar canónico del escalado es cfg1 según `lru_fase4_signal_model.html` S7 + `lru_postulado_12` S8.3) · **C2 concluido** (censo de lectores del vertido: el único dependiente era la aguja, ya migrada en C1; Chart/Mixer/inspectores leen el slot propio de cada sensor; LruBar lee `hwObj.hw1`, no el DataPlane → **C3 seguro sin migración**) · cierre `d92e4f6` · build verde **2194** + **411/411** · `tsc` limpio · 0 núcleo · frente s192 = C3 (punto de no retorno) → C4
* **s190 · 2026-06-05** — sesión mixta · **R5 COMPLETO (R5.5)**: borrado el RouterService — C1 retira el handler `router1Send1` + listener ORC + imports huérfanos `commsSend`/`mqttTopicSim` (`aedda2e`) · C2 borra `RouterService.ts` + `RouterServiceReact.ts` + `routerServiceInit.ts` + `initRouterService` en `App.tsx` (`d2f4a79`) · **punto de no retorno de R5** · el eje de modo per-hwObj queda demolido de punta a punta (vestigial conservado `RouterMode`/`ROUTER_*`/`HwObj.routerMode` por D-s185 ALCANCE=B, lo lee el display diferido T-S189) · **+ FRENTE NUEVO: colapso puerto→señal** (alfiler `lru_alfiler_estadificacion_colapso_senal_s190.md` · `9d21e31` · detonado por bug observado: aguja congelada bajo override manual del slider pero viva con hardware real · causa raíz = el vertido puerto→señal vive en `createSensorDataHandler` s172 R1, en la ingesta de comms, no en la escritura · dos ejes ortogonales ENTRADA/SALIDA · modelo objetivo un universo de señales con puertos hw = señales de primera clase · invariantes I1-I5 · decisiones Q1-Q5 · rebanadas C0-C4, punto de no retorno único en C3 · resuelve T-S151-N2 y T-S174 · **T-S190-COLAPSO-PORT-SENAL** nueva) · 3 commits build verde **2194** + **411/411** · `tsc` limpio · 0 núcleo · frente s191 = colapso C1 (re-apuntado · aditiva/reversible · arregla el bug de la aguja)
* **s189 · 2026-06-05** — sesión de código · **R5.4 COMPLETO (autoridad)** del frente R5 · retirados todos los acoplamientos que colgaban del RouterService: **R5.4.1** gate fuera de `updateHwObjSensors` (ORC · único llamador externo vivo · siempre true por construcción · neutral · `7e155f6`) + **R5.4.2** `cb.init(getMode)` fuera + idempotencia por flag de módulo en `controlBusInit` (`c703695`) + **R5.4.3** gate per-hwObj entero fuera de `ControlBus.ts` (`HW_PERMISSIONS`/`UNGATED`/`init`/`allows`/`allowsChannel`/`filterAllowed`/`getAllowedSources`/`getPermissionTable`/`blocked*`/`status` + 8 entradas `ControlBusDebug` + import `RouterMode` · eje per-señal intacto · `2e8ca5b`) + **R5.4.4** ORC desacoplado (mirror `subscribeAll→routerMode`, register+sync, unregister, `resetAll`×2, rama muerta 'Rnd', import · `f0f1477`) + **R5.4.5** auto-registro `rs.register(vhwObj)` fuera de `registryMqttHandlers` (`40243b1`) · **el RouterService queda SIN llamador externo en `src/`** · hallazgos (Patrón 6): `routerMode` YA universalmente HwReal (sin consumidor de `useRouterActions`) · `ComponenteLru` NO lee `routerMode` (refuta handoff s188 · cabo display = solo `TabStatus` + `DeviceBadge`) · `RouterModeChip` ya fuera del eje · corrección a R5.0 (el gate NO estaba fuera de la senda de datos: `updateHwObjSensors` lo llamaba · inerte en efecto, no en presencia) · **D-s189-DISPLAY=C** (diferir el chip per-hwObj a per-señal · **T-S189-DISPLAY-PER-SENAL** nueva · baja) · `HwObj.routerMode` + `RouterMode`/`ROUTER_LABELS` quedan vestigiales (D-s185 ALCANCE=B) · 5 commits build verde **2196** + **411/411** · `tsc` limpio · 0 núcleo · frente s190 = R5.5 (borrar el servicio · `tsc` rojo guía · regla migrar-antes-de-borrar satisfecha)
* **s188 · 2026-06-04** — sesión de código · **R5.3 COMPLETO** (firmware / TabControl) del frente **R5** (retirada del eje per-hwObj · umbrella T-S183) · **R5.3.1** `firmwareInhibitionShim` (singleton vanilla nuevo · reubica la inhibición `to/sim` del eje per-hwObj al modo per-señal · placa inhibida sii ≥1 señal en HwSim · whole-board, espejo del flag único `g_sim_inhibited` del firmware · edge-triggered · accesor inverso `hwObjsForSignal` en `signalRoute` +3 tests · reconstruye el `to/sim` que R5.2.4 dejó dormido · `ad6ab5e`) + **R5.3.2a** el ingest de `from/status` fija `simInhibited` desde el campo `sim` del firmware (verdad de firmware · sirve popup+TabControl · realiza la intención del tipo + los lectores `DeviceBadge`/`ComponenteLru` · refuta la predicción "nadie lo lee" · `03a250e`) + **R5.3.2b** TabControl migrado del eje per-hwObj al modo per-señal del ControlBus sobre `signalsForHwObj` (espejo HwObjPopup R5.2.4 · grid 5 modos + `handleSim` toggle Real↔HwSim) · retira el write optimista de `simInhibited` + neutraliza `RouterService._handleInhibition` a no-op (`_inhibitionSender` dormido hasta R5.5 · grep confirma inocuo: ninguna UI pone `rs` en HwSim · `273bd8b`) · **D-s188-FW=A** (no tocar firmware · contrato `to/sim` whole-board ya correcto · per-canal diferido T-S184) · **D-s188-R5.3.1=A** / **R5.3.2-INH=C** / **-SIM=A** / **-RS=A** · build verde **2196** + **411/411** · `tsc` limpio · cabo: `TabStatus`/`hw1Shared`/`ComponenteLru` leen `routerMode` para display (vestigial → migrar a per-señal en R5.4+) · 0 núcleo · cierre `d89c62f` (incluye `index.html`) · frente s189 = **R5.4** (autoridad: mirror ORC + register mqttHandlers + `cb.init(getMode)` + retirar gate per-hwObj inerte) → R5.5 (borrar el servicio)
* **s187 · 2026-06-04** — sesión de código · **R5.2 COMPLETO** del frente **R5** · **R5.2.4** HwObjPopup a modo per-señal sobre `signalsForHwObj` de ESE hwObj (rejilla "Modo de datos" + toggle HwSim/HwReal + display Real/Local/HwSim/Gen/Rnd vía ControlBus · `isSimActive` = ≥1 señal HwSim · chip INH retirado · `onModoChange`/RouterService retirados del popup y del `EnlacesPanelContent` · **D-s187-R5.2.4-FW=A** migración limpia · inhibición firmware `to/sim` **DORMIDA** hasta R5.3) + **R5.2.5** Sim1 auto-Rnd al cargar retirado (gemelo de D-s186-R5.2.3-AUTORND · **D-s187-R5.2.5-AUTORND=A** · las agujas siguen con datos del motor/Gen por el gate `allowsSignal` permisivo sin modo) · **desde la UI ya no hay escritores de modo per-hwObj** · RouterService vive hasta R5.5 (regla migrar-antes-de-borrar) · commits `7560647` (R5.2.4) / `7ac086f` (R5.2.5) · build verde **2195** + **408/408** · `tsc` limpio · 0 núcleo · frente s188 = **R5.3** (firmware: shim whole-board + migrar TabControl)
* **s186 · 2026-06-04** — sesión de código · **R5** avanza · **R5.1** indicador online a per-señal (`ControlOnline1` al agregado per-señal del ControlBus · online sii ≥1 aguja en Real|HwSim · sim/virtual arranca offline/rojo · **D-s186-R5.1-IND**) + **R5.2.0** accesor `signalsForHwObj` en `signalRoute` (+3 tests · cimiento del lote per-señal) + **R5.2.1** LruToolbar (re-key del lote de `{hwObjId}/{puerto}` a `signalsForHwObj` · cierra la deuda alfa **D-s181-R2b-1**) + **R5.2.2** RouterModeChip + **R5.2.3** Detalle01 (selector/Rnd/countdown a per-señal · auto-Rnd `onAnimacionReady` retirado · **D-s186-R5.2.3-AUTORND**) · watchdog **D-s151-LRUBAR-5** retirado (cerrada) · 4 consumidores de UI fuera del eje per-hwObj · `RndControls` (SimService) intacto · 5 commits `294b8b9`/`a389bcc`/`1cbb6f5`/`5cd83a3`/`1092ebf` · build verde **2195** + **408/408** · 0 núcleo · frente s187 = R5.2.4 (HwObjPopup) + R5.2.5 (Sim1 auto-Rnd)
* **s185 · 2026-06-03** — sesión **articulatoria pura** (Patrón 7) · frente **R5** (retirada del eje per-hwObj · RouterService) bajo la umbrella **T-S183** · se arbitra el **alcance B** (retirar el RouterService entero · `HwObj.routerMode` vestigial · sin tocar el tipo núcleo · D-s185-ALCANCE=B) + **4 decisiones** (**D-R5-UI** reconvertir los controles de grupo a lote per-señal · **D-R5-IND** online sii modo ∈ {Real, HwSim}, con Gen/Rnd/Local/sin-enlace→offline · **D-R5-FW** shim fino whole-board, per-canal a T-S184 · **D-R5-GATE** retirar el gate per-hwObj inerte en R5.4) + **R5.0 auditoría ejecutada** · hallazgos (el sweep s182/s184 §4 estaba incompleto): **dimensión de gate del ControlBus** (per-hwObj `allows`/`HW_PERMISSIONS` alimentado por `rs.getMode` vía `cb.init(getMode)`, **inerte** porque la senda de escritura del DataPlane solo consulta `allowsSignal()` per-señal → cortar la inyección es seguro) + 3 consumidores no listados (`LruToolbar` residual · `TabControl` firmware · `Sim1` auto-Rnd) + **`router1Send1` muerto** (sin `dispatchEvent` en `src`) · `HW_PERMISSIONS` vive en `ControlBus.ts` · estadificación **R5.0–R5.5** con punto de no retorno en R5.5 (borrar el servicio) y regla dura "migrar consumidores antes de borrar" · alfiler `lru_alfiler_estadificacion_r5_s185.md` (hilo conductor) · **0 código · 0 núcleo** · build heredado verde **2196** + **405/405** · frente s186 = **R5.1** (indicador online a per-señal)
* **s184 · 2026-06-03** — sesión de código · **R3 = B + cabecera del LruBar a lote per-señal** (dos rebanadas del frente per-hwObj hacia el modelo unificado) · **R3 = B** (`aaf569f` · D-s184-R3-1=B/R3-2): `Rnd` deja de ser modo per-hwObj y vuelve a ser **fuente per-señal 1:1** (`'rnd'` en `DataSource` + `SignalMode 'Rnd'` + `SIGNAL_PERMISSIONS.Rnd`); el generador del `SimService` (`demoTick`) consulta el modo per-señal y escribe `'rnd'`; `SimService` deja de importar `RouterService` (era su único uso) · **mini-rebanada cabecera** (`f333d79`): `handleModoChange` del `ComponenteLru` deja el RouterService y hace **fan-out per-señal** (`setSignalMode` + `cancelSignalCountdown`) sobre las señales del grupo, espejo del lote del LruToolbar; el select muestra el modo agregado (común o "Mixto"); retirados `getRouterService`/`RouterMode`/`ROUTER_*`/`mqttTopicTo`/`LOC_COUNTDOWN_MS` y el set optimista de `simInhibited` (el icono se conserva) · **las dos UIs de modo del LruBar (per-slider + cabecera) son ya per-señal** · **firmware (D-s182-FW resuelta de facto)**: el control per-entrada ya existe app-side (gate 2 per-señal), el `to/sim` del firmware es whole-board → granularidad per-canal diferida a **T-S184-FW-INHIB-PER-CANAL** (nueva · horizonte) · build verde **2196** + **405/405** · `tsc` limpio · 6 ficheros src/ (R3: 5 · cabecera: 1) · 0 núcleo · + commit `2df9ba4` (sync living-docs s183) · horizonte s185 = **R5** (retirada del eje per-hwObj · RouterService)
* **s183 · 2026-06-03** — sesión mixta código + diseño · **R1 + R2 enviados** (primeras rebanadas del mapa s182): **R1** lego `SignalCountdownRing` extraído de MixerRow a `components/panels/` (`4ac905d` · D-s183-R1-1/R1-2) + **R2** slider del LruBar a per-señal (espejo MixerRow: `setSignalMode('Local')` + anillo per-slider) y **retirada del gate 1 (per-hwObj)** de `writeBySensorId`/`fromRecord`/handler MQTT — el gate 2 (per-señal) queda como único árbitro (`2cbe9e4` · D-s183-R2-FX) · **🟢 T-S182-SLIDER-LRUBAR CERRADA** · **giro conceptual mayor**: el frente se eleva de "retirar el modo per-hwObj" a **modelo unificado de señales** (destilado `lru_destilado_modelo_unificado_senales_s183.md` `f3399a0` · reconocimiento+materialización de piezas canónicas): registro abierto de fuentes/sumideros (espacio · adaptadores ingress/egress) × continuo vivo/grabado/programado (tiempo · grabación/reproducción de caja negra/clase magistral) × bidireccionalidad sensor↔actuador (dirección · "los actuadores son nuestras manos"), arbitrado per-señal por la mesa de parcheo (exclusividad atómica = invariante de seguridad en egress), Realm como partitura · **Gen desaparece** → fuentes 1:1 / sourceId abierto · RouterService se retira en R5 · **umbrella T-S183-MODELO-UNIFICADO-SENALES** (absorbe la cola de T-S165) · hallazgo empírico: quitar el gate 1 NO toca Rnd (D-s182-RND→split) · auditoría SimService (dos escritores excluyentes por run-state · SignalLab vía override sin fuente propia · actuadores ya parciales · actuador NO es sensor invertido pero comparte bus) · build verde **2196** + **405/405** · 4 commits (2 código + destilado + docs s182) · living docs s183 actualizados · 0 núcleo
* **s182 · 2026-06-02** — sesión mixta código + audit/articulación · **R3 cerrado** (modos per-señal · eje limpio): R3.1 semilla hw→`Real` (`1d77253` · D-R3-B=B1) + R3.2+R3.3 retira el eje `origin` de raíz en 6 ficheros (`caabdc9`) + R3.4 quita "Auto" de la UI y retira `clearSignalMode` código muerto (`c17180` · D-R3-4-1/2) · un solo eje, siempre con modo, sin Auto/origin/pin · **frente posterior mapeado** (`lru_mapa_unificacion_modos_universal_s182.md` hilo conductor): retirar el **modo** per-hwObj conservando el hwObj como **agrupación** (reencuadre **D-s182-UNIF-3** modo/agrupación/efecto-físico · nomenclatura `Loc→Local`/`HwReal→Real`/`Rnd→Gen` · operaciones de grupo = fan-out a `setSignalMode` · firmware = efecto físico derivado del grupo-hardware) + **R0 sweep** (inventario completo · superficie mayor de lo confirmado: 7 UIs, generador Rnd en SimService, indicador online en useIB04Registry, `allowsSource` muerto) · estadificación borrador R0..R5 · **🔴 T-S182-SLIDER-LRUBAR** regresión viva en HEAD (slider del LruBar en `Real` cortado por el gate 2 per-señal · causada por R3.1 · cierra en R2) · **T-S182-ENLACE-SENAL-SENAL** nueva (horizonte · señal virtual y física ambas de primer orden · enlace señal→señal con conversión diferido) · D-s182-UNIF-1/2/3 + D-s182-FW/GEN/RND/ORDEN abiertas · build verde 2193 + 405/405 · 7 src/ (R3) · 0 núcleo
* **s181 · 2026-06-01** — sesión de código · **R1 notify del ControlBus** (cierra T-S178-CONTROLBUS-PUBSUB · D-s181-R1-1=B simétrico a RouterService): `subscribeSignalMode`/`subscribeAllSignalModes`/`notifySignalMode` + hooks `useSignalMode(id)`/`useSignalModes()` en `ControlBusReact`; el poll de 1s del Mixer (`setModeTick` s169) **jubilado** (commit `f58e48b`) · **R2a dropdown de modo per-slider en el LruBar** vía lego nuevo `SignalModeSelect` extraído del MixerRow (encapsula `useSignalMode` → instanciable en `.map` sin romper Rules of Hooks · `ComponenteLru` lo monta por sensor con `signalId={hwObjKey/sensorKey}` · CSS muerta `.mp-mode*` retirada · commit `096e65d` · D-s181-R2-1) · **R2b lote per-señal del LruToolbar** (el `<select>` global deja el RouterService y batea sobre las signalIds del grupo · "— Mixto —" si difieren · reactivo vía `useSignalModes()` · 1er consumidor del hook global · commit `f24922c` · D-s181-R2b-1=alfa) · **Mixer y LruBar comparten ahora el almacén `signalModes` entero** (per-señal + lote · propagación instantánea) · **R3 articulado y diferido** (`lru_alfiler_r3_quitar_auto_origin_s181.md` hilo conductor · quitar Auto + origin + semilla por defecto · superficie ~7 sitios auditada · 2 decisiones semánticas abiertas D-R3-A persistencia al desenlazar / D-R3-B semilla hw suelto · el gate per-hwObj NO es R3 · D-s181-R3-1) · build verde **2193** + **405/405** · 7 src/ + 3 nuevos del lego · 0 núcleo
* **s180 · 2026-06-01** — sesión de código + saneamiento documental · **R0** del hilo de unificación de modos: rename ancho y puro del eje de modo per-señal `Sensor*→Signal*` (`SensorMode→SignalMode` · `SENSOR_PERMISSIONS→SIGNAL_PERMISSIONS` · `allowsSensor→allowsSignal` · getters/setters · familia countdown `start/cancel/pause/resume/getSignalCountdown` · hook `useSignalCountdown` · detector `SignalSourceDiag`/`diagnoseSignal` · `applySignalDefaults` · claves `window.ControlBusDebug.*` + `SourceMapDebug` + `shadowStats().governedSignals`) · auditoría **Camino Y** previa deslindó **tres ejes "sensor"** (MODO=R0 · SLOT del DataPlane y CANAL/spy FUERA · un find-replace ciego habría roto los tres) · 8 ficheros src/ + 1 comentario · `tsc` como red exhaustiva de llamadores tipados (0 rezagados) · build verde **2190** + **405/405** · commit `dd154ca` · **D-s180-R0-1** (countdown dentro) **-2** (detector a Signal*) **-3** (claves de consola renombradas · sin alias) · **T-S180-PROSA-MODO** nueva (baja · la prosa de comentarios sigue en "per-sensor") · **saneamiento documental entero**: backfill bitácoras s167→s180 + MAPA sincronizado + artefactos de cierre (resuelve el rezago heredado desde s166/s168) · 0 cambios núcleo
* **s179 · 2026-06-01** — sesión mixta auditoría + código + articulación · **auditoría de rutas Fase 0** (capa por-lego: los 3 legos NO comparten autoridad de modo · Mixer=ControlBus per-señal, LruBar=RouterService per-hwObj, Chart=ciego · DataPlane=nudo previsto, no autoridad única · hipótesis raíz del charter CONFIRMADA) · **D-s179-CHART-1** el buffer del Chart se nutre del slot del DataPlane para señales gobernadas (Camino A · `SimService.tick` lee `readBySensorId` si `getSensorMode!=Gen`) · **modelo objetivo de la unificación de modos per-señal articulado** (`lru_alfiler_unificacion_modos_signal_s179.md`: un solo eje, el de la señal · device=agrupador sin modo · A/B/C obsoletas · Real/Gen/Local/HwSim · fuera Auto+origin · semilla por defecto · rename sensorModes→signalModes · notify prerrequisito) · **D-s179-MODE-1..4** · T-S179-CHART-FUENTE + T-S179-CHAIN-SENALES nuevas (esta absorbe T-S174) · T-S178 promovida a prerrequisito · 2 alfileres nuevos · build verde 2190 + 405/405 · 1 fichero src/ · 0 núcleo
* **s178 · 2026-06-01** — sesión de código · paso 5 **R-bidi.2c + R-D** · el slider del Mixer gana retorno automático a Real + candado · countdown per-señal vanilla en `ControlBus` (10s · al expirar `setSensorMode('Real','auto')` D-s178-3) + hook `useSensorCountdown` (poll 250ms) en `ControlBusReact.ts` **nuevo** + `MixerRow.tsx` extraída (Rules of Hooks) con anillo SVG read-only · **R-D candado** = botón separado que pausa/reanuda el timer (slider con candado conduce pero no re-arma) · D-s178-1..3 + A/B/C + R-D-1/2 · T-S178-CONTROLBUS-PUBSUB nueva (informativa) · build verde 2190 + 405/405 · 2 src/ nuevos + 2 modificados · commit `125034b` · 0 núcleo
* **s177 · 2026-06-01** — sesión de código · paso 5 **R-bidi.1+2a+2b** · el slider del Mixer pasa de freeze a seguidor/conductor de la señal vía DataPlane (T-S176 honrada) · R-bidi.1 seguidor (`useDataPlaneValues` ~20fps · NaN→bridge) · R-bidi.2a conductor crudo (`writeBySensorId('slider')` · gate s167 bloquea al motor en Local) · R-bidi.2b toque→Local pin manual (regla única) · D-s177-1..4 · **🟢 cabo ILS s176 CERRADO** (bug conocido de Adobe Animate · una sola instancia recibe datos · el seguidor sobre adc1/adc2 validó hardware→slot→señal de punta a punta) · T-S177-ANIMATE-INSTANCIA-UNICA nueva (diferida) · build verde 2188 + 405/405 · 1 src/ · 0 núcleo
* **s176 · 2026-05-31** — sesión mixta articulatoria + código · paso 5 **R-A** · control de modo per-señal editable en el Mixer (el chip read-only s169 pasa a `<select>` Auto/Real/Local/HwSim/Gen) · `ControlBus` gana **procedencia** del modo (auto/manual) + `getSensorOrigin`/`isManualPinned` + guarda de **precedencia manual** en `enlazar`/`desenlazar` (el auto-productor no pisa un pin manual) · **Vía A** (coexistencia) · antes del código se cristalizó el hilo conductor `señal↔referencia` en `lru_alfiler_senal_eje_productor_consumidor_s176.md` (**T-S176-SEÑAL-EJE** · la señal es el eje productor↔consumidor) · build verde 2188 + 405/405 · 4 src/ · cabo ILS GS_Dev/LOC_Dev congeladas (diferido) · 0 núcleo
* **s175 · 2026-05-31** — sesión de código · **R4 del paso 4 cerrado por Vía B** (slot hw promovido a punto de inspección, no eliminado) · **paso 4 COMPLETO (R0-R4)** · una sonda de lectores en runtime **refutó** el supuesto del alfiler ("nadie depende del slot hw"): los slots hw/* los leen en vivo `Obs1LiveValues` + `DataPlaneSpy.getSensorStats` · D-s175-1=Vía B (conservar slot hw como namespace de inspección + reducir R4 a retirar el camino legacy Modo B con tripwire) · T-S172-MODOB-SIN-SIGNALID cerrada · el 🔴 de `_sensorIdToIndex` "primero gana" resultó **inofensivo** (sim y hw nunca colisionan por el prefijo `/`) · build verde 2188 + 405/405 · tripwire no se dispara · 1 src/ · 0 núcleo
* **s174 · 2026-05-31** — sesión de código · **R3 del paso 4 materializado** (lectura uniforme de la aguja · punto de no retorno) · `rebindFromEnlace` deja de arrancar las agujas Modo A al slot hw (leen el slot de señal como única fuente · vertido R1 lo alimenta · gate R2 lo gobierna) · D-s174-1=A + tripwire · diagnóstico previo del puerto de `IAS` reencuadró T-S173 (señal compartida `Pfd1HSpeed1`+`AirSpeed1` · el A92418 no está mudo en bloque) · validado runtime sobre Ils1/GS_Dev/LOC_Dev · T-S174-VERTIDO-SIN-ESCALA nueva informativa (el vertido copia el crudo sin escalar) · build verde 2188 + 405/405 · 1 src/ · 0 núcleo
* **s173 · 2026-05-30** — sesión de código · **R2 del paso 4 materializado** (modo a la señal + enforcement) · el modo per-señal se fija sobre el `signalId` (no el slot hw) en `enlazar`/`desenlazar` · el gate per-señal (firme en `DataPlane` desde s167) corta el motor (`'sim1'`) sobre las señales en Real = **primer corte de comportamiento real** · D-s173-1=A · **T-S167-N1 CERRADA** en runtime sobre A92418 (motor parado=cero 'sim1' en canales hw; con Play=GS_Dev/LOC_Dev en Real alimentadas por 'mqtt-hw', motor rechazado, shadowStats subiendo) · T-S173-IAS-PUERTO-MUDO nueva informativa · build verde 2188 + 405/405 · 1 src/ (2 ediciones) · 0 núcleo
* **s172 · 2026-05-30** — sesión de código · **R0+R1 del paso 4 materializados** · R0 el enlace lleva la ruta `{hwObjId}/{puerto}→signalId` (singleton nuevo `signalRoute` · `signalForPort` · poblado por el auto-enlace desde el manifest) · R1 el handler MQTT vierte al slot de señal en sombra (`signalForPort`→`writeBySensorId(signalId,v,mqtt-hw)`, slot hw mantenido) + cobertura manual/restore por re-derivación del manifest de IB_04 (accessor nuevo `getNeedleSignalIds`) · D-s172-1=A/-2=B/-3=A/-4=A · build verde 2188 + 405/405 · reversible hasta R3 · **R1 antes de R4 satisfecha** · T-S172-MODOB-SIN-SIGNALID nueva · 5 src/ (2 nuevos) + 1 test · 0 núcleo
* **s171 · 2026-05-30** — sesión **articulatoria pura** (Patrón 7) · alfiler de estadificación del paso 4 · D-s171-1=B (el enlace posee la tabla puerto→señal · `signalMap` por canal poblado desde el manifest · ruta device→señal con hogar en el ORC) · estadificación **R0-R4** articulada (R0 enlace lleva señal · R1 vertido device→señal en sombra · R2 modo a la señal + enforcement · R3 lectura uniforme de la aguja · R4 retirar auto-registro hw) · orden duro **R1 antes de R4** (evita el no-op silencioso) · alfiler nuevo `lru_alfiler_estadificacion_paso4_s171.md` · 0 código · 0 núcleo
* **s170 · 2026-05-30** — sesión de código · **dos frentes 🟢 del hilo de unificación cerrados** (MCP) · (1) **paso 2 pleno por cura B** (D-s170-1): `SimService.pushToRegistry` excluye el display `hw` del `fromRecord('sim1')` → el motor deja de volcar los canales hw enlazados · **T-S164 disuelta** en su raíz de vertido + **T-S163 disuelta** (commit `eacfeb5`) · (2) **paso 6** (D-s170-2, Opción A): el tick del SignalEngine + su `init` se desacoplan del panel SignalLab y pasan al motor persistente (SimService constructor) · **T-S157 cerrada** · driver único (commit `135fcd6`) · build verde 2187 · 0 núcleo
* **s169 · 2026-05-29** — sesión mixta · indicador **read-only** de modo per-señal en el Mixer (chip Real/Loc/HwSim/Gen, o "—" si mode-less) + mapa de impacto del arbitraje (`lru_mapa_impacto_arbitraje_unificacion_s169.md`) + **§8.3 RESUELTA** (D-s169-SLOT-1: un slot por señal · el DataPlane es el mundo de las señales · crudo en `hwObj.hw1` · inspeccionar=enlazar · "test points" candidato P11) · T-S164 localizado en vivo · build verde · 0 cambios núcleo
* **s168 · 2026-05-29** — sesión de código · **productor de modos automático en el ORC (Lectura B)** · `enlazar()` reconcilia los modos per-señal del hwObj físico contra el `channelMap` (puerto activo→`setSensorMode('Real')`; puerto que cae→`clearSensorMode`); `desenlazar()` limpia todos los puertos (D-s168-1, simetría) · **la cura del parpadeo ya no depende de consola** · validado sobre A92418 real (`diagnoseSensor` da mode:'Real', lastWriter:'mqtt-hw', conflict:false) · "Real inmortal" tras F5 explicado como enlace persistido en localStorage (no bug) · T-S168-N1 nueva (diagnóstica) · build verde 2187 · 1 src/ · 0 núcleo
* **s167 · 2026-05-29** — sesión de código · **paso 2 del hilo de unificación completo** (sombra → default → gate selectivo) + rebanada vertical Lectura B validada sobre A92418 real · **primera cura visible del parpadeo** · `DataPlane` observa el gate per-señal (`observeSensorWrite`→shadowStats) y **bloquea selectivamente** solo si el modo existe y != Gen (Real/Local/HwSim) y la fuente no está permitida (los sim en Gen y los mode-less pasan siempre · el motor no se toca) + default de arranque (`applySensorDefaults`: sim→'Gen', hw→mode-less) en `controlBusInit` · 3 ficheros src/ + 1 destilado (`lru_destilado_paso3_override_modo_s167.md`) · build verde 2187 · T-S165 paso 2 cerrado + T-S167-N1 nueva (productor 'sim1' sin Play) · 0 núcleo

* **s166 · 2026-05-29** — sesión de código · materialización del **paso 1** del hilo de unificación del arbitraje (alfiler s165): `ControlBus` gana capa per-sensor **aditiva e inerte** (`SensorMode` Real/Local/HwSim/Gen + `SENSOR_PERMISSIONS` + `allowsSensor` permisivo-sin-modo + 6 entradas `window.ControlBusDebug`) + nuevo `sourceConflictDetector.ts` (detector/mapa vivo solo-lectura · `diagnoseSensor`/`sourceMap`/`conflicts`/`liveWriters` · `window.SourceMapDebug` · módulo aparte para evitar ciclo ControlBus⇄DataPlane) + registro en `controlBusInit` · **nadie consulta `allowsSensor` en escritura aún** (el gate real es el paso 2, modo sombra) · **fix colateral deuda s165**: `Sim2.tsx` `buildMixerData` alineado al contrato Mixer ampliado en s165 (catalog + isVisible + toggleSignal · paridad cross-host botones Sensores/Displays) destapado por `tsc` (validación s165 fue runtime, vite dev) · build verde **2187 módulos** (+1) + **399/399 tests** · 2 decisiones arbitradas (alcance: árbitro+detector · ubicación: módulo aparte · fix Sim2: cablear) · **T-S165-UNIFICACION-ARBITRAJE avanza** + **T-S166-CONTRATO-LEGO-INLINE** nueva menor · 0 ZIP (escritura directa MCP · commit manual)
* **s165 · 2026-05-28** — sesión mixta · 3 cambios de código Sim4/Mixer (preset A-1 a 5 legos + botones **Sensores** y **Displays** en el lego Mixer compartido) + **frente de unificación del arbitraje articulado** (sin código): alfiler `lru_alfiler_unificacion_arbitraje_senales_s165.md` + referencia `lru_referencia_capa_datos_s165.md` (lectura íntegra de 9 ficheros núcleo de la capa de datos) · disparador parpadeo en el Visor (motor↔hardware real) · dos árbitros nombrados juntos (LruBar/RouterMode por-hwObj + Mixer/motor por-sensor) · 4 decisiones arbitradas (por-sensor · generalizar generadores · motor gateado · arbitraje uniforme) + §8.1·C el ControlBus es el árbitro + refinamientos §6b (modo sombra · detector de conflicto · transición suave · estrella polar `Generador`) · **T-S165-UNIFICACION-ARBITRAJE sembrada** como paraguas (absorbe T-S164/T-S163/T-S162/T-S157/T-S161-DOC-1) · paso 1 listo para ejecutar en frío
* **s164 · 2026-05-28** — sesión quirúrgica de **higiene DataPlane** (T1 de T-S163-RUIDO-DERIVADOS-ISA) · arbitraje **R1 (fix de granularidad)** tras audit empírico previo obligatorio de 8+ ficheros del repo (`SimService.ts` íntegro · `DataPlane.ts` · `InstrumentBridge_04.ts` · `useIB04Registry.ts` · `sim-catalog.ts` · `pfd-catalog.ts` · Col4/`Detalle01`/`ArticGrid01` · `Sim1ChartPanel`) · **1 línea efectiva** modificada en `SimService.ts` (`if (activeCount > 0)` envolviendo la llamada final a `computeDerived()` del `demoTick`) + 6 líneas de comentario · build verde 2186 módulos (sin cambio vs s162/s163) + **399/399 tests unit verdes** (0 tests tocados) · **T-S163-RUIDO-DERIVADOS-ISA cerrada operativamente** por T1+R1 (con motor parado y 0 hwObjs en Rnd, los 4 derivados ISA dejan de escribirse al DataPlane a 20 fps con `source: sim1` · síntoma exacto que Manuel observó en SIM4) · resolución táctica no estructural (deja abierto asimetría de source + monolinguismo singleton + acoplamiento dominio/motor = espacio de R3) · destilado breve `lru_destilado_t_s163_ruido_derivados_isa_r1_s164.md` · **hallazgo colateral del audit → T-S164-DUAL-WRITE-MASIVO nueva informativa** (`pushToRegistry` escribe ~250 sensores del catálogo completo al DataPlane con `source: sim1` a ~10 fps cuando el motor corre = ~2500 writes/s · el ruido de derivados era la punta visible de un patrón mayor) · audit rectifica cabecera s28/s34 (los "instrumentos de Col4" son clips Animate vía IB_04 con gating propio `epsilon=0.0005`, no lectores directos del DataPlane) · Camino Y **6ª aplicación limpia consecutiva** (s159·s160·s161·s162·s163·s164) · sub-patrón familia "audit empírico previo …" **3ª instancia** (variante "destapa hallazgo colateral mayor") · ZIP + PS1 con SHA256
* **s163 · 2026-05-27** — sesión densa de implementación **Camino Y** del handoff s162→s163 · materialización del **consumidor inaugural Cat.6** (Observador1 v2) que cierra el ciclo articulación→materialización→consumidor abierto en s161+s162 · 4 ficheros src/ tocados quirúrgicamente (`Observador1.tsx` shell + `Obs1SensorBrowser.tsx` browser + `Obs1LiveValues.tsx` tabla + `observador1.css` grid 5→6 columnas + layout 2 filas facetas) · 4 facetas activas (quantity + ata + enlaceFrom + enlaceTo) con AND implícito · columna `enlace` con composición `'swObjId.canal'` (D4=C) · D1=todos+badge huérfanos (gris itálico + sufijo `(sin enlaces)` + tooltip) · D2.B label em-dash `'id — Tipo'` · D3-refinada F2.b verticales simétricas (post-validación visual: 1ª fila inicialmente horizontal pero F2.a hubiera roto paridad · F2.b honra Patrón 6) · build verde 2186 módulos (sin cambio vs s162) + **399/399 tests unit verdes** · **alfiler T-S163-RUIDO-DERIVADOS-ISA articulado** (~13 KB · 12ª aplicación disciplina alfiler s125 · voz fundacional Manuel preservada verbatim *"hay que evitar que se escriban continuamente vmo tas sat tat que estan siempre activados, esto es ruido"* · 3 articulaciones en una formulación · 3 opciones de resolución R1/R2/R3 articuladas sin arbitrar · bug de granularidad documentado en demoTick · frente NO abierto en s163) · destapado por uso operativo del consumidor inaugural en SIM4 al ver activity stream con `source: sim1` permanente · Camino Y **5ª aplicación limpia consecutiva** (s159 + s160 + s161 + s162 + s163) · sub-patrón s162-s163 "audit empírico previo refina contrato del destilado" **2ª instancia confirmada formalizable** · sub-patrón s163 "consumidor inaugural empírico destapa deudas preexistentes invisibles hasta ahora" 1ª instancia · 0 ZIP (escritura directa vía MCP arbitrada por Manuel)
* **s162 · 2026-05-27** — sesión mixta (bloque articulatorio breve + materialización densa) · **materialización Cat.6 enlace N:M aguja↔sensor** según plan §11 del destilado s161 · 6 pasos ejecutados · contrato `Facet` extendido con unión discriminada `SensorFacet | EdgeFacet` · 3 firmes s160 migradas a `kind:'sensor'` · `facetEnlace.ts` nuevo (134 líneas) con `from`/`to`/`values` y `to(hwObjId): EnlaceValue[]` ergonómico (D1=B refinamiento sobre destilado §6.4) · helper `expandEnlaceToSensorIds()` exportado · `query()` extendido con facetIds reservados `enlaceFrom`/`enlaceTo` (D4=C híbrido) · factoría `createRegistryWithEnlaces(enlaces)` por inyección (D5=A) · 12 tests unit nuevos · **alfiler T-S162-ENLACES-SUSTRATO articulado** (14.55 KB · 11ª aplicación disciplina alfiler s125 · voz fundacional Manuel preservada verbatim · frente NO abierto · trigger T1/T2 articulados) · build verde 2186 módulos + **399/399 tests unit verdes** · Camino Y 4ª aplicación limpia (audit empírico previo + refinamiento honesto D5 sobre strawman §6) · candidato muy firme a formalización en `LRU_METODO` · T-S160-CAT6-CAND cerrada operativamente por materialización · T-S162-ENLACES-SUSTRATO + T-S162-VITEST-ALIAS nuevas informativas · sub-patrón emergente 1ª instancia: "audit empírico previo refina contrato del destilado" · 0 ZIP (escritura directa vía MCP arbitrada por Manuel)
* **s161 · 2026-05-26** — sesión articulatoria pura · 6ª catalogación **Cat.6 enlace N:M aguja↔sensor** articulada con material empírico vivo (audit 7 ficheros del repo) · destilado anillo 2 producido `lru_destilado_facetas_cat6_enlace_s161.md` (38.52 KB · 13 secciones) · 5 decisiones de diseño D-Q1..Q5 arbitradas por Manuel una a una (D-Q4=B extensión del contrato `Facet` con `kind` discriminante = decisión arquitectural mayor) · átomo `EnlaceValue = {swObjId, hwObjId, channelMap, activeChannels}` · `EdgeFacet<TFrom, TTo, TValue>` con métodos `from`/`to`/`values` paralelos al `SensorFacet` existente · filtros planos `enlaceFrom`/`enlaceTo` con AND implícito en `query()` · output uniforme `sensorId[]` siempre (D-Q3.bis=ii) · materialización lazy pura · 0 cambios src/ · Camino Y 3ª aplicación limpia (audit empírico previo al strawman) · candidato firme a formalización en `LRU_METODO` · T-S160-CAT6-CAND cerrada arquitecturalmente · T-S161-DOC-1 nueva informativa (doble representación redundante del cableado) · agnosticismo del `channelMap` respecto a la naturaleza de sus claves articulado como propiedad emergente del diseño (P5 llevado a sub-estructura) – NO ambigüedad arqueológica · plan de materialización futura con 6 pasos articulados
* **s160 · 2026-05-26** — sesión densa de implementación · materialización de la **API consultora multi-faceta** articulada como strawman en destilado s158 · 14 ficheros desplegados (7 nuevos en `src/realm/queryFacets/` + 3 docs nuevos + 4 modificados Observador1) · build verde 2185 módulos + 25/25 tests unit + validación funcional confirmada por Manuel (dropdowns quantity+ata operativos · composición AND coherente entre SensorBrowser y LiveValues) · **cierre operativo T-S151-N2 por absorción arquitectural** · 5º modo de cierre articulado (cuando la solución estructural rinde innecesaria la formulación original) · cita gold s160 *"el panel enlaces es la llave para unir ambos mundos"* registrada · T-S160-CAT6-CAND firme sembrada (6ª catalogación candidata) + T-S160-CAT3-A menor · 2ª aplicación limpia disciplina Camino Y · candidato firme a formalización en `LRU_METODO` · 4 ajustes empíricos al strawman s158 (A delegación DataPlane · B Cat.4 diferida · C Cat.5 diferida · D Cat.1 derivada SIM_CATALOG)
* **s159 · 2026-05-25** — sesión densa de implementación **Camino Y** del handoff s158→s159 · materialización del consumidor inaugural Observador1 v1 · 14 ficheros desplegados (10 nuevos + 4 modificados) · 5 sub-paneles + página + overlay draggable + botón 🔍 en Header + ruta `/Observador1` · backend consumido `getSpyService()` s152 sin tocar el lego existente · build verde + validación empírica funcional confirmada por Manuel (4 paneles renderizando + captura del SpyService activa) · T-S158-OBS-1 cerrada por cuarto modo (cierre negativo por ausencia de trigger empírico tras uso real) · 0 tensiones nuevas · 10ª aplicación operativa disciplina alfiler s125 (consumidor inaugural cerrando parcialmente el ciclo destilado→código) · 7 decisiones arbitradas D-s159-1..4 + Opción 1 + Opción A + cierre T-S158-OBS-1

* **s158 · 2026-05-24** — sesión arquitectural dedicada **Frente H** del handoff s157→s158 · destilado anillo 2 producido `lru_destilado_facetas_multivista_s158.md` 46.7 KB 724 líneas · API consultora multi-faceta articulada sobre 5 catalogaciones (4 materializadas + 1 implícita TipoSensor · scope cerrado por arbitraje Manuel s158) · 5 decisiones de diseño D-Q1..Q5 arbitradas · cierre arquitectural T-S151-N2 (4ª reformulación v4) · T-S158-P13-CAND nominada (candidato cristalización fundacional 8ª · formulación refinada respecto al alfiler s157: sustrato = entidades funcionales no DataPlane) · 9ª aplicación operativa disciplina alfiler s125 forma alfiler→destilado · 0 cambios src/ · regla de disputa activa hasta materialización
* **s157 · 2026-05-24** — sesión de cierre arqueológico frente observatorio + migración documental masiva al histórico (Compresión II · ~80 KB liberados) · 3 piezas articulatorias producidas (`lru_inventario_consumidores_s157.md` 35.4 KB + `lru_ampliacion_signalengine_trinidad_s157.md` 29.1 KB + `lru_geografia_desconexion_uso_real_s157.md` 42.8 KB) + alfiler facetas multivista `lru_alfiler_facetas_multivista_s157.md` 48.5 KB (8ª aplicación disciplina alfiler s125) · validación empírica runtime rectifica T-S151-N2 (NO son dos namespaces · ES un sensorId en dos contenedores) · 29 handoffs + 34 sesiones condensados a bloques únicos · 7ª aplicación disciplina alfiler s125 · 0 cristalizaciones fundacionales · 0 cambios núcleo

* **s156 · 2026-05-24** — sesión articulatoria pura · alfiler `lru_alfiler_observatorio_reformulado_s156.md` + `lru_observatorio_mapa_fuentes_s156.md` reformulando observatorio como herramienta diagnóstica preventiva (no lujo) · Manuel articula 3 dependencias del observatorio
* **s155 · 2026-05-23** — 3 frentes · T-S154-N1 TraceView cerrada 13/13 + T-S155-N1 tokens responsive sistémicos cerrada parcial (diminishing returns) + alfiler observatorio creado · 2ª aplicación operativa alfiler discipline s125
* **s154 · 2026-05-22** — T-S153-N4 cerrada 12/13 legos vía tokens responsive · TraceView diferido como T-S154-N1 · 17 archivos tocados · sub-patrón "tres convenciones coexistentes" 1ª aplicación
* **s153 · 2026-05-22** — validación T-S152-VALIDACION (2/4 fases al 100% + 1 parcial + 1 diferida) + 5 fixes contextuales · T-S153-N1 + T-S153-N4 articuladas con profundidad arquitectural · patrón 6.b cristalizado (Manuel detiene deriva)
* **s152 · 2026-05-21** — materialización en cascada 5 fases del DataPlane viewer dentro de Analyzer1 (gap mayor del proyecto desde s55 cerrado parcialmente) · 8 ficheros nuevos ~129 KB · sub-patrón "vista hermana adaptativa por protocolo" 3 aplicaciones · alfiler `lru_alfiler_dataplane_viewer_s152.md`
* **s151 · 2026-05-21** — sesión articulatoria mayor · LruToolbar materializada (frente táctico) + alfiler `lru_alfiler_dos_universos_nomenclatura_s151.md` 36 KB · T-S151-N2 articulada como cristalización candidata fundacional 8ª
* **s150 · 2026-05-21** — 2 frentes productivos PanelGrid drag-to-edge + compactación automática + 1 articulatorio descubrimiento naming `sim1_*` · T-S149-DRAG-EDGE cerrada (reclassificación) · T-S150-N1 abierta acoplada con T-S149-SEMANTICA
* **s149 · 2026-05-20** — 5 frentes · SignalTreePanel materializado (9ª aplicación canónico) + TechModal cristalizado como modal canónico + 2 bugs TechModal + cambio cosmético "Explorador"→"Sensores"
* **s148 · 2026-05-19** — frente único denso · navegación horizontal EnlacesPanel cross-host · 6 iteraciones diseño + cristalización hook `useAtlasPassthrough` con migración total · 4 fixes mecánicos nombrados · 0 regresiones
* **s147 · 2026-05-18** — 4 frentes encadenados · FallosPanel 8ª aplicación canónico + doctrina doble vida modal+grid cristaliza con 4 instancias + Atlas Sim4 reorganizado (col A compuestas · col B pantalla-completa)
* **s146 · 2026-05-18** — atomización completa ScenariosPanel a 4 legos componibles cross-host · 17 ficheros nuevos · 2 legos cross-host (SystemsPanel + ScenariosPanel) · doctrina "caras funcionales" + sub-patrón "doble vida modal+grid" articulados
* **s145 · 2026-05-17** — sesión articulatoria pura · alfiler `lru_alfiler_modelo_estado_registry_s145.md` con 5 caminos arquitectónicos · T-S144-N1 reformulada como asimetría persistencia · 0 código · pivote a proyecto paralelo arquitectura
* **s144 · 2026-05-17** — EnlacesPanel Sim4 A-4 + investigación arqueológica Switches digitales + 2 legos duales nuevos (LruBar + LruSeq con pattern T-S142-N1 dual) · 7 aplicaciones firmes
* **s143 · 2026-05-16** — Frente B' DisplaysMenu + Sim4Header (2ª/3ª aplicación T-S142-N1) · limpieza CSS legacy bonus · análisis empírico Sistemas vs Explorador Signals
* **s142 · 2026-05-15** — Camino V materializado · 2 legos nuevos VisorPanel + SimBrowser · D-s142-V promoción a lego compartido · sub-patrón "lego autocontenido" 1ª aplicación T-S142-N1
* **s141 · 2026-05-15** — Opción 3 HostBridge unificado materializada · 7 sub-pasos + 1 fix
* **s140 · 2026-05-15** — 7 hitos · `useEngineBridge` + SignalLab Sim4 A-2 + fix wheel + T-S130-LEGO-1 cerrada + bug latente statusColor cerrado
* **s139 · 2026-05-14** — sesión histórica densa · 5 hitos · Sim4 Fase 1 cerrada · Iteración 2 cerrada · D-s138-PARTIDA materializada · ciclo s136→s139 completado
* **s138 · 2026-05-14** — materialización documental pura · 3 HTMLs hermanos producidos · D-s138-PARTIDA arbitrada (materialización Sim4 Fase 1 para s139)
* **s137 · 2026-05-14** — 6ª sesión articulatoria densa · alfiler canónico Sim4 unificación atriles + mapa plataforma HTML 60 KB (3 estratos + 3 dimensiones transversales) + postulado P-N18 candidato sembrado · 13 deudas nuevas con triggers
* **s136 · 2026-05-14** — cristalización arquitectural NavBridge (D-s136-N · router como single source of truth) + alfiler semilla Sim4 articulado · sub-patrón "pregunta detona refactor" 1ª aplicación
* **s135 · 2026-05-13** — D-s134-D ejecutada en 3 bloques + bug latente basename canónico + reactividad URL→Atlas con triple barrera anti-loop · P-candidato s135 "Partitura compartida" con metáfora orquestal articulada por Manuel
* **s134 · 2026-05-12** — sesión transformada de bugs Sim3 a cristalización arquitectural mayor · doble candidato P16 "Coreografía sobre Atlas" + P17 "DataPlane multi-protocolo" · D-s134-D URL universal arbitrada · alfiler `lru_hilo_director_panel_s134.md` 75.83 KB
* **s133 · 2026-05-11** — 3ª sesión LRU Código · rename Atlas P13 (Primacía visual del nombrado) + Sim3 Lab operativo + bug ficticio wheel descartado · cristalizaciones P13/P15/Glosario Atlas articuladas
* **s132 · 2026-05-10** — 2ª sesión LRU Código · refactor layout 5×8 paridad Sim2 + Overview transpuesto + favoritos minimenu modal · sub-patrones "overflow recorta menus" + "Camino B mejorado"
* **s131 · 2026-05-09** — 1ª sesión LRU Código · SignalLab lego compartido (5/5 aplicaciones) + Atlas Sim1 lego nuevo ~1560 líneas · 4 fixes encadenados scroll-snap CSS
* **s130 · 2026-05-08** — 3 bloques código real · paridad sensors-bar Sim2 + SignalsPanel extraído a lego (3ª aplicación patrón) + 7º tool 'Sensores' Sim2 + cabecera fija SignalLab · 19 sesiones sin código rotas · proyecto LRU Código creado
* **s129 · 2026-05-07** — sesión densa mantenimiento documental · 4 palancas M+N+P+P-bis sobre MAPA (−61% · −76% acumulado vs s127) · D6 política unificada formalizada + bipartición §1.B.1/§1.B.2 + R2 checklist arranque · Anillo 1.B con 6 piezas · 0 código
* **s128 · 2026-05-07** — sesión densa documental · 8 palancas A+B+C+E+F+I+Q+O sobre MAPA (−38.7%) · 4 sesiones de deuda absorbidas · Anillo 1.B inaugurado con 2 piezas · convención capitalización formalizada
* **s127 · 2026-05-07** — sesión densa código · paquete completo nav UX Sim2 (rail-X/Y clicables + flechas + indicador X/N + atajos teclado) + bloque emergente 3 bugs latentes lego sliders/Mixer + 9+ ajustes empíricos · 2ª aplicación operativa disciplina alfiler s125
* **s126 · 2026-05-06** — sesión densa código · Bloque C-1 ampliado · cabecera Sim2 con 4 barras encadenadas operativa contra motor real · 4 sub-pasos validados · fix mismatch IDs + corrección semántica FRD→FDR · T-S122-port-systems cerrada en dos tiempos s125+s126
* **s125 · 2026-05-06** — sesión articulatoria larga · alfiler `lru_hilo_realms_estructura_plana_s125.md` ~480 líneas (categoría documental nueva "hilo conductor con alfileres") + rename quirúrgico Bloque B
* **s124 · 2026-05-05** — sesión densa código · TransportPanel extraído como lego autocontenido (último gran portaje pendiente cluster T-S122) · 3 tensiones cerradas + 1 nueva
* **s123 · 2026-05-05** — integración fundacional · Postulado 12 "El hardware físico es sustrato del sistema" integrado al núcleo como `LRU_FUNDAMENTOS §11sexies` cerrando 5 aplazamientos consecutivos s118-s122 · 8ª aplicación destilado→núcleo · 2 cerradas (T-S117-F1 + T-S119-1)

---

### §B · Sesiones s100, s117, s120, s121, s122 (condensadas · histórico previo)

5 bloques densos preservados en s129 absorbidos en bloque único en s157 (precedente: misma compresión aplicada a handoffs §5.archivo-II). Detalle íntegro accesible vía git del MAPA pre-s128 + handoffs individuales.

* **s122 · 2026-05-05** — Sim2 conectado al motor singleton compartido con Sim1 + fusión Mixer+Chart en tool combinado "Osciloscopio" · 8 tensiones nuevas · 5º aplazamiento P12 (territorio sin precedentes)
* **s121 · 2026-05-04** — ChartPanel autocontenido (2º lego del proyecto) · 4 bugs históricos Sim1 cerrados por materialización · 1ª instancia patrón "extracción a lego revela bugs latentes"
* **s120 · 2026-05-04** — MixerPanel autocontenido (1er lego validado del proyecto) · arquitectura tools-paneles articulada s119 validada · contrato data+actions desacoplado del host
* **s117 · 2026-05-03** — 7ª cristalización fundacional sembrada · Postulado 12 "El hardware físico es sustrato del sistema" destilado 913 líneas · 11 citas verbatim · articulación "laboratorio real/virtual de señales" emerge en sesión
* **s100 · 2026-04-26** — materialización T-S85-A3 · S1000D migrado al realm aviation (`src/realm/aviation/s1000d.ts` + 24 tests) · 12ª aplicación ciclo destilado→código · borrado huérfano `src/types/ata.ts`

---

### §C · Sesiones s89→s99 (condensadas)

* **s99 · 2026-04-26** — Postulado 11 integrado al núcleo como `§11quinquies` · 4 modos cierre de tensión formalizados en `LRU_METODO §8.1-8.3` · 11 postulados explícitos integrados
* **s98 · 2026-04-26** — 6ª cristalización fundacional sembrada · destilado `lru_estructura_sectorial_s98.md` 507 líneas · 4º modo "cierre tardío con reconocimiento de continuidad" articulado
* **s97 · 2026-04-26** — cierre T-S84-A8 Tanda 4 · `aviation/profile.ts` AviationProfile singleton + cierre regla disputa s84 · 6ª cristalización fundacional sembrada T-S97-F1
* **s96 · 2026-04-26** — Tanda 3 T-S84-A8 · 9 ficheros aviation (4088 líneas) · sub-patrón "split de tests" + "escape P10 en superRefine cruzado" consolidado
* **s95 · 2026-04-25** — Tanda 2 T-S84-A8 · `aviation/organizations.ts` + `NominatedPersonDef` + `SimulatedContextDef` con `notEqualToReal: true` (1ª materialización P10 en código vivo)
* **s94 · 2026-04-25** — materialización T-S93-DIETA · 5 disciplinas D1-D5 formalizadas en `LRU_METODO §3` · MAPA −15% (200.3 KB→165 KB)
* **s93 · 2026-04-25** — cleanup metodológico denso · 5 tensiones cerradas negativamente · destilado `lru_dieta_documental_s93.md` arbitrado punto por punto
* **s92 · 2026-04-25** — T-S90-A1 cerrada por materialización · cobertura tests del schema (5 ficheros nuevos · 94 tests pasando) · T-S92-T1 ganancia colateral −30 CVEs npm
* **s91 · 2026-04-25** — T-S84-F1 cerrada por integración · 4ª cristalización fundacional culminada · Postulado 10 al núcleo · primera vez ninguna cristalización pendiente
* **s90 · 2026-04-25** — Tanda 1 T-S84-A8 materializada · 7ª aplicación ciclo destilado→código · `roles.ts` + `manifest.ts` + `AviationRealmManifest`
* **s89 · 2026-04-25** — T-S85-F1 cerrada por integración · 5ª cristalización fundacional culminada · Postulado pluralidad realms al núcleo como `§11ter`

---

*LRU Platform · Bitácora de sesiones · 2026-06-15 · sincronizado al cierre de s212 · s89→s193 condensadas en §A/§B/§C (backfill s167→s185 previo · s186 + s187 + s188 en saneamiento s188 · s189 + s190 + s191 + s192 + s193 en saneamiento s193) · Compresión I s129 + Compresión II s157*
