# Changelog ## 1.9.2-beta1 — unreleased ### Fixed — server resilience (#142) - **A single flapping device can no longer saturate the event loop.** A new load-aware, per-device reconnect throttle (`lib/reconnect-throttle.js`) gates genuine reconnects *before* the heavy register work (DB writes + playlist build). The verdict is per-device; global event-loop lag only multiplies an already-flagged device's backoff and never throttles a healthy one. Hard ceiling + cold-start warm-up so a full-fleet reconnect after a deploy is never throttled. - **`device_status_log` growth is bounded.** Added `idx_device_status_log_device_ts`, a global retention sweep (`pruneStatusLog`, `STATUS_LOG_RETENTION_DAYS` default 3) covering removed/idle devices and the `offline_timeout` path, and de-duplicated the table's `CREATE TABLE`. - **`content-ack` spam de-duplicated.** Repeated identical `(device_id, content_id, status)` reports are suppressed within `CONTENT_ACK_DEDUP_MS` (default 10s). - **Provisioning cleanup window corrected.** Unclaimed provisioning devices are now swept after 24h (the code used `365 * 86400` — a year — contradicting its own comment). ### Added — observability (#142) - **Event-loop lag telemetry** via `perf_hooks.monitorEventLoopDelay()`. Sampled to a bounded `event_loop_lag` table (indexed + pruned, `LAG_TELEMETRY_RETENTION_DAYS`) and surfaced on `/api/status` as `loop_lag` (mean/p50/p99/max + band). ### Maintenance - Operators whose `device_status_log` is already bloated from a pre-1.9.2 deployment should reclaim disk with a **one-time manual `VACUUM`** in a maintenance window; retention now bounds further growth. Auto-VACUUM is intentionally not enabled. See [`docs/maintenance-device-status-log.md`](docs/maintenance-device-status-log.md). ## 1.9.1-beta3 — unreleased ### Fixed — Tizen player - **#118 Sticky "Not authenticated" banner.** On TV sleep/wake the socket reconnects and a heartbeat could fire on the fresh, not-yet-registered socket; the server rejected it with `device:auth-error`, which the player showed as a *sticky* toast over still-playing content (and, worse, dropped its saved credentials and re-paired). Heartbeats are now gated on a per-connection `authenticated` flag (set only between `device:registered` and `disconnect`/`auth-error`), the heartbeat timer is stopped on `connect`/`disconnect`/ `auth-error`, the stale banner is cleared on `device:registered`, and the `auth-error` toast is non-sticky so any transient case self-clears. - **#119 `app_version` stuck at `1.0.0`.** The hardcoded constant made every Tizen device report `1.0.0` regardless of the installed `.wgt`. The version now resolves at runtime from `config.xml` via the Tizen application API, with a fallback constant that `build-wgt.sh` stamps from `config.xml`'s `version=""`. ### Added — Tizen player - **Video walls (`wall:sync`).** The Tizen player now supports wall membership: when the payload carries `wall_config`, a new `WallController` positions the stage (vw/vh) as this screen's slice of the wall and drives the single-zone player as leader or follower. The leader broadcasts `wall:sync` at 4Hz; followers align their index and keep their video locked to the leader's clock with a latency-compensated drift controller (hard-seek past 0.3s, gentle ±3% playbackRate nudge past 0.05s), and request an immediate position on (re)connect via `wall:sync-request`. Mirrors the web player (the Android player has no wall support). Per-tile `rotation` is not applied yet (web-player parity). Wall emits are gated on auth + connection so a pre-register tick can't trip `device:auth-error`. - **Multi-zone layouts (Android parity).** The Tizen player now renders assigned layouts, not just fullscreen single-zone. A new `ZoneRenderer` (ports the Android `ZoneManager`) positions zones by percent geometry with `z_index`/`fit_mode`/background, groups assignments by `zone_id` (unassigned content goes to the first zone), and rotates each zone independently with the same per-item schedule gating (#74/#75). `app.js` selects the renderer from `payload.layout`; single-zone playback is unchanged. (Video walls `wall:sync` are still Android-only.) - **#121 Remote commands.** Added a `device:command` handler (`refresh`, `launch`, `screen_on`, `screen_off`, plus honest no-op toasts for `update`/`reboot`/`shutdown`, which need B2B/MDM privileges a sideloaded app lacks). Removed the dead `device:reload` listener (the server never emitted it) in favour of `device:command` `refresh`. - **#120 Dashboard preview.** Added `device:screenshot-request` / `device:remote-start` / `device:remote-stop`. Images capture for real; `