# 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 stop(): void measure(probe: QualityProbe): Promise 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 } 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 function createRttProbe(url: string, options?: RttProbeOptions): QualityProbe function createBandwidthProbe(url: string, options?: BandwidthProbeOptions): QualityProbe function selectBestPeer( 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.