Two latent issues found in a self-review pass:
- LayoutContext._fit_cache was an unbounded dict (the image cache got an
LRU cap, the text-fit cache didn't). Cache keys embed the fitted TEXT,
so a plugin fitting changing strings — a live game clock, a ticker —
on a 24/7 service grows it forever. Now LRU-bounded at 512 entries via
the same pattern as the image cache.
- fit_image returned the caller's ORIGINAL image object when the source
was already RGBA at target size (contain/fill_height, no ink crop).
ImageFitResult is documented as an independent copy, and LayoutContext
caches results — an aliased image lets later mutations of the source
corrupt cached fits (or vice versa). Copy in that branch.
Both covered by new regression tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam