Initial Commit
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user