Fix 429 retry header and parallelize portal fetches#27
Merged
Conversation
Read X-RateLimit-Retry-After (the header HelpScout sends) with Retry-After as fallback, and guard non-numeric values so a bad header falls back to exponential backoff instead of setTimeout(NaN). Fetch ProductLift portals in parallel in get_feature_requests, matching the synthesize_feedback path. Add retry tests with fake timers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
HelpScoutClient.apiGetread the standardRetry-Afterheader on 429s, but HelpScout sendsX-RateLimit-Retry-After(rate-limiting docs). The header was never found, so every 429 fell back to blind exponential backoff.setTimeout(NaN), an instant retry with no wait.get_feature_requestsfetched ProductLift portals serially, whilesynthesize_feedback/generate_product_planalready fetch them in parallel viaPromise.allSettled.What changed
x-ratelimit-retry-after, falls back toretry-after, and only uses the header when it parses to a positive finite number; otherwise exponential backoff.MAX_429_RETRIESbehavior is unchanged.get_feature_requestsuses the samePromise.allSettledpattern as the other tools. Per-portal warning isolation and message format are unchanged.Tests
New
src/helpscout.test.ts(fake timers + stubbed fetch): honoursX-RateLimit-Retry-After, backs off when the header is absent, throws afterMAX_429_RETRIES, and falls back to backoff (not an instant retry) on a non-numeric header. 70 tests passing overall.🤖 Generated with Claude Code