发布

  • fix(x): cross-cycle Gmail rate-limit cooldown honoring "Retry after" lockouts (#926)
    rowboat / apps/x Vitest suites (push) Has been cancelled
    rowboat / apps/harbor typecheck + tests (push) Has been cancelled
    rowboat / apps/x Electron package smoke test (push) Has been cancelled

    frostbyte_neo 发布于 2026-08-27 16:10:51 +00:00 | 143 次提交 在此版本后已推送到 main

    The follow-up deferred by #869. When Gmail rate-limits, the sync loop kept
    retrying every 30s with no backoff: each failed pass saved no state, redid
    the same listing/fetch burst from scratch, and its partial progress re-armed
    the very quota that killed the previous pass — a permanent lockout (reported
    on 0.8.9 after a rate-limit fix that only retried in-request, capped at 30s,
    and never read the "Retry after " Gmail puts in the error body).

    • New gmail-rate-limit.ts: rate-limit detection (moved from the client
      factory), deadline parsing (Retry-After header — delta-seconds or
      HTTP-date — plus the message-embedded ISO timestamp), and a process-wide
      cooldown. Gmail's own deadline when it names one (clamped to 6h);
      otherwise a per-strike escalating default (1m→15m, episode reset after a
      10m quiet spell). In-memory by design: lockouts are minutes long, and a
      stale on-disk deadline would silently pause sync after a crash.
    • gmailClient() retry hooks size the single in-request retry from the parsed
      deadline (a deadline beyond the 30s cap now fails fast instead of burning
      a doomed 2s retry) and arm the cooldown whenever a throttled request fails
      through to its caller — all 18 Gmail call sites feed it.
    • sync_gmail: performSync stands down while the cooldown is active (covers
      triggerSync wakes, prune, and the classify sweep) and init() stretches its
      sleep to Gmail's deadline instead of ticking every 30s through a lockout.
      gmail_sent_contacts' background refresh stands down too. User-initiated
      actions (send, archive, mark read) deliberately do not consult the
      cooldown — an explicit error beats a silent no-op.
    • One Sync Activity notice per lockout episode ("Rate limited by Gmail —
      next sync attempt at HH:MM"), deduped on the deadline. A warn-level
      progress event on a synthetic run: a run_complete would wrongly clear the
      sidebar's red failed state (any non-error outcome clears it) while Gmail
      is still locked out.

    Co-authored-by: Claude Fable 5 noreply@anthropic.com

    下载附件