Skip to content

Flag Subscribe and EnableCardColumnOnHold as naturally idempotent - #514

Merged
jeremy merged 3 commits into
mainfrom
idempotent-subscribe
Jul 31, 2026
Merged

jeremy merged 3 commits into
mainfrom
idempotent-subscribe

Conversation

@jeremy

@jeremy jeremy commented Jul 31, 2026

Copy link
Copy Markdown
Member

Why

#480: Subscribe and EnableCardColumnOnHold are set-membership POSTs — subscribing an already-subscribed user and enabling an already-enabled on-hold section are server-side no-ops — so a retried request cannot duplicate anything. They carry the same contract as the already-flagged CompleteTodo/PauseQuestion/SubscribeToCardColumn but were never flagged, leaving them un-retried in every SDK for no reason.

What

  • @basecampIdempotent(natural: true) on both operations in the Smithy spec; full make generate flows it through behavior-model.json (idempotent: true), all five metadata emitters, and Go's doWithRetry call-site booleans.
  • maxAttempts stays 2 — whether these belong at 3 is Retry metadata fidelity: Go and Python ignore per-operation retry_on and max_attempts #479's question, deliberately untouched.
  • Parity gate source-of-truth counts: 69→71 idempotent, 186→188 idempotent∪readonly.
  • SPEC.md (ceiling narrative, Swift divergence note, Appendix E) and SECURITY.md follow the counts; the sweep also surfaced and fixes two pre-existing off-by-ones (183→182 ops at max 3, 187→186-now-188 retry-eligible).
  • New conformance case: Subscribe mocked [503, 200] must make 2 requests — mirrors the CompleteTodo case; dispatch branches in the Go/TS/Python/Kotlin runners, Ruby skip-list entry (GET-only retry).

Verification

Full make green locally (exit 0). Conformance: Go/Kotlin/Python print PASS: Subscribe POST retries when marked idempotent, Ruby SKIP (Ruby SDK only retries GET), TS suite 118 passed (unknown-op dispatch throws there, so the case demonstrably ran).

Behavior change: these two POSTs now retry on 429/503 (and network errors in Go/Python/Swift) — bundled ahead of v0.11.0 so the release carries all retry-contract changes at once.

Closes #480.


Summary by cubic

Marks Subscribe and EnableCardColumnOnHold as naturally idempotent so SDKs safely retry these POSTs on transient errors. Aligns behavior with existing idempotent POSTs and closes #480.

  • New Features
    • These POSTs now retry on 429/503 (and network errors where supported: Go/Python/Swift).
    • Added @basecampIdempotent(natural: true) in the Smithy spec; regeneration updates all SDK metadata and Go doWithRetry call sites.
    • New conformance test for Subscribe with mocked [503, 200]; runners updated (Ruby skipped due to GET-only retries).
    • Docs and parity counts updated: 71 idempotent ops, 188 retry-eligible; fixed two prior count drifts. maxAttempts stays 2 (see Retry metadata fidelity: Go and Python ignore per-operation retry_on and max_attempts #479 for any change).

Written for commit c5e91ab. Summary will update on new commits.

Review in cubic

jeremy added 3 commits July 31, 2026 00:12
Both POSTs are set-membership operations: subscribing an already-
subscribed user and enabling an already-enabled on-hold section are
server-side no-ops, so a retried request cannot duplicate anything.
That is the same contract CompleteTodo/PauseQuestion/SubscribeToCardColumn
already carry, and BC3's controllers confirm it (#480).

@basecampIdempotent(natural: true) in the Smithy spec flows through
regeneration to all six SDKs: behavior-model idempotent: true, the five
metadata emitters, and Go's doWithRetry call sites. The parity gate's
source-of-truth counts move 69->71 idempotent, 186->188 idempotent-or-
readonly. maxAttempts stays 2 — whether these belong at 3 is #479's
question, not this change.

Closes #480.
SPEC.md's ceiling narrative, Swift divergence note, and Appendix E move
to the new totals; SECURITY.md's retry-behavior section names the five
flagged POSTs. Also corrects two pre-existing count drifts the sweep
surfaced: 183->182 ops at max 3 and 187->186 (now 188) retry-eligible —
both had been off by one since the per-op ceiling landed.
Mirrors the CompleteTodo case — mock [503, 200-with-subscription],
assert two requests and no error. Dispatch branches added to the Go,
TypeScript, Python, and Kotlin runners; Ruby skips it with the standard
GET-only-retry reason.
Copilot AI review requested due to automatic review settings July 31, 2026 07:13
@github-actions github-actions Bot added documentation Improvements or additions to documentation typescript Pull requests that update TypeScript code ruby Pull requests that update the Ruby SDK go kotlin swift spec Changes to the Smithy spec or OpenAPI conformance Conformance test suite python Pull requests that update the Python SDK labels 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.

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 commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

Copilot errored on both review attempts (service-side; same pattern on cli#62 today). All required checks green; full local make exit 0 with the conformance case passing in all five runners. Merging; #515 stacks on this branch and will be retargeted to main.

@jeremy
jeremy merged commit 9b317ab into main Jul 31, 2026
44 of 46 checks passed
@jeremy
jeremy deleted the idempotent-subscribe branch July 31, 2026 07:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conformance Conformance test suite documentation Improvements or additions to documentation go kotlin python Pull requests that update the Python SDK ruby Pull requests that update the Ruby SDK spec Changes to the Smithy spec or OpenAPI swift typescript Pull requests that update TypeScript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two operations under-classified as non-idempotent: Subscribe and EnableCardColumnOnHold

2 participants