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

7.5 KiB
Raw Permalink Blame History

Capability Spec Delta: bitpacker-network (bitpacker-stub-completion)

Package: @suaveplan/bitpacker-network Status: PROPOSED

Every requirement below carries a **Verified by:** line at authoring time.

ADDED Requirements

Requirement: NetworkSimulator class with full upstream feature surface

The package SHALL expose a NetworkSimulator extends EventEmitter class implementing bandwidth-limited delivery, configurable latency baseline + jitter, packet loss probability, packet duplication probability, packet corruption probability, in-flight reordering, burstiness scheduling, and random-disconnect scheduling. Public API matches upstream network-simulation.ts (L15-L593): send(packet), start(), stop(), disconnect(durationMs), reconnect(), getStats(), clearStats(), setOptions(partial).

Scenario: bandwidth limit honoured across a 1-second window

  • WHEN bandwidthBytesPerSecond is 1000 and 5000 bytes are submitted in 100ms
  • THEN the simulator emits exactly 1000 bytes' worth of delivered events in the first second; the remaining 4000 bytes queue and deliver across the next 4 seconds

Scenario: latency + jitter inside configured bounds

  • WHEN latencyMs is 50 and jitterMs is 10
  • THEN every delivered packet's measured delivery delay is within [40, 60] ms

Scenario: packet-loss probability hits empirical bucket

  • WHEN lossProbability is 0.25 and 1000 packets are sent through a deterministic Rng
  • THEN the recorded stats.packetsLost falls within [230, 270] (25% ± 2.5 σ for n=1000)

Scenario: duplication, corruption, reordering surface as events

  • WHEN the corresponding probabilities are non-zero
  • THEN duplicate, corrupt, and reorder events fire with the affected PacketInfo payload AND stats.packetsDuplicated / stats.packetsCorrupted / stats.packetsReordered increment accordingly

Scenario: burstiness scheduler activates burst then ends it

  • WHEN enableBurstiness: true with burstIntervalMs: 100, burstDurationMs: 50, burstLossMultiplier: 4
  • THEN during the burst window, effective lossProbability is 4× the configured value, and after burstDurationMs it returns to baseline

Scenario: disconnect / reconnect lifecycle

  • WHEN disconnect(200) is called
  • THEN connected becomes false, all pending sends emit dropped, and after 200ms connected becomes true again with a reconnected event

Verified by: packages/bitpacker/bitpacker-network/src/simulator/simulator.test.ts::"honours bandwidth limit across a 1-second window" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.test.ts::"applies latency + jitter inside configured bounds" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.test.ts::"loss probability hits empirical bucket over 1000 packets" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.test.ts::"emits duplicate / corrupt / reorder events with PacketInfo" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.test.ts::"burstiness scheduler activates and deactivates the loss multiplier" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.test.ts::"disconnect drops in-flight, reconnect restores delivery"


Requirement: NETWORK_CONDITIONS preset bindings

The package SHALL export a NETWORK_CONDITIONS map binding each of IDEAL, HOME, MOBILE_4G, MOBILE_3G, SATELLITE, LOSSY_WIFI, CUSTOM to a NetworkSimulationOptions object. The values SHALL match upstream constants byte-for-byte (cited in proposal.md source mapping).

Scenario: preset selection populates options

  • WHEN a NetworkSimulator is constructed with { condition: "MOBILE_3G" }
  • THEN the resulting options match NETWORK_CONDITIONS.MOBILE_3G for latencyMs, jitterMs, bandwidthBytesPerSecond, lossProbability

Scenario: explicit fields override preset

  • WHEN { condition: "MOBILE_3G", lossProbability: 0 } is passed
  • THEN the resulting options take lossProbability: 0 and inherit the rest from the preset

Verified by: packages/bitpacker/bitpacker-network/src/simulator/simulator.spec-conformance.test.ts::"NETWORK_CONDITIONS presets bind to options as documented" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.spec-conformance.test.ts::"explicit options override preset values"


Requirement: Deterministic Rng and Clock injection

The package SHALL accept an optional Rng and Clock from @suaveplan/types. Default Rng is the crypto.getRandomValues-backed Rng.crypto(). The package SHALL NOT call Math.random() or Date.now() at module scope or in production code paths.

Scenario: deterministic Rng yields reproducible loss sequence

  • WHEN a seeded Rng is injected and the same 100-packet input is replayed
  • THEN the set of dropped packet indexes is byte-identical between runs

Scenario: deterministic Clock advances burst windows

  • WHEN a fake clock is injected and ticked manually
  • THEN burst start / end fire at exactly the configured offsets

Scenario: no module-scope Math.random() or Date.now()

  • WHEN grep -E "Math\.random\(\)|Date\.now\(\)" packages/bitpacker/bitpacker-network/src/ is run
  • THEN there are zero matches in implementation files

Verified by: packages/bitpacker/bitpacker-network/src/simulator/simulator.spec-conformance.test.ts::"seeded Rng yields reproducible loss sequence" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.spec-conformance.test.ts::"fake Clock advances burst windows deterministically" AND packages/bitpacker/bitpacker-network/src/simulator/simulator.spec-conformance.test.ts::"no module-scope Math.random or Date.now in implementation files"


Requirement: Stat counters bounded; overflow signalled

The simulator SHALL track packetsSent, packetsReceived, packetsLost, packetsDuplicated, packetsCorrupted, packetsReordered, bytesSent, bytesReceived. When any counter would exceed Number.MAX_SAFE_INTEGER, the simulator SHALL emit BitpackerError with code BITPACKER.NETWORK_STAT_OVERFLOW and context.counter naming the affected field; the counter SHALL freeze at MAX_SAFE_INTEGER.

Scenario: stat overflow throws coded error

  • WHEN packetsSent is one below MAX_SAFE_INTEGER and one more packet is sent
  • THEN a BitpackerError with code BITPACKER.NETWORK_STAT_OVERFLOW is emitted as a stat-overflow event AND stats.packetsSent === Number.MAX_SAFE_INTEGER

Verified by: packages/bitpacker/bitpacker-network/src/simulator/simulator.spec-conformance.test.ts::"emits NETWORK_STAT_OVERFLOW when a counter would exceed MAX_SAFE_INTEGER"

REMOVED Requirements

Requirement: 48-line Math.random()-based stub at bitpacker-network/src/index.ts

Reason: Stub used module-scope Math.random() (CLAUDE.md determinism rule violation) and exposed only three free functions; the real 622-line implementation lived (misplaced) at bitpacker-game/src/network/network-simulation.ts.

Migration: The three existing free functions (simulateLatency, shouldDropPacket, applyNetworkConditions) are preserved as thin wrappers around the new simulator. Existing call sites compile unchanged. The misplaced copy at bitpacker-game/src/network/network-simulation.ts is deleted; a one-release shim re-exports the simulator from @suaveplan/bitpacker-network so any consumer importing it via the game package's deep path keeps compiling.