The xoxp fallback in conversations_unreads calls GetConversationInfoContext
per channel to check for unreads. slack-go's standard Client does NOT
auto-retry on HTTP 429 — it returns *slack.RateLimitedError to the caller.
The scan loop handled ALL errors with continue at Debug level, so rate
limit errors were silently swallowed, causing non-deterministic results
(niebloomj confirmed 279/400 channels hit rate limits on their workspace).
This commit adds:
- limiter.CallWithRetry[T] generic helper in pkg/limiter: proactive
rate.Limiter.Wait + retry via caller-provided retryAfter callback
(no slack-go dependency in the limiter package)
- slackRetryAfter helper in the handler to extract RetryAfter from
*slack.RateLimitedError
- Applied to both GetConversationInfoContext and
GetConversationHistoryContext in scanTypeGroupForUnreads
- rateLimited counter surfaced in scan metadata so the LLM response
includes a WARNING when channels are skipped
- Rate limit errors promoted from Debug to Warn level
- 8 unit tests in pkg/limiter covering retry logic, exhaustion,
context cancellation, and typed return values