Files
suaveplan/openspec/changes/complete-network-plugins/specs/network-plugin-circuit-breaker/spec.md
2026-05-27 23:29:53 +01:00

5.0 KiB

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 StateChangeListeners

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.<KEY>"

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"