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

219 lines
6.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-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.