219 lines
6.2 KiB
Markdown
219 lines
6.2 KiB
Markdown
# OpenSpec Change: enterprise-network-quality
|
||
|
||
- **Change ID**: enterprise-network-quality
|
||
- **Package**: `@suaveplan/network-quality`
|
||
- **Category**: universal/network
|
||
- **Target path**: `universal/network/network-quality`
|
||
- **Status**: SUPERSEDED-BY-GENESIS-WAVE
|
||
- **Date**: 2026-04-24
|
||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/network-quality.md`
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
Deliver `@suaveplan/network-quality` on the genesis branch from scratch at enterprise quality. The package provides network quality monitoring: continuous measurement of RTT, jitter, packet loss, and throughput; quality scoring with configurable thresholds; trend analysis (sliding window, exponential smoothing); degradation alerts; and a quality-aware selector for choosing the best peer/transport. Genesis adds structured errors via `@suaveplan/error`, Zod schemas for measurement and threshold types, OTel instrumentation on all quality metrics, `AbortSignal` on collection tasks, injectable `Clock` for measurement timestamps, event-bus emission for quality degradation and recovery, and property-based tests for scoring invariants.
|
||
|
||
---
|
||
|
||
## 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 `NetworkQualityError`; stable error codes |
|
||
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike` |
|
||
| `@suaveplan/events` (`workspace:^`) | Event bus for quality change events |
|
||
| `@suaveplan/logger` (`workspace:^`) | Structured logging |
|
||
| `@suaveplan/timing` (`workspace:^`) | Stopwatch and interval timer for measurement |
|
||
| `zod` (`catalog:forms`) | Zod schemas |
|
||
| `@opentelemetry/api` (`catalog:observability`) | Deep OTel metric emission for network quality |
|
||
| `tslib` (`catalog:build`) | Runtime helpers |
|
||
|
||
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`).
|
||
|
||
---
|
||
|
||
## Public API surface
|
||
|
||
### Core
|
||
|
||
```ts
|
||
function createQualityMonitor(options?: QualityMonitorOptions): QualityMonitor
|
||
|
||
interface QualityMonitor {
|
||
readonly score: number
|
||
readonly grade: QualityGrade
|
||
start(signal?: AbortSignal): Promise<void>
|
||
stop(): void
|
||
measure(probe: QualityProbe): Promise<QualityMeasurement>
|
||
report(): QualityReport
|
||
on(event: QualityEvent, handler: (report: QualityReport) => void): () => void
|
||
}
|
||
```
|
||
|
||
### Measurement types
|
||
|
||
```ts
|
||
interface QualityMeasurement {
|
||
readonly rttMs: number
|
||
readonly jitterMs: number
|
||
readonly packetLoss: number // 0–1
|
||
readonly throughputBps?: number
|
||
readonly timestamp: number
|
||
}
|
||
|
||
interface QualityReport {
|
||
readonly score: number // 0–100
|
||
readonly grade: QualityGrade
|
||
readonly current: QualityMeasurement
|
||
readonly trend: QualityTrend
|
||
readonly window: QualityWindowStats
|
||
readonly thresholdViolations: ReadonlyArray<ThresholdViolation>
|
||
}
|
||
|
||
interface QualityWindowStats {
|
||
readonly avgRttMs: number
|
||
readonly avgJitterMs: number
|
||
readonly avgPacketLoss: number
|
||
readonly p95RttMs: number
|
||
readonly sampleCount: number
|
||
readonly windowMs: number
|
||
}
|
||
|
||
type QualityTrend = 'improving' | 'stable' | 'degrading'
|
||
type QualityGrade = 'excellent' | 'good' | 'fair' | 'poor' | 'critical'
|
||
|
||
interface ThresholdViolation {
|
||
readonly metric: string
|
||
readonly threshold: number
|
||
readonly actual: number
|
||
readonly severity: 'warning' | 'critical'
|
||
}
|
||
```
|
||
|
||
### Probe and selector
|
||
|
||
```ts
|
||
type QualityProbe = (signal?: AbortSignal) => Promise<QualityMeasurement>
|
||
|
||
function createRttProbe(url: string, options?: RttProbeOptions): QualityProbe
|
||
function createBandwidthProbe(url: string, options?: BandwidthProbeOptions): QualityProbe
|
||
|
||
function selectBestPeer<T extends { id: string; score?: number }>(
|
||
peers: T[],
|
||
monitor: QualityMonitor
|
||
): T | null
|
||
```
|
||
|
||
### Options
|
||
|
||
```ts
|
||
interface QualityMonitorOptions {
|
||
measureIntervalMs?: number
|
||
windowSize?: number // number of samples in sliding window
|
||
smoothingFactor?: number // 0–1 for exponential smoothing
|
||
thresholds?: QualityThresholds
|
||
probe?: QualityProbe
|
||
logger?: LoggerLike
|
||
clock?: Clock
|
||
eventBus?: EventBusLike
|
||
}
|
||
|
||
interface QualityThresholds {
|
||
maxRttMs?: number
|
||
maxJitterMs?: number
|
||
maxPacketLoss?: number
|
||
minThroughputBps?: number
|
||
degradationScore?: number
|
||
criticalScore?: number
|
||
}
|
||
|
||
type QualityEvent = 'measurement' | 'degradation' | 'recovery' | 'critical'
|
||
```
|
||
|
||
### Error codes
|
||
|
||
```ts
|
||
type NetworkQualityErrorCode =
|
||
| 'QUALITY_PROBE_FAILED'
|
||
| 'QUALITY_MONITOR_RUNNING'
|
||
| 'QUALITY_NO_MEASUREMENTS'
|
||
| 'QUALITY_ABORT'
|
||
|
||
function registerNetworkQualityErrorCodes(): void
|
||
```
|
||
|
||
---
|
||
|
||
## Genesis additions
|
||
|
||
1. **Structured `NetworkQualityError`** with stable `QUALITY.*` codes
|
||
2. **Zod schemas** for `QualityMonitorOptions`, `QualityThresholds`
|
||
3. **Exponential smoothing + sliding window** statistics
|
||
4. **Trend detection** — compares recent half of window against older half
|
||
5. **Quality scoring** — weighted composite (RTT 40%, jitter 20%, packet loss 30%, throughput 10%)
|
||
6. **`createRttProbe` / `createBandwidthProbe`** factory utilities
|
||
7. **OTel metric emission** — gauges for `net.quality.rtt`, `net.quality.jitter`, `net.quality.packet_loss`, `net.quality.score`
|
||
8. **Property-based tests** — score monotonically decreases as RTT increases; grade ordering matches score ordering
|
||
|
||
---
|
||
|
||
## File layout
|
||
|
||
```text
|
||
universal/network/network-quality/
|
||
package.json
|
||
tsconfig.json
|
||
vitest.config.ts
|
||
src/
|
||
index.ts
|
||
error-codes.ts
|
||
constants/
|
||
index.ts
|
||
types/
|
||
common.ts
|
||
common.md
|
||
core/
|
||
monitor/
|
||
monitor.ts
|
||
monitor.md
|
||
monitor.types.ts
|
||
monitor.test.ts
|
||
index.ts
|
||
scoring/
|
||
scorer.ts
|
||
scorer.md
|
||
scorer.test.ts
|
||
index.ts
|
||
stats/
|
||
window-stats.ts
|
||
window-stats.md
|
||
window-stats.test.ts
|
||
index.ts
|
||
probes/
|
||
rtt-probe.ts
|
||
rtt-probe.md
|
||
rtt-probe.test.ts
|
||
bandwidth-probe.ts
|
||
bandwidth-probe.md
|
||
bandwidth-probe.test.ts
|
||
index.ts
|
||
__benchmarks__/
|
||
quality.bench.ts
|
||
tests/
|
||
integration.test.ts
|
||
property.test.ts
|
||
```
|
||
|
||
---
|
||
|
||
## Impact
|
||
|
||
**Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/timing` must be published.
|