diff --git a/.changeset/method-aware-retries.md b/.changeset/method-aware-retries.md
deleted file mode 100644
index 6a1b20e..0000000
--- a/.changeset/method-aware-retries.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-'@smooai/fetch': major
----
-
-SMOODEV-3375: Retries never duplicate a side effect: they now check the HTTP method, and timeouts cancel the attempt. This changes a default, so it is a major release.
-
-**What was wrong.** Retries ignored the HTTP method. A `POST` that timed out or got a 429/5xx was sent again, up to twice more. In TypeScript the per-attempt timeout (mollitia's `Timeout` module) did not stop the losing request, so the first attempt kept running on the server while the retry sent it again. Image generation takes 20-50s against the 10s default timeout, so it billed three images and returned nothing. Any non-idempotent call (CRM writes, message sends, payments) could run its side effect more than once.
-
-**New default, in all five languages:**
-
-- **Only idempotent methods are retried:** `GET`, `HEAD`, `OPTIONS`, `TRACE`, `PUT` and `DELETE` (RFC 9110 §9.2.2).
-- **`POST`, `PATCH` and any other method make exactly one attempt.** You get that attempt's own error (`HTTPResponseError` / `TimeoutError` and the equivalents in other languages), not the retries-exhausted wrapper.
-- **A 429 with `Retry-After` on a `POST` is not retried either.**
-- **`onRejection` is never called for these requests**, so it cannot turn their retries back on.
-
-There are two ways to opt back in:
-
-- Send a non-empty `Idempotency-Key` header, in any casing. The server then deduplicates.
-- Set `retry: { allowNonIdempotent: true }`. The name in each language:
- - TypeScript: `allowNonIdempotent`
- - Python and Rust: `allow_non_idempotent`
- - Go and .NET: `AllowNonIdempotent`
-
-Eligibility is checked after the pre-request hooks and the auth provider run, so a hook can add the key. The client-side rate limiter's retry loop is unchanged, because it rejects requests before anything is sent.
-
-**Timeouts cancel the attempt.** TypeScript now aborts each attempt with an `AbortController`, combined with the caller's own `signal`, so the connection is closed before any retry. A `fetch` that ignores `signal` still times out on schedule. Rust, Go, Python and .NET already cancelled the attempt. A new test in every language proves the first attempt's connection closes. Go now also waits for the cancelled attempt to finish before it retries. Python now puts a hard deadline on each attempt with `asyncio.timeout`. Before, httpx only had per-phase timeouts, so a server that kept sending bytes slowly never timed out.
-
-**Also fixed:** in TypeScript, a request the caller aborted is no longer retried.
-
-**Other API changes:**
-
-- **TypeScript:**
- - New exports: `isIdempotentMethod`, `isRetryEligible`, `IDEMPOTENCY_KEY_HEADER` and the `RetryOptions` type.
- - `options.retry` and `FetchBuilder.withRetry` now take a partial. It is merged over the defaults, so `retry: { allowNonIdempotent: true }` keeps every other default.
- - The `signal` that `fetch` receives now combines the caller's signal with the timeout's. It is no longer the caller's own object.
-- **Rust:**
- - `RetryOptions` gains `allow_non_idempotent`. This breaks existing `RetryOptions { .. }` struct literals.
- - `RetryOptions` now implements `Default`, so literals can end with `..Default::default()` from now on.
- - New: `is_idempotent_method`, `is_retry_eligible` and `IDEMPOTENCY_KEY_HEADER`.
-- **Go:**
- - New: `RetryOptions.AllowNonIdempotent`, `IsIdempotentMethod` and `IdempotencyKeyHeader`.
- - The module path moves to `/v4`.
-- **Python:**
- - New: `RetryOptions.allow_non_idempotent`.
- - `is_idempotent_method` and `IDEMPOTENCY_KEY_HEADER` are exported.
-- **.NET:**
- - New on `RetryPolicy`: `AllowNonIdempotent`, `IsIdempotentMethod`, `IsRetryEligible` and `IdempotencyKeyHeader`.
- - `Microsoft.SourceLink.GitHub` goes to 10.0.303. 8.0.0 pulls in `Microsoft.Build.Tasks.Git` 8.0.0, which has an advisory against it (GHSA-23fw-v26w-5fgq), and a clean restore now fails with NU1902.
-
-**Why a major version:**
-
-- A caller that relied on `POST` retries quietly loses them.
-- In Rust, adding a field to a struct that callers construct breaks their code. The 3.7.1 entry below explains why that must not ship as a minor.
-
-The shared `spec/retry-idempotency-corpus.json` pins the rule. Each language's tests run it against a real local server and count the requests the server received.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e0c2ce..75cc29a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,57 @@
# @smooai/fetch
+## 4.0.0
+
+### Major Changes
+
+- 1a84633: SMOODEV-3375: Retries never duplicate a side effect: they now check the HTTP method, and timeouts cancel the attempt. This changes a default, so it is a major release.
+
+ **What was wrong.** Retries ignored the HTTP method. A `POST` that timed out or got a 429/5xx was sent again, up to twice more. In TypeScript the per-attempt timeout (mollitia's `Timeout` module) did not stop the losing request, so the first attempt kept running on the server while the retry sent it again. Image generation takes 20-50s against the 10s default timeout, so it billed three images and returned nothing. Any non-idempotent call (CRM writes, message sends, payments) could run its side effect more than once.
+
+ **New default, in all five languages:**
+ - **Only idempotent methods are retried:** `GET`, `HEAD`, `OPTIONS`, `TRACE`, `PUT` and `DELETE` (RFC 9110 §9.2.2).
+ - **`POST`, `PATCH` and any other method make exactly one attempt.** You get that attempt's own error (`HTTPResponseError` / `TimeoutError` and the equivalents in other languages), not the retries-exhausted wrapper.
+ - **A 429 with `Retry-After` on a `POST` is not retried either.**
+ - **`onRejection` is never called for these requests**, so it cannot turn their retries back on.
+
+ There are two ways to opt back in:
+ - Send a non-empty `Idempotency-Key` header, in any casing. The server then deduplicates.
+ - Set `retry: { allowNonIdempotent: true }`. The name in each language:
+ - TypeScript: `allowNonIdempotent`
+ - Python and Rust: `allow_non_idempotent`
+ - Go and .NET: `AllowNonIdempotent`
+
+ Eligibility is checked after the pre-request hooks and the auth provider run, so a hook can add the key. The client-side rate limiter's retry loop is unchanged, because it rejects requests before anything is sent.
+
+ **Timeouts cancel the attempt.** TypeScript now aborts each attempt with an `AbortController`, combined with the caller's own `signal`, so the connection is closed before any retry. A `fetch` that ignores `signal` still times out on schedule. Rust, Go, Python and .NET already cancelled the attempt. A new test in every language proves the first attempt's connection closes. Go now also waits for the cancelled attempt to finish before it retries. Python now puts a hard deadline on each attempt with `asyncio.timeout`. Before, httpx only had per-phase timeouts, so a server that kept sending bytes slowly never timed out.
+
+ **Also fixed:** in TypeScript, a request the caller aborted is no longer retried.
+
+ **Other API changes:**
+ - **TypeScript:**
+ - New exports: `isIdempotentMethod`, `isRetryEligible`, `IDEMPOTENCY_KEY_HEADER` and the `RetryOptions` type.
+ - `options.retry` and `FetchBuilder.withRetry` now take a partial. It is merged over the defaults, so `retry: { allowNonIdempotent: true }` keeps every other default.
+ - The `signal` that `fetch` receives now combines the caller's signal with the timeout's. It is no longer the caller's own object.
+ - **Rust:**
+ - `RetryOptions` gains `allow_non_idempotent`. This breaks existing `RetryOptions { .. }` struct literals.
+ - `RetryOptions` now implements `Default`, so literals can end with `..Default::default()` from now on.
+ - New: `is_idempotent_method`, `is_retry_eligible` and `IDEMPOTENCY_KEY_HEADER`.
+ - **Go:**
+ - New: `RetryOptions.AllowNonIdempotent`, `IsIdempotentMethod` and `IdempotencyKeyHeader`.
+ - The module path moves to `/v4`.
+ - **Python:**
+ - New: `RetryOptions.allow_non_idempotent`.
+ - `is_idempotent_method` and `IDEMPOTENCY_KEY_HEADER` are exported.
+ - **.NET:**
+ - New on `RetryPolicy`: `AllowNonIdempotent`, `IsIdempotentMethod`, `IsRetryEligible` and `IdempotencyKeyHeader`.
+ - `Microsoft.SourceLink.GitHub` goes to 10.0.303. 8.0.0 pulls in `Microsoft.Build.Tasks.Git` 8.0.0, which has an advisory against it (GHSA-23fw-v26w-5fgq), and a clean restore now fails with NU1902.
+
+ **Why a major version:**
+ - A caller that relied on `POST` retries quietly loses them.
+ - In Rust, adding a field to a struct that callers construct breaks their code. The 3.7.1 entry below explains why that must not ship as a minor.
+
+ The shared `spec/retry-idempotency-corpus.json` pins the rule. Each language's tests run it against a real local server and count the requests the server received.
+
## 3.7.1
### Patch Changes
diff --git a/dotnet/SmooAI.Fetch/SmooAI.Fetch.csproj b/dotnet/SmooAI.Fetch/SmooAI.Fetch.csproj
index b8c8034..26896f1 100644
--- a/dotnet/SmooAI.Fetch/SmooAI.Fetch.csproj
+++ b/dotnet/SmooAI.Fetch/SmooAI.Fetch.csproj
@@ -10,7 +10,7 @@
$(NoWarn);CS1591
SmooAI.Fetch
- 3.7.1
+ 4.0.0
SmooAI
SmooAI
Resilient HTTP client for .NET with Polly-based retry, timeouts, typed JSON responses, and auth token injection. Port of @smooai/fetch.
diff --git a/go/fetch/go.mod b/go/fetch/go.mod
index e69e9ad..4cefa86 100644
--- a/go/fetch/go.mod
+++ b/go/fetch/go.mod
@@ -1,4 +1,4 @@
-module github.com/SmooAI/fetch/go/fetch/v3
+module github.com/SmooAI/fetch/go/fetch/v4
go 1.23.0
diff --git a/go/fetch/version.go b/go/fetch/version.go
index a14279d..d1e2978 100644
--- a/go/fetch/version.go
+++ b/go/fetch/version.go
@@ -1,4 +1,4 @@
package fetch
// Version is the current version of the smooai-fetch Go package.
-const Version = "3.7.1"
+const Version = "4.0.0"
diff --git a/package.json b/package.json
index a737c76..7064bfc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@smooai/fetch",
- "version": "3.7.1",
+ "version": "4.0.0",
"description": "A powerful fetch client library built on top of the native `fetch` API, designed for both Node.js and browser environments. Features built-in support for retries, timeouts, rate limiting, circuit breaking, and Standard Schema validation.",
"homepage": "https://github.com/SmooAI/fetch#readme",
"bugs": {
diff --git a/python/pyproject.toml b/python/pyproject.toml
index e03197c..26bc0f6 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "smooai-fetch"
-version = "3.7.1"
+version = "4.0.0"
description = "A resilient HTTP fetch client with retries, timeouts, rate limiting, and circuit breaking."
# readme = "README.md"
authors = [{ name = "SmooAI", email = "brent@smooai.com" }]
diff --git a/python/src/smooai_fetch/__init__.py b/python/src/smooai_fetch/__init__.py
index f6ae9e2..96a3fcf 100644
--- a/python/src/smooai_fetch/__init__.py
+++ b/python/src/smooai_fetch/__init__.py
@@ -4,7 +4,7 @@
and circuit breaking.
"""
-__version__ = "3.7.1"
+__version__ = "4.0.0"
# Core client
# Builder
diff --git a/rust/fetch/Cargo.lock b/rust/fetch/Cargo.lock
index 9a81280..cd0b02a 100644
--- a/rust/fetch/Cargo.lock
+++ b/rust/fetch/Cargo.lock
@@ -1230,7 +1230,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "smooai-fetch"
-version = "3.7.1"
+version = "4.0.0"
dependencies = [
"opentelemetry",
"opentelemetry_sdk",
diff --git a/rust/fetch/Cargo.toml b/rust/fetch/Cargo.toml
index 1904dcf..8d214ff 100644
--- a/rust/fetch/Cargo.toml
+++ b/rust/fetch/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "smooai-fetch"
-version = "3.7.1"
+version = "4.0.0"
edition = "2021"
description = "A resilient HTTP fetch client with retries, timeouts, rate limiting, and circuit breaking."
license = "MIT"