Files
2026-05-27 23:29:53 +01:00

165 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenSpec Change: enterprise-network-standby-transport
- **Change ID**: enterprise-network-standby-transport
- **Package**: `@suaveplan/network-standby-transport`
- **Category**: universal/network
- **Target path**: `universal/network/network-standby-transport`
- **Status**: SUPERSEDED-BY-GENESIS-WAVE
- **Date**: 2026-04-24
---
## Summary
Deliver `@suaveplan/network-standby-transport` on the genesis branch from scratch at enterprise quality. The package provides a standby/failover transport layer that maintains a pool of transport connections ranked by quality, promotes the best available transport when the primary degrades, and transparently re-routes in-flight messages without loss. Genesis adds structured errors via `@suaveplan/error`, Zod schemas for transport and pool configuration, OTel instrumentation on promotion events, `AbortSignal` on async operations, injectable `Clock` for promotion timers, event-bus integration for standby state changes, and property-based tests for promotion ordering.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | Base class for `StandbyTransportError`; stable error codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike` |
| `@suaveplan/events` (`workspace:^`) | Event bus for standby state changes |
| `@suaveplan/logger` (`workspace:^`) | Structured logging |
| `@suaveplan/retry` (`workspace:^`) | Retry on transport failures |
| `zod` (`catalog:forms`) | Zod schemas |
| `@opentelemetry/api` (`catalog:observability`) | Spans + counters on transport promotion |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`).
---
## Public API surface
### Core
```ts
function createStandbyTransportPool(options: StandbyTransportPoolOptions): StandbyTransportPool
interface StandbyTransportPool {
readonly primary: Transport | null
readonly standby: ReadonlyArray<Transport>
readonly state: PoolState
add(transport: Transport): void
remove(transportId: string): void
send(message: unknown, signal?: AbortSignal): Promise<void>
promote(transportId: string): void
on(event: PoolEvent, handler: (data: PoolEventData) => void): () => void
}
```
### Transport interface
```ts
interface Transport {
readonly id: string
readonly priority: number
readonly state: TransportState
readonly score: number // 0–100 quality score
send(message: unknown, signal?: AbortSignal): Promise<void>
connect(signal?: AbortSignal): Promise<void>
disconnect(): void
on(event: TransportEvent, handler: (data: unknown) => void): () => void
}
type TransportState = 'disconnected' | 'connecting' | 'connected' | 'degraded' | 'failed'
type TransportEvent = 'connect' | 'disconnect' | 'degrade' | 'recover' | 'score-change'
type PoolState = 'healthy' | 'degraded' | 'standby-active' | 'no-transport'
type PoolEvent = 'primary-promoted' | 'standby-promoted' | 'transport-failed' | 'state-change'
interface PoolEventData {
previousPrimary?: Transport
newPrimary?: Transport
reason?: string
}
```
### Options
```ts
interface StandbyTransportPoolOptions {
promotionThreshold?: number // min score to promote, default 30
degradationThreshold?: number // score below which primary is considered degraded, default 60
promotionDelayMs?: number // debounce window before promotion, default 1000
maxStandby?: number
logger?: LoggerLike
clock?: Clock
eventBus?: EventBusLike
}
```
### Error codes
```ts
type StandbyTransportErrorCode =
| 'STANDBY_NO_TRANSPORT'
| 'STANDBY_SEND_FAILED'
| 'STANDBY_TRANSPORT_NOT_FOUND'
| 'STANDBY_ALL_FAILED'
| 'STANDBY_ABORT'
function registerStandbyTransportErrorCodes(): void
```
---
## Genesis additions
1. **Structured `StandbyTransportError`** with stable `STANDBY.*` codes
2. **Zod schemas** for `StandbyTransportPoolOptions`
3. **Score-based promotion** — promotion triggered when primary score drops below `degradationThreshold`
4. **Promotion debounce** — uses `@suaveplan/timing` throttle to avoid promotion thrash
5. **In-flight message re-routing** — queued messages replayed on new primary after promotion
6. **Injectable `Clock`** for promotion delay testing
7. **OTel instrumentation** — span `standby.promote`, counter `standby.promotions_total`, histogram `standby.promotion_delay_ms`
8. **Property-based tests** — pool always has a primary when transports are available; promotion selects highest-scoring standby
---
## File layout
```text
universal/network/network-standby-transport/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
types/
common.ts
common.md
core/
pool/
pool.ts
pool.md
pool.types.ts
pool.test.ts
index.ts
promoter/
promoter.ts
promoter.md
promoter.test.ts
index.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/retry` must be published.