# T2.2 — `BandwidthEstimator` in `wzp-proto::bandwidth` **Status:** Pending Review **Agent:** Kimi Code CLI **Started:** 2026-05-11T17:05Z **Completed:** 2026-05-11T17:12Z **Commit:** (see git log) **PRD:** ../PRD-transport-feedback-bwe.md ## What I changed - `crates/wzp-transport/src/quic.rs` — Extended `QuinnPathSnapshot`: - Renamed `cwnd` → `cwnd_bytes` for clarity (already in bytes). - Added `bytes_in_flight: u64` (set to 0 because quinn 0.11.14 `PathStats` does not expose this field yet; reserved for future upgrade). - `crates/wzp-proto/src/bandwidth.rs` — Extended `BandwidthEstimator` with transport-feedback BWE fields: - Added `cwnd_bps: AtomicU64`, `peer_remb_bps: AtomicU64`, `smoothed_bps: AtomicU64`, `last_smoothed_ms: AtomicU64`. - Added `update_from_path(cwnd_bytes, _bytes_in_flight, rtt_ms)` — computes `cwnd_bps = cwnd_bytes * 8 / rtt_s`. - Added `update_from_peer(fb_remb_bps: u32)` — stores peer REMB. - Added `target_send_bps(&self) -> u64` — returns `0.9 * min(cwnd_bps, peer_remb_bps)`. - Added `smoothed_bps(&self) -> u64` — returns the EWMA-smoothed estimate. - EWMA smoothing uses a 2-second half-life: `alpha = 1 - 0.5^(dt_ms / 2000)`. ## Why these choices `QuinnPathSnapshot` lives in `wzp-transport`; `BandwidthEstimator` lives in `wzp-proto`. Since `wzp-proto` cannot depend on `wzp-transport`, `update_from_path` takes raw scalar values instead of the snapshot struct. Callers in `wzp-client` (T2.3) will destructure `QuinnPathSnapshot` and pass the fields through. `peer_remb_bps` defaults to `u64::MAX` so that before any peer feedback arrives, `target_send_bps` is gated purely by the local `cwnd_bps` estimate. ## Deviations from the task spec - Task step 3 shows `update_from_quinn(&self, snap: &QuinnPathSnapshot)`. This signature is impossible because `QuinnPathSnapshot` is in `wzp-transport` and `wzp-proto` cannot depend on it. Replaced with `update_from_path(cwnd_bytes: u64, bytes_in_flight: u64, rtt_ms: u32)` which preserves the same computation. - `bytes_in_flight` is hard-coded to `0` in `QuinnPathSnapshot` because quinn 0.11.14 does not expose it on `PathStats`. A comment documents this. ## Verification output ```bash $ cargo test -p wzp-proto bandwidth running 15 tests ...(all 15 pass)... test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 103 filtered out; finished in 0.11s ``` ```bash $ cargo test -p wzp-transport running 11 tests ...(all 11 pass)... test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` ## Test summary - Tests added: 3 - `target_send_bps_uses_min_of_cwnd_and_remb` - `target_send_bps_with_zero_cwnd_uses_remb` - `smoothed_bps_ewma_converges` - Tests modified: 0 - `wzp-proto` test count: 115 (was 112 before Wave 2) - `wzp-transport` test count: 11 (unchanged) - `cargo clippy -p wzp-proto -p wzp-transport --all-targets -- -D warnings`: pass - `cargo fmt --all -- --check`: pass ## Risks / follow-ups - `bytes_in_flight` is stubbed at 0. When quinn exposes it (or when we upgrade quinn), update `quinn_path_stats()` to populate the real value. - T2.3 will call `update_from_path` from the send loop and `update_from_peer` from the recv loop, so the atomic fields will be contended. `Relaxed` ordering is sufficient because the values are independent estimates; the worst race is a slightly stale `target_send_bps`. ## Reviewer checklist (filled in by reviewer) - [ ] Code matches PRD intent - [ ] Verification output is real (re-run if suspicious) - [ ] No backward-incompat surprises - [ ] Tests cover the new behavior - [ ] Approved