Moe Hamade 657fee0de6 fix: stop the GitHub release check exhausting its own rate limit
Opening the About sheet runs a release check, and an exhausted quota fed
itself: only successes were cached, and a 403 reporting zero remaining
was classed retryable, so every sheet open spent three more requests
rediscovering the same limit. Unauthenticated GitHub allows 60 requests
an hour per IP, and over Tor that IP is an exit node shared with every
other user on it, so the ceiling arrives far sooner than per-user maths
suggests.

Three changes:

- Conditional requests. The client now stores the release ETag and
  replays it as If-None-Match. GitHub does not charge a 304 against the
  rate limit, so revalidating an expired cache is free where an
  unconditional refetch costs one of the 60. This is why neither polling
  nor long polling is the right answer here.

- A rate-limit gate. X-RateLimit-Reset and Retry-After were read only to
  interpolate into an error string; they now set a deadline before which
  no request is sent at all. While blocked, a stale cached release is
  served in preference to an error the user cannot act on. Clamped to an
  hour so a bad header cannot lock the feature out, and a reset time in
  the past falls back to a fixed backoff rather than unblocking a skewed
  clock immediately.

- Rate limits are no longer retried in-loop. The gate decides when it is
  worth asking again. A plain 403 is a permissions failure and is no
  longer retried either.

The gate's decision logic is pure and unit tested. The wiring around it
is not: that needs a MockWebServer, which is not currently a dependency.

Known gap: the cache and ETag are in memory only, so a process restart
still costs one request. Persisting them needs a Context threaded into
what is currently a context-free object; left as a follow-up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 15:56:25 +03:00
..
2025-12-14 21:55:41 +07:00
2026-07-29 03:41:16 +02:00