Bridge hat seit 5+h still gehangen — Container Up, asyncio idle im
selectors.select(), TCP-Verbindung zum RVS ESTABLISHED, aber keine
Events mehr verarbeitet. Klassischer Fall: NAT-Tabelle/Firewall hat
die TCP-Verbindung still gekillt (kein RST), Linux-Kernel mit Default-
Keepalive (2h idle) hat's nicht gemerkt, und der ws.ping()-Future hat
im Limbo gehangen ohne Exception zu werfen.
Schicht 1 — TCP-Keepalive aufm Socket:
SO_KEEPALIVE=1, TCP_KEEPIDLE=30s, TCP_KEEPINTVL=10s, TCP_KEEPCNT=3.
Halb-tote Verbindungen werden in ~1 min mit ECONNRESET sichtbar statt
nach 2h. Loest 80% der Faelle direkt.
Schicht 2 — Asyncio-Watchdog (_rvs_heartbeat_watchdog):
Separate Coroutine parallel zu _rvs_heartbeat. Letzterer markiert
_last_heartbeat_ok nach jedem erfolgreichen pong. Watchdog checkt
alle 20s: > 60s stale → ws.close() + transport.close() als Notausgang.
Schuetzt gegen ws.ping()-Limbo.
Schicht 3 — File-Based Liveness Thread:
Separater OS-Thread (NICHT asyncio) — immun gegen asyncio-Hangs.
Schreibt /shared/health/bridge_alive periodisch. Wenn
_last_heartbeat_ok > 180s stale: os._exit(1), Docker restart_policy
uebernimmt. Last-Resort wenn Schichten 1+2 versagen.
Plus: chat_history-Render nach Reload bezog nur #chat-box, nicht
#chat-box-fs (Vollbild). Wer im FS-Modus reloaded hat sah eine leere
Box statt der History. Jetzt rendert der Handler in beide Boxen
(gleicher Pattern wie addChat / addAriaFile).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>