# Capability Spec Delta: network-plugin-circuit-breaker (complete-network-plugins) **Package**: `@suaveplan/network-plugin-circuit-breaker` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. First canonical spec for this package. ## ADDED Requirements ### Requirement: MeshCircuitBreaker implements three-state breaker FSM The package SHALL export `MeshCircuitBreaker`, `MeshCircuitBreakerOptions`, a `CircuitBreakerState` union `"CLOSED" | "OPEN" | "HALF_OPEN"`, and a `StateChangeListener` type. State transitions SHALL follow CLOSED→OPEN on failure threshold, OPEN→HALF_OPEN after cooldown, HALF_OPEN→CLOSED on success or HALF_OPEN→OPEN on failure. Illegal transitions SHALL throw `CircuitBreakerError(INVALID_TRANSITION)`. #### Scenario: failure threshold opens the breaker - **WHEN** the configured number of consecutive failures is recorded - **THEN** the breaker transitions to `OPEN` and notifies registered `StateChangeListener`s #### Scenario: cooldown elapsed transitions to HALF_OPEN - **WHEN** the breaker is `OPEN` and `cooldownMs` has elapsed - **THEN** the next probe transitions to `HALF_OPEN` **Verified by:** `src/breaker/breaker.spec-conformance.test.ts::"opens after failure threshold"` AND `src/breaker/breaker.spec-conformance.test.ts::"transitions to HALF_OPEN after cooldown"` --- ### Requirement: Calls through an open path throw PATH_OPEN The breaker SHALL throw `CircuitBreakerError(PATH_OPEN)` when a call is attempted through a path that is in the `OPEN` state. #### Scenario: call through open path - **WHEN** `breaker.run(fn)` is called while state is `OPEN` - **THEN** the call throws `CircuitBreakerError` with `code === "CB.PATH_OPEN"` and `fn` is NOT invoked **Verified by:** `src/breaker/breaker.spec-conformance.test.ts::"throws PATH_OPEN when path is open"` --- ### Requirement: createCircuitBreakerMiddleware integrates with the relay path The package SHALL export `createCircuitBreakerMiddleware`, `CircuitBreakerMiddlewareOptions`, `RelayNext`, and a `PathIdBuilder` type. The middleware SHALL wrap each relay call in the breaker associated with the resolved path id. #### Scenario: middleware short-circuits an open path - **WHEN** the breaker for the resolved path id is `OPEN` and the middleware is invoked - **THEN** `next()` is NOT awaited and the middleware throws `CircuitBreakerError(PATH_OPEN)` **Verified by:** `src/middleware/middleware.spec-conformance.test.ts::"short-circuits open path"` --- ### Requirement: HalfOpenProbe scouts recovery before reopening the path The package SHALL export `HalfOpenProbe`, `HalfOpenProbeOptions`, and a `ProbeSender` type. The probe SHALL send a minimal canary message via the `ProbeSender` and SHALL transition the breaker to `CLOSED` only when the canary succeeds. #### Scenario: canary succeeds - **WHEN** the half-open probe runs and the `ProbeSender` resolves - **THEN** the breaker transitions to `CLOSED` **Verified by:** `src/probe/probe.spec-conformance.test.ts::"closes breaker on canary success"` --- ### Requirement: CircuitBreakerRegistry manages per-path breaker instances The package SHALL export `CircuitBreakerRegistry` and `CircuitBreakerRegistryOptions`. The registry SHALL lazily create a breaker per path id and SHALL surface stable lookup semantics. #### Scenario: registry returns the same instance for the same path id - **WHEN** `registry.for(pathId)` is called twice with the same id - **THEN** the same `MeshCircuitBreaker` instance is returned both times **Verified by:** `src/registry/registry.spec-conformance.test.ts::"returns same instance for same path id"` --- ### Requirement: Error-code registry is frozen and namespaced CB `CB_CODES` SHALL be frozen under the `CB` namespace and SHALL include exactly: `PATH_OPEN`, `INVALID_TRANSITION`. Every code SHALL be reachable from a production throw site. #### Scenario: registry membership - **WHEN** `CB_CODES` is inspected - **THEN** every key maps to a value of the form `"CB."` **Verified by:** `src/error-codes.spec-conformance.test.ts::"CB_CODES is frozen and prefixed CB"` --- ### Requirement: No bare-Error throws in production code `src/` SHALL contain zero bare `throw new (Error|TypeError|RangeError|SyntaxError)\(` sites outside test files. #### Scenario: source grep returns zero hits - **WHEN** the grep is run - **THEN** the output is empty **Verified by:** `tests/no-bare-throws.test.ts::"network-plugin-circuit-breaker src/ has no bare Error throws"` --- ### Requirement: README is auto-docs idempotent The package `README.md` SHALL contain exactly one balanced auto-docs block; the generator SHALL produce a byte-identical file on a second run. #### Scenario: marker count is exactly one - **WHEN** `grep -c "begin:auto-docs" packages/network/network-plugin-circuit-breaker/README.md` - **THEN** output is `1` **Verified by:** `scripts/generate-package-module-tree.test.ts::"README regen is idempotent for network-plugin-circuit-breaker"`