Deep-dive findings, all three latent on every 24/7 install:
- font_manager.metrics_cache and display_manager._text_width_cache were
unbounded dicts keyed by (text, id(font)). Two problems: keys embed
the measured TEXT, so ever-changing strings (a clock, a live score, a
ticker) grow them without limit; and id()-keying without holding a
reference means a garbage-collected font's id can be recycled by a
DIFFERENT font, silently returning wrong widths/metrics (classic
plugins create fonts per render, so this is reachable). Both caches
are now LRU-bounded (1024) and pin the font in the entry so its id
stays valid. metrics_cache also keyed on the text itself instead of
hash(text), removing a collision path.
- _write_snapshot_if_due logged failures at DEBUG — invisible at the
default level. The snapshot's mtime is the web UI's display mirror
AND its hardware-liveness proxy, so a quiet failure freezes the
mirror and makes health checks lie (seen in the field: a stale
root-owned /tmp file froze it for a day). Failures now WARN, rate-
limited to once per 5 minutes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam