7.5 KiB
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
bandwidthBytesPerSecondis 1000 and 5000 bytes are submitted in 100ms - THEN the simulator emits exactly 1000 bytes' worth of
deliveredevents in the first second; the remaining 4000 bytes queue and deliver across the next 4 seconds
Scenario: latency + jitter inside configured bounds
- WHEN
latencyMsis 50 andjitterMsis 10 - THEN every delivered packet's measured delivery delay is within
[40, 60]ms
Scenario: packet-loss probability hits empirical bucket
- WHEN
lossProbabilityis 0.25 and 1000 packets are sent through a deterministicRng - THEN the recorded
stats.packetsLostfalls 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, andreorderevents fire with the affectedPacketInfopayload ANDstats.packetsDuplicated/stats.packetsCorrupted/stats.packetsReorderedincrement accordingly
Scenario: burstiness scheduler activates burst then ends it
- WHEN
enableBurstiness: truewithburstIntervalMs: 100,burstDurationMs: 50,burstLossMultiplier: 4 - THEN during the burst window, effective
lossProbabilityis 4× the configured value, and afterburstDurationMsit returns to baseline
Scenario: disconnect / reconnect lifecycle
- WHEN
disconnect(200)is called - THEN
connectedbecomes false, all pending sends emitdropped, and after 200msconnectedbecomes true again with areconnectedevent
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
NetworkSimulatoris constructed with{ condition: "MOBILE_3G" } - THEN the resulting options match
NETWORK_CONDITIONS.MOBILE_3GforlatencyMs,jitterMs,bandwidthBytesPerSecond,lossProbability
Scenario: explicit fields override preset
- WHEN
{ condition: "MOBILE_3G", lossProbability: 0 }is passed - THEN the resulting options take
lossProbability: 0and 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
Rngis 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
packetsSentis one belowMAX_SAFE_INTEGERand one more packet is sent - THEN a
BitpackerErrorwith codeBITPACKER.NETWORK_STAT_OVERFLOWis emitted as astat-overflowevent ANDstats.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.