Skip to content

Ruby retry docs from ground truth; TS docstring examples typecheck - #519

Merged
jeremy merged 2 commits into
mainfrom
docs/ruby-retry-ts-docstrings
Jul 31, 2026
Merged

jeremy merged 2 commits into
mainfrom
docs/ruby-retry-ts-docstrings

Conversation

@jeremy

@jeremy jeremy commented Jul 31, 2026

Copy link
Copy Markdown
Member

Fixes #510. Fixes #511.

Ruby — Retry Behavior rewritten from the transport's ground truth

The old section was wrong twice over, both proven by driving the real request_with_retry loop with only single_request stubbed:

  • "Retryable errors: 429, 502, 503, 504" — but the loop keys off retryable?, which is true for 500 and every other 5xx. Proof: a stubbed 500 on GET under max_retries: 3 produced exactly 3 invocations.
  • max_retries documented as "max retry attempts" — it is the total attempt budget. Proof: Config.new(max_retries: 0)zero HTTP calls, raising Basecamp::ApiError: Request failed after 0 attempts.

The rewrite also covers the contract corners the old text ignored: GET-only scope with the raw-path carve-outs; the read-timeout exception (Faraday maps read timeouts to Faraday::TimeoutError < ServerError, caught ahead of the NetworkError rescue and classified as a status-less, retryable? == false ApiError — proven empirically: stubbed read timeout → 1 attempt, no retry, vs ConnectionFailedNetworkError, 3 attempts); uncapped exponential backoff; Retry-After only via 429; the one-shot 401 refresh replay for all methods outside the budget; per-operation retry metadata being inert in Ruby; and retryable? as the actual transport predicate (the inverse of Python's "classification is only a hint" framing). Config comment, options table, env-var table (adding the missing BASECAMP_TIMEOUT/BASECAMP_MAX_RETRIES rows), and the same misstatements in config.rb's doc comments all aligned.

Ruby suite: 908 runs, 2079 assertions, 0 failures; rubocop clean.

TypeScript — docstring examples pass strict typecheck

fetchAllPages/paginateAll @example blocks used bare (r) => r.json()Promise<unknown> under Node fetch types, failing the parse callback's Promise<T[]> in any strict/NodeNext project configured like this repo (the defect #507 fixed in the README, still present in source). Red proof, the extracted examples verbatim under the repo-shaped harness:

error TS2322: Type 'Promise<unknown>' is not assignable to type 'Promise<unknown[]>'.

Green with the README's proven form (r) => r.json() as Promise<any[]>; tsc --noEmit clean; vitest 979 passed (local count).

These are the only two bare r.json() docstring sites in typescript/src (verified by grep — everything else is real code operating under the SDK's own types).


Summary by cubic

Updates Ruby retry docs to match the actual transport behavior and fixes TypeScript pagination examples to pass strict typecheck. Clarifies max_retries semantics to prevent misleading guidance.

  • Bug Fixes
    • Ruby docs: Rewrite Retry Behavior to reflect transport ground truth — GET-only; retryable?-based (429, all 5xx, network); no retry on read timeouts/4xx; max_retries = total attempts (0 sends none); exponential backoff with jitter and 429 Retry-After; one-shot 401 refresh; raw upload/download carve-outs; per-operation retry metadata ignored. Also align config.rb comments and README tables, adding BASECAMP_TIMEOUT and BASECAMP_MAX_RETRIES.
    • TypeScript: Make fetchAllPages/paginateAll @example blocks typecheck by casting r.json() to Promise<any[]>.

Written for commit 572bce9. Summary will update on new commits.

Review in cubic

jeremy added 2 commits July 31, 2026 01:35
…ments

The Retry Behavior section was wrong twice over (#510): it listed the
retryable statuses as "429, 502, 503, 504" when the transport keys off
the error's retryable? classification -- 500 and every other 5xx retry
on GET, as do connection-level network errors -- and it described
max_retries as "max retry attempts" when it is the TOTAL attempt budget:
3 means one initial attempt plus two retries, and max_retries: 0 sends
zero requests and raises "Request failed after 0 attempts".

Rewrite the section from the transport's ground truth: GET-only scope
with the raw-path carve-outs; classification-based statuses including
the read-timeout exception (Faraday maps read timeouts to a status-less
non-retryable ApiError, not NetworkError -- only connect-phase timeouts
retry); total-attempts semantics; uncapped exponential backoff;
Retry-After only via 429; the one-shot 401 refresh replay outside the
budget; per-operation retry metadata being inert; and retryable? as the
actual transport predicate rather than a hint. Align the config comment,
options table, and env-var table (adding the missing BASECAMP_TIMEOUT /
BASECAMP_MAX_RETRIES rows), and the same misstatement in config.rb's doc
comments.

Fixes #510.
The fetchAllPages/paginateAll @example blocks use bare (r) => r.json(),
which is Promise<unknown> under Node fetch types and fails against the
parse callback's Promise<T[]> in any strict/NodeNext project configured
like this repo -- the same defect #507 fixed in the README. Mirror the
README's proven form: (r) => r.json() as Promise<any[]>.

Fixes #511.
Copilot AI review requested due to automatic review settings July 31, 2026 08:37
@jeremy jeremy added documentation Improvements or additions to documentation typescript Pull requests that update TypeScript code ruby Pull requests that update the Ruby SDK labels Jul 31, 2026
@github-actions github-actions Bot removed the documentation Improvements or additions to documentation label Jul 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jeremy
jeremy requested a review from Copilot July 31, 2026 08:42
@jeremy
jeremy merged commit b9e15ae into main Jul 31, 2026
43 of 45 checks passed
@jeremy
jeremy deleted the docs/ruby-retry-ts-docstrings branch July 31, 2026 08:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ruby Pull requests that update the Ruby SDK typescript Pull requests that update TypeScript code

Projects

None yet

2 participants