Skip to content

READMEs: fix every sample that misstates the API - #507

Merged
jeremy merged 6 commits into
mainfrom
docs/readme-example-fixes
Jul 31, 2026
Merged

jeremy merged 6 commits into
mainfrom
docs/readme-example-fixes

Conversation

@jeremy

@jeremy jeremy commented Jul 31, 2026

Copy link
Copy Markdown
Member

Six commits, one per SDK. Every defect below was proven against the real SDK before editing, and every corrected snippet was re-proven green. Nothing here touches behavior — READMEs plus five doc comments in Kotlin/Swift source that showed the same phantom signatures.

Ruby — todos.create takes todolist_id:, not project_id:

The Quick Start's documented call fails at argument binding, before any HTTP:

ArgumentError: unknown keyword: :project_id

The corrected call (with http_post stubbed on the service singleton) POSTs /todolists/67890/todos.json.

Go — five fences didn't compile

Each reproduced offline as the exact compiler error (scratch module + replace directive, GOPROXY=off); all corrected snippets go build + go vet clean:

cannot range over projects (variable of type *basecamp.ProjectListResult)   [both Quick Start programs]

not enough arguments in call to account.Campfires().List
	have (context.Context)
	want (context.Context, *basecamp.CampfireListOptions)
not enough arguments in call to account.Campfires().ListLines
	have (context.Context, int64)
	want (context.Context, int64, *basecamp.CampfireLineListOptions)

not enough arguments in call to account.Webhooks().List
	have (context.Context, int64)
	want (context.Context, int64, *basecamp.WebhookListOptions)

no new variables on left side of :=                                          [OTel fence redeclares hooks]

undefined: prometheus                                                        [chain-hooks fence]

Also "Requires Go 1.25 or later" → 1.26, matching go.mod. The README's fragment conventions (undeclared ctx, use(), top-level return err) are established style and stay.

Kotlin — error-handling sample didn't compile; tables lied

  • todos.get(projectId = 123, todoId = 456) — the generated signature is get(todoId: Long) (generated/services/todos.kt).
  • The when over sealed BasecampException covered 9 of 11 subclasses; a non-exhaustive when statement over a sealed subject is a compile error, and the SDK's own ErrorTest.kt pins all 11 branches ("This ensures all branches compile (exhaustive when)"). Added DiscoverySelection/DeviceFlow branches and their Error Types rows.
  • Configuration Options documented maxRetries/baseRetryDelay, but BasecampClientBuilder has no setter for either and the client constructor is internal — marked as fixed defaults.
  • consoleHooks sample output showed Projects.List; the generated OperationInfo emits Projects.ListProjects.
  • Same phantom todos.get in BasecampException/BasecampClient doc comments.

Swift — phantom arity plus an API that needs macOS 13

  • todos.get(projectId: 123, todoId: 456) — the generated signature is get(todoId: Int).
  • The same fence used Task.sleep(for: .seconds(...)) while Package.swift declares .macOS(.v12). Proven under a macOS 12 target:
error: 'sleep(for:tolerance:clock:)' is only available in macOS 13.0 or newer

Replaced with the nanoseconds form the SDK's own retry path uses (green under the same target). Also fixed the phantom signatures in BasecampError/AccountClient/Pagination doc comments.

TypeScript — list() auto-paginates; the docs claimed the opposite

"List methods return a single page of results by default" inverts reality. Behavioral proof against a stubbed fetch serving two pages:

HTTP calls: 2; items returned: 3; meta.totalCount: 3; Array.isArray: true
maxItems:2 -> items: 2; truncated: true

The Pagination section now leads with auto-pagination and ListResult/.meta, shows maxItems, and reframes fetchAllPages/paginateAll as the escape hatch for raw client.GET calls. The helper callbacks read (r) => r.json() as Promise<any[]> because bare r.json() is Promise<unknown> under the repo's strict/NodeNext config and fails tsc --noEmit; all three replacement blocks typecheck clean under that config.

All six SDKs — "45 services" → 46

Stale since EverythingService landed. Verified per SDK: 46 Kotlin accessors, 46 Swift accessors, 46 Ruby service classes (47 files minus base_service.rb), 46 Python service modules (excluding _base/_async_base), matching SPEC.md's counts.

Deliberately not touched


Summary by cubic

Fix incorrect README code samples and doc comments across all SDKs to match real API signatures, pagination behavior, and compile-time constraints. No runtime changes; updates service count references to 46.

  • Bug Fixes
    • Ruby: todos.create uses todolist_id, not project_id.
    • Go: All snippets compile; iterate result.Projects, pass nil to Campfires/Webhooks list calls, fix OTel hooks reassignment (construct client after), add prometheus import; "Requires Go 1.26".
    • Kotlin: Use todos.get(todoId), make BasecampException sample exhaustive with DiscoverySelection/DeviceFlow; mark maxRetries/baseRetryDelay as fixed defaults; console hooks show Projects.ListProjects; fix doc comments.
    • Swift: Use todos.get(todoId), replace Task.sleep(for:) with nanoseconds for macOS 12; fix doc comments and the pagination example.
    • TypeScript: list() auto-paginates and returns a ListResult with .meta.totalCount and .truncated; show maxItems; update raw pagination examples using fetchAllPages/paginateAll from @37signals/basecamp with typed r.json().
    • All SDKs: "45 services" → 46 to reflect EverythingService.

Written for commit 60be468. Summary will update on new commits.

Review in cubic

The Quick Start passes project_id: 12345, but the generated signature
has no such keyword -- todos are addressed by todolist
(POST /todolists/:todolist_id/todos.json) and TodosExtensions never
overrides create. Running the documented call raises
ArgumentError: unknown keyword: :project_id at argument binding,
before any HTTP.

Also: "45 account-scoped services" is stale since EverythingService
landed; the generated set is 46.
Copilot AI review requested due to automatic review settings July 31, 2026 06:45
@github-actions github-actions Bot added typescript Pull requests that update TypeScript code ruby Pull requests that update the Ruby SDK go kotlin swift python Pull requests that update the Python SDK labels Jul 31, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9df3c594f6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread typescript/README.md Outdated
@jeremy jeremy added 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.

Pull request overview

This is a documentation-only PR that corrects README code samples and a handful of in-source doc comments across all six SDKs (Go, TypeScript, Ruby, Swift, Kotlin, Python) so that every published snippet matches the real generated API surface, pagination behavior, and compile-time requirements. It fits the repo's "generated-from-spec" architecture: none of the changes touch runtime behavior — they only bring hand-maintained docs back in sync with the generated services. I verified each corrected signature, count, and behavioral claim against the generated code:

  • Go: Projects().List returns *ProjectListResult (iterate result.Projects); Campfires().List/ListLines and Webhooks().List all require the trailing options arg; go.mod is go 1.26; the OTel fence's hooks = reassignment and added prometheus import are correct.
  • Ruby todos.create takes todolist_id: (no project_id:); Kotlin/Swift todos.get(todoId) match the generated single-arg signatures; Kotlin's when is now exhaustive over all 11 BasecampException subclasses; consoleHooks emits Projects.ListProjects; maxRetries/baseRetryDelay are indeed absent from BasecampClientBuilder.
  • TypeScript list() auto-paginates and returns ListResult<T> with .meta, maxItems bounds it, the 10,000-page cap and @37signals/basecamp exports of fetchAllPages/paginateAll all check out.
  • Service count is 46 in each SDK (Swift/Kotlin/Ruby/Python file counts confirmed).

Changes:

  • Fix incorrect method signatures/arguments in README + doc-comment samples (Ruby todos.create, Go list calls, Kotlin/Swift todos.get).
  • Correct behavioral/config documentation (TS auto-pagination + ListResult, Kotlin exhaustive error when/fixed retry settings/hook output, Go 1.26 and compiling fences).
  • Update the stale "45 services" → "46" across all six SDKs.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

One minor consistency gap worth noting for a follow-up (outside the changed lines, so not annotated inline): swift/Sources/Basecamp/BasecampError.swift:17 still shows Task.sleep(for: .seconds(seconds)) — the exact macOS 13-only API this PR replaced in swift/README.md — within the same doc-comment sample whose signature was corrected on line 10. Aligning it with the Task.sleep(nanoseconds:) form would fully deliver the PR's stated goal on the declared macOS 12 floor.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
go/README.md Iterate result.Projects, pass nil options to Campfires/Webhooks list calls, fix OTel reassignment + prometheus import, bump to Go 1.26
typescript/README.md Rewrite Pagination to document list() auto-pagination, ListResult/.meta, maxItems, and typed fetchAllPages/paginateAll
ruby/README.md Remove invalid project_id: from todos.create; 45→46 services
swift/README.md Fix todos.get(todoId:), replace macOS 13-only Task.sleep(for:) with nanoseconds form; 45→46 services
swift/Sources/Basecamp/BasecampError.swift Fix phantom todos.get signature in doc comment
swift/Sources/Basecamp/AccountClient.swift Fix phantom todos.get signature in doc comment
swift/Sources/Basecamp/Pagination.swift Fix phantom todos.list signature in doc comment
kotlin/README.md Fix todos.get, make error when exhaustive (+DiscoverySelection/DeviceFlow rows), mark retry settings fixed, correct hook output, 45→46
kotlin/.../BasecampException.kt Fix phantom todos.get signature in doc comment
kotlin/.../BasecampClient.kt Fix phantom todos.get signature in doc comment
python/README.md 45→46 generated services

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings July 31, 2026 06:51
@jeremy
jeremy force-pushed the docs/readme-example-fixes branch from 9df3c59 to 2937fd9 Compare July 31, 2026 06:51
@github-actions github-actions Bot removed the documentation Improvements or additions to documentation label Jul 31, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2937fd999c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread go/README.md
@jeremy
jeremy force-pushed the docs/readme-example-fixes branch from 2937fd9 to 665442f Compare July 31, 2026 06:56
@jeremy

jeremy commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

Applied Copilot's catch: BasecampError.swift:17's doc sample carried the same macOS 13-only Task.sleep(for:) as the README fence it mirrors — now the nanoseconds form, folded into the Swift commit. rg 'Task.sleep\(for' swift/ is clean.

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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 31, 2026 06:58

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 added 4 commits July 31, 2026 00:00
Five fences failed to compile against the SDK they document; each
defect reproduced as the exact compiler error offline (scratch module
with a replace directive, GOPROXY=off), and each corrected snippet
builds and vets clean:

- Both Quick Start programs range over the *ProjectListResult struct
  ("cannot range over projects"); iterate result.Projects, matching
  the root README's correct form.
- Campfires().List / ListLines and Webhooks().List omit their options
  parameter ("not enough arguments in call"); pass nil.
- The OTel fence redeclares hooks in the same scope ("no new variables
  on left side of :="); plain assignment, and construct the client
  after the hooks alternatives so the custom providers actually reach
  it when copied as written.
- The chain-hooks fence uses prometheus.DefaultRegisterer without
  importing prometheus ("undefined: prometheus"); add the import.

Also: "Requires Go 1.25 or later" contradicted go.mod's go 1.26.

The README's fragment conventions (undeclared ctx, use(), top-level
return err) are established style and stay as they are.
The Error Handling fence could not compile: todos.get has no projectId
parameter (generated signature is get(todoId: Long)), and the when over
the sealed BasecampException covers 9 of 11 subclasses -- a
non-exhaustive when statement over a sealed subject is a compile error,
and the SDK's own ErrorTest pins all 11 branches. Add the
DiscoverySelection and DeviceFlow branches and their Error Types rows.

Also:
- "45 services" is stale since EverythingService landed; the generated
  set is 46 accessors.
- The Configuration Options table lists maxRetries and baseRetryDelay,
  but BasecampClientBuilder exposes no setter for either and the client
  constructor is internal -- mark them as fixed defaults.
- The consoleHooks sample output shows Projects.List; the emitted
  operation name is Projects.ListProjects (OperationInfo in the
  generated service).
- The same phantom todos.get(projectId, todoId) call appears in the
  BasecampException and BasecampClient doc comments; fix those too.
The Error Handling fence calls todos.get(projectId: 123, todoId: 456);
the generated signature is get(todoId: Int). The same fence sleeps with
Task.sleep(for: .seconds(...)), which is only available on macOS 13+
while Package.swift declares .macOS(.v12) -- under a macOS 12 target
swiftc rejects it ("'sleep(for:tolerance:clock:)' is only available in
macOS 13.0 or newer"). Use the nanoseconds form the SDK's own retry
path uses.

Also:
- "45 services" is stale since EverythingService landed; the generated
  set is 46 accessors.
- The phantom projectId parameter also appears in the BasecampError and
  AccountClient doc comments, and Pagination's doc example calls
  todos.list(projectId:todolistId:) where the real signature takes
  todolistId only; fix those too.
- BasecampError's doc sample carries the same macOS 13-only sleep as
  the README fence it mirrors; fix it the same way.
Stale since EverythingService landed; the generated set is 46 service
modules.
…site

"List methods return a single page of results by default" inverts
reality: generated list() follows Link: rel="next" up to a 10,000-page
safety cap and returns ListResult<T> (an Array<T> subclass) with
.meta.totalCount / .meta.truncated, bounded per call by maxItems.
Proven behaviorally: against a stubbed fetch serving two pages, list()
made 2 HTTP calls and returned all 3 items; list({maxItems: 2})
returned 2 with truncated: true.

Rewrite the Pagination section around the real behavior and reframe
fetchAllPages/paginateAll as the escape hatch for raw client.GET calls.
The helper callbacks read (r) => r.json() as Promise<any[]> because
bare r.json() is Promise<unknown> under the repo's strict/NodeNext
config and fails tsc --noEmit; all three replacement blocks typecheck
clean.
@jeremy
jeremy force-pushed the docs/readme-example-fixes branch from 665442f to 60be468 Compare July 31, 2026 07:01
Copilot AI review requested due to automatic review settings July 31, 2026 07:01

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 merged commit 8f3c48d into main Jul 31, 2026
47 of 50 checks passed
@jeremy
jeremy deleted the docs/readme-example-fixes branch July 31, 2026 07:05
jeremy added a commit that referenced this pull request Jul 31, 2026
)

* Ruby README: document the retry contract the transport actually implements

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.

* TS: make the pagination helper docstring examples pass strict typecheck

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go kotlin python Pull requests that update the Python SDK ruby Pull requests that update the Ruby SDK swift typescript Pull requests that update TypeScript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants