From a6c45b5cddcf938104e5d5a464b9658c4013ad33 Mon Sep 17 00:00:00 2001 From: fylorn <249551762+fylorn@users.noreply.github.com> Date: Sun, 1 Nov 2026 20:21:35 +0800 Subject: [PATCH 1/2] chore(release): prepare 0.87.1 Publish the insufficient-credit and terminal SSE health fixes with the watcher test stabilization. Update workspace package versions and document the fixes and unchanged protocol and storage compatibility. --- Cargo.lock | 44 ++++++++++++++++++++--------------------- Cargo.toml | 2 +- release-notes/0.87.1.md | 13 ++++++++++++ 3 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 release-notes/0.87.1.md diff --git a/Cargo.lock b/Cargo.lock index a88a6c3a..d18da011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3670,7 +3670,7 @@ dependencies = [ [[package]] name = "tw-api" -version = "0.87.0" +version = "0.87.1" dependencies = [ "serde", "serde_json", @@ -3681,7 +3681,7 @@ dependencies = [ [[package]] name = "tw-bedrock" -version = "0.87.0" +version = "0.87.1" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -3702,7 +3702,7 @@ dependencies = [ [[package]] name = "tw-breaker" -version = "0.87.0" +version = "0.87.1" dependencies = [ "serde", "serde_json", @@ -3710,7 +3710,7 @@ dependencies = [ [[package]] name = "tw-config" -version = "0.87.0" +version = "0.87.1" dependencies = [ "blake3", "libc", @@ -3736,7 +3736,7 @@ dependencies = [ [[package]] name = "tw-control" -version = "0.87.0" +version = "0.87.1" dependencies = [ "axum", "base64 0.22.1", @@ -3782,7 +3782,7 @@ dependencies = [ [[package]] name = "tw-dialect" -version = "0.87.0" +version = "0.87.1" dependencies = [ "memchr", "serde", @@ -3791,7 +3791,7 @@ dependencies = [ [[package]] name = "tw-engine" -version = "0.87.0" +version = "0.87.1" dependencies = [ "chrono", "serde", @@ -3805,7 +3805,7 @@ dependencies = [ [[package]] name = "tw-gateway" -version = "0.87.0" +version = "0.87.1" dependencies = [ "arc-swap", "async-stream", @@ -3856,7 +3856,7 @@ dependencies = [ [[package]] name = "tw-guard" -version = "0.87.0" +version = "0.87.1" dependencies = [ "aho-corasick", "base64 0.22.1", @@ -3875,7 +3875,7 @@ dependencies = [ [[package]] name = "tw-handover" -version = "0.87.0" +version = "0.87.1" dependencies = [ "libc", "serde", @@ -3888,7 +3888,7 @@ dependencies = [ [[package]] name = "tw-link" -version = "0.87.0" +version = "0.87.1" dependencies = [ "serde", "serde_json", @@ -3902,7 +3902,7 @@ dependencies = [ [[package]] name = "tw-observe" -version = "0.87.0" +version = "0.87.1" dependencies = [ "tokio", "tracing", @@ -3911,7 +3911,7 @@ dependencies = [ [[package]] name = "tw-pack" -version = "0.87.0" +version = "0.87.1" dependencies = [ "age", "blake3", @@ -3935,7 +3935,7 @@ dependencies = [ [[package]] name = "tw-plugin" -version = "0.87.0" +version = "0.87.1" dependencies = [ "libc", "rand 0.10.2", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "tw-pricing" -version = "0.87.0" +version = "0.87.1" dependencies = [ "arc-swap", "flate2", @@ -3964,14 +3964,14 @@ dependencies = [ [[package]] name = "tw-secret" -version = "0.87.0" +version = "0.87.1" dependencies = [ "thiserror 2.0.20", ] [[package]] name = "tw-store" -version = "0.87.0" +version = "0.87.1" dependencies = [ "blake3", "bytes", @@ -3991,7 +3991,7 @@ dependencies = [ [[package]] name = "tw-sync" -version = "0.87.0" +version = "0.87.1" dependencies = [ "age", "axum", @@ -4019,7 +4019,7 @@ dependencies = [ [[package]] name = "tw-types" -version = "0.87.0" +version = "0.87.1" dependencies = [ "serde", "serde_json", @@ -4028,7 +4028,7 @@ dependencies = [ [[package]] name = "tw-watch" -version = "0.87.0" +version = "0.87.1" dependencies = [ "libc", "notify", @@ -4039,7 +4039,7 @@ dependencies = [ [[package]] name = "tw-yaml" -version = "0.87.0" +version = "0.87.1" dependencies = [ "saphyr-parser", "serde_yaml_ng", @@ -4049,7 +4049,7 @@ dependencies = [ [[package]] name = "twcore" -version = "0.87.0" +version = "0.87.1" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 3f5cb7dc..8aae482e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ categories = ["network-programming", "web-programming::http-server"] # 二进制走 CalVer,crate 走 SemVer —— 两者是两回事,卖给不同的人。 # 这里是 crate 的版本。 -version = "0.87.0" +version = "0.87.1" [workspace.dependencies] # ── 内部 crate ─────────────────────────────────────────── diff --git a/release-notes/0.87.1.md b/release-notes/0.87.1.md new file mode 100644 index 00000000..08f9da49 --- /dev/null +++ b/release-notes/0.87.1.md @@ -0,0 +1,13 @@ +This release keeps upstreams with insufficient credits or exhausted quota out of later requests, including when an upstream returns its error inside an HTTP 200 event stream. + +**Upgrade notes** + +- The control-plane protocol version stays at 58; ThinkWatch Lite releases that include 0.87.0 also connect to 0.87.1. +- The request store's schema stays at 28; nothing is cleared. +- Existing configurations are unchanged. No new message codes. + +**Insufficient credits.** JSON error fields are decoded before balance failures are classified, so escaped insufficient-credit messages trigger failover and pause the affected upstream. Credit mentions in echoed requests or metadata do not turn a client error into a balance failure. + +**Errors inside streamed answers.** An upstream that returns HTTP 200 followed by an SSE error and a clean end of stream is no longer recorded as a success before its error is inspected. Balance and quota failures pause the upstream, server and authentication failures use the existing circuit breaker, and rate limits pause the upstream without adding a success or failure sample. A final error frame is recognized even without a trailing blank line. The original error still reaches the client, and an answer that has already delivered content is never replayed. + +**Watcher tests.** Filesystem integration tests no longer assume that writes finish within one debounce window or that a quiet event channel means descriptor handover has completed. Exact event batching and descriptor leak checks remain covered. Production watcher behavior is unchanged. From a7baf4bd0516550140172525820c0036ffd4034e Mon Sep 17 00:00:00 2001 From: fylorn <249551762+fylorn@users.noreply.github.com> Date: Sun, 1 Nov 2026 20:38:47 +0800 Subject: [PATCH 2/2] docs(release): link the insufficient-credit fix Keep the source pull request visible in the release summary even when the generated change list omits it. --- release-notes/0.87.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-notes/0.87.1.md b/release-notes/0.87.1.md index 08f9da49..4caffc0d 100644 --- a/release-notes/0.87.1.md +++ b/release-notes/0.87.1.md @@ -6,7 +6,7 @@ This release keeps upstreams with insufficient credits or exhausted quota out of - The request store's schema stays at 28; nothing is cleared. - Existing configurations are unchanged. No new message codes. -**Insufficient credits.** JSON error fields are decoded before balance failures are classified, so escaped insufficient-credit messages trigger failover and pause the affected upstream. Credit mentions in echoed requests or metadata do not turn a client error into a balance failure. +**Insufficient credits ([#379](https://github.com/ThinkWatchProject/ThinkWatch-Core/pull/379)).** JSON error fields are decoded before balance failures are classified, so escaped insufficient-credit messages trigger failover and pause the affected upstream. Credit mentions in echoed requests or metadata do not turn a client error into a balance failure. **Errors inside streamed answers.** An upstream that returns HTTP 200 followed by an SSE error and a clean end of stream is no longer recorded as a success before its error is inspected. Balance and quota failures pause the upstream, server and authentication failures use the existing circuit breaker, and rate limits pause the upstream without adding a success or failure sample. A final error frame is recognized even without a trailing blank line. The original error still reaches the client, and an answer that has already delivered content is never replayed.