Two compounding issues in the sliding-window edge loading:
- loadOlder()/loadNewer() kept pushing windowStart/windowEnd outward
even when a fetch came back empty (e.g. extending into future dates
with no data yet). Since nothing about the table's height or scroll
position changes when 0 rows come back, checkEdges() stayed
satisfied and the very next scroll/layout tick fired the same load
again, drifting the window further out forever. Now bounded by
MIN_LOADABLE_DATE/MAX_LOADABLE_DATE.
- loadOlder() and loadNewer() had independent in-flight guards and
could run concurrently, racing on the same rows/windowStart/windowEnd
state -- a scroll-position compensation write inside one (from
trimming the far end) fires a real scroll event that can kick off
the other direction mid-flight. Now serialized behind a shared
loadingWindow lock.