The serve window shipped at 15 minutes, which was calibrated for a
constraint that no longer exists. In the first cut the browser held a
flat 5-minute staleTime decoupled from the server, so the two windows
compounded and a short server window was the only bound on observable
staleness. Since the review round, a server-cached response is stale on
arrival client-side, so nothing compounds.
What 15 minutes actually cost: no background job keeps a snapshot warm —
only a picker open reads or refreshes it — and the browser's react-query
cache dies with the tab. So opening the agent form once today and again
tomorrow was guaranteed to be a cold miss both times, paying the full
1-15s daemon round trip. The cache only helped inside a single session.
Guarding a change that happens on a scale of days (a CLI upgrade) with a
minutes-scale window bought no freshness for that price.
Freshness is not traded away, because the two constants do different
jobs and only the other one governs it: any served snapshot older than
modelCatalogRevalidateAfter (60s, unchanged) queues a background refresh,
and the client treats a cached answer as immediately revalidatable. The
observable behaviour stays "this open shows the old list, the next open
shows the new one" at any window length.
The window stays bounded rather than infinite because a *failed*
discovery report deliberately leaves the snapshot in place — a transient
failure must not empty the picker — so for a runtime whose CLI was
uninstalled or logged out, expiry is the only thing that eventually
retires the stale catalog. 24h covers the dominant daily-usage pattern
while keeping that backstop inside a day.
Both backends derive from the one constant (the in-memory retainFor and
the Redis TTL), so there is no second place to keep in sync. Storage is
not a factor: a measured snapshot is 206 bytes for a Claude catalog and
6.4KB for a codex catalog with full thinking levels and service tiers.
Adds a behavioural regression test instead of restating the constant: an
hours-old and a just-inside-the-window snapshot are still served, a
past-the-window one is a miss, and serving an aged snapshot still
enqueues the refresh plus its wakeup hint. It also fails loudly if the
window is ever dropped back to minutes or if the revalidate threshold
stops being well below it.
Co-authored-by: Eve <eve@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>