You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
chore(planning): archive the circuit-breaker-rate-mode bundle (#69)
Ship bookkeeping for PR #69: fill the 0.13.0 release-notes PR number, mark
the bundle shipped (pr: 69), move it from changes/active/ to changes/archive/,
flip its Index line to Archived, and trim the deferred "CircuitBreaker v2" item
to the still-open axes (count-based window, manual control + state).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: planning/README.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -70,10 +70,11 @@ carry **no** frontmatter — living prose, dated by git.
70
70
71
71
### Active
72
72
73
-
-**[circuit-breaker-rate-mode](changes/active/2026-06-16.02-circuit-breaker-rate-mode/design.md)** (2026-06-16) — Add an opt-in time-based failure-rate trip mode to the circuit breaker (classic stays default). Closes deferred item "CircuitBreaker v2 (a)". Targets 0.13.0.
73
+
_None._
74
74
75
75
### Archived (shipped)
76
76
77
+
-**[circuit-breaker-rate-mode](changes/archive/2026-06-16.02-circuit-breaker-rate-mode/design.md)** (#69, 2026-06-16) — Added an opt-in time-based failure-rate trip mode to the circuit breaker (classic stays default). Shipped 0.13.0; closed deferred item "CircuitBreaker v2 (a)".
77
78
-**[per-verb-with-response](changes/archive/2026-06-16.01-per-verb-with-response/design.md)** (#68, 2026-06-16) — Added `get_with_response` … `request_with_response` siblings (required `response_model`, returns `(Response, T)`) to both clients. Shipped 0.12.0; closed the deferred "Per-verb-with-response siblings" item.
78
79
-**[custom-decoder-guide](changes/archive/2026-06-15.01-custom-decoder-guide/change.md)** (#67, 2026-06-15) — Docs: a "write your own `ResponseDecoder`" guide for Seam B, mirroring `docs/middleware.md`. Closed deferred item G6.
79
80
-**[audit-doc-fixes](changes/archive/2026-06-14.06-audit-doc-fixes/change.md)** (#66, 2026-06-14) — Closed the [deep-audit](audits/2026-06-14-deep-audit.md) doc-accuracy findings: `Client.stream()` docs, terminal-call attribution, the four auto-raise sites, the pydantic upper bound, and root import paths.
Copy file name to clipboardExpand all lines: planning/deferred.md
+5-19Lines changed: 5 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,27 +8,13 @@ As of 0.7.0, all planned epics (3, 4, 5, 6) are closed — see the [change Index
8
8
9
9
### Resilience
10
10
11
-
-**CircuitBreaker v2 — rolling-window / failure-rate mode** (`src/httpware/middleware/resilience/circuit_breaker.py`) — the 0.10.0 breaker ships only the *classic consecutive-failure* model (open after N counted failures in a row; any success resets the streak). That can't catch *partial* degradation (e.g. a steady 50% error rate that alternates success/fail never trips). Deferred to v2 in the 0.10.0 spec; the config was shaped so a rate mode is purely additive (a new opt-in `failure_rate_threshold` + window + `minimum_calls`, with classic remaining the default). Demand-gated: build when someone needs rate-based tripping.
11
+
-**CircuitBreaker v2 — remaining axes** (`src/httpware/middleware/resilience/circuit_breaker.py`) — 0.13.0 shipped axis **(a)**, the opt-in **time-based** failure-rate trip mode (`failure_rate_threshold` + `window_seconds` + `minimum_calls`; classic stays default). Still open, each independent and demand-gated:
12
12
13
-
**Comparison with the reference implementations** (verified against current docs, 2026-06-13):
13
+
-**Count-based window variant** — a `window_type="count"` selector (ring buffer of the last N outcomes) alongside the shipped time-based window. Resilience4j offers both; we chose time-based first as the better HTTP-service fit. Additive: a new window-type knob, time-based remaining the default. Build if someone needs volume-relative (not time-relative) windows.
14
+
-**(b) Manual control + read-only `state`** — `force_open`/`force_closed` and a `state` introspection property (Resilience4j's registry, Polly's `StateProvider`/`ManualControl`). Parked as YAGNI in the 0.10.0 audit (decision 4: events-only control surface). Independent of the trip mode.
15
+
-**(c) Slow-call-rate dimension** — *don't*: Resilience4j-only, and redundant with `AsyncTimeout`. Recorded here only so a future reader doesn't re-propose it.
| Control surface | events-only (no `state`/`reset`/`isolate` — audit decision 4) | registry: state + metrics + manual transitions |`StateProvider` (read) + `ManualControl` (Isolate/Close) |
26
-
27
-
**Takeaways for scoping v2:** (1) Polly v8 *deleted* consecutive-count; Resilience4j doesn't default to it — so httpware v1's only mode is the one both treat as legacy/non-default. Adding rate mode while *keeping* classic is a small edge neither offers. (2) "Polly-v8-equivalent" = just the rate-over-window mode. "Resilience4j-equivalent" additionally implies count-vs-time window choice and (separately) manual control + state introspection. (3) httpware's HTTP-native classification (429-as-success out of the box) is already *ahead* of both generic-predicate libraries — don't regress it. (4) Skip the slow-call axis (Resilience4j-only; redundant with `AsyncTimeout`).
28
-
29
-
Three separable additions, rough priority: **(a) rate-over-window trip mode** (the core ask; additive opt-in), **(b) manual control + read-only `state`** (independent; both libraries have it, httpware parked it as YAGNI), **(c) slow-call-rate dimension** (don't — covered by `AsyncTimeout`).
30
-
31
-
**Concurrency note:** the window recorder (ring buffer for count-based; time-bucketed counters for time-based) is more state than v1's single counter, but recording an outcome stays a synchronous mutation, so the async lock-free atomicity invariant and the sync `threading.Lock` both still hold. Time-based eviction must read `_now()` inside the same synchronous critical section.
17
+
**Don't regress:** httpware's HTTP-native failure classification (429/4xx = success out of the box) is already ahead of the generic-predicate breakers — preserve it in any v2 work.
0 commit comments