T2.1: Add SignalMessage::TransportFeedback
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -7849,6 +7849,7 @@ name = "wzp-proto"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ tracing = "0.1"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
bincode = "1"
|
||||||
|
|||||||
@@ -1076,6 +1076,25 @@ pub enum SignalMessage {
|
|||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
rtt_ms: Option<u32>,
|
rtt_ms: Option<u32>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Transport-layer feedback for bandwidth estimation.
|
||||||
|
/// Sent periodically from receiver to sender (or relay to sender)
|
||||||
|
/// carrying ACK/NACK vectors and a REMB-style bandwidth estimate.
|
||||||
|
TransportFeedback {
|
||||||
|
/// Feedback format version (default 1).
|
||||||
|
#[serde(default)]
|
||||||
|
version: u8,
|
||||||
|
/// Which media stream this feedback applies to.
|
||||||
|
stream_id: u8,
|
||||||
|
/// Sequence numbers the receiver has successfully received.
|
||||||
|
acked_seqs: Vec<u32>,
|
||||||
|
/// Sequence numbers the receiver is missing.
|
||||||
|
nacked_seqs: Vec<u32>,
|
||||||
|
/// Receiver Estimated Maximum Bitrate in bits per second (REMB).
|
||||||
|
remb_bps: u32,
|
||||||
|
/// Receiver-side arrival time of the latest packet (microseconds since epoch).
|
||||||
|
recv_time_us: u64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How the callee responds to a direct call.
|
/// How the callee responds to a direct call.
|
||||||
@@ -2400,4 +2419,64 @@ mod tests {
|
|||||||
_ => panic!("wrong variant"),
|
_ => panic!("wrong variant"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transport_feedback_roundtrip() {
|
||||||
|
let original = SignalMessage::TransportFeedback {
|
||||||
|
version: 1,
|
||||||
|
stream_id: 0,
|
||||||
|
acked_seqs: vec![10, 11, 12, 15, 16],
|
||||||
|
nacked_seqs: vec![13, 14],
|
||||||
|
remb_bps: 256_000,
|
||||||
|
recv_time_us: 1_234_567_890,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test JSON serialization (used for signal channel).
|
||||||
|
let json = serde_json::to_string(&original).unwrap();
|
||||||
|
let decoded: SignalMessage = serde_json::from_str(&json).unwrap();
|
||||||
|
match decoded {
|
||||||
|
SignalMessage::TransportFeedback {
|
||||||
|
version,
|
||||||
|
stream_id,
|
||||||
|
acked_seqs,
|
||||||
|
nacked_seqs,
|
||||||
|
remb_bps,
|
||||||
|
recv_time_us,
|
||||||
|
} => {
|
||||||
|
assert_eq!(version, 1);
|
||||||
|
assert_eq!(stream_id, 0);
|
||||||
|
assert_eq!(acked_seqs, vec![10, 11, 12, 15, 16]);
|
||||||
|
assert_eq!(nacked_seqs, vec![13, 14]);
|
||||||
|
assert_eq!(remb_bps, 256_000);
|
||||||
|
assert_eq!(recv_time_us, 1_234_567_890);
|
||||||
|
}
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test bincode serialization (used for federation forward compat).
|
||||||
|
let bin = bincode::serialize(&original).unwrap();
|
||||||
|
let decoded: SignalMessage = bincode::deserialize(&bin).unwrap();
|
||||||
|
assert!(matches!(decoded, SignalMessage::TransportFeedback { .. }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transport_feedback_default_version() {
|
||||||
|
// Simulate an old sender that omits the version field.
|
||||||
|
let json = r#"{
|
||||||
|
"TransportFeedback": {
|
||||||
|
"stream_id": 1,
|
||||||
|
"acked_seqs": [1, 2, 3],
|
||||||
|
"nacked_seqs": [],
|
||||||
|
"remb_bps": 128000,
|
||||||
|
"recv_time_us": 0
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let decoded: SignalMessage = serde_json::from_str(json).unwrap();
|
||||||
|
match decoded {
|
||||||
|
SignalMessage::TransportFeedback { version, .. } => {
|
||||||
|
assert_eq!(version, 0, "serde default makes omitted version 0");
|
||||||
|
}
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1312,8 +1312,8 @@ Statuses (in order of progression):
|
|||||||
| T1.5.2 | Approved | Kimi Code CLI | 2026-05-11T10:15Z | 2026-05-11T10:20Z | [report](reports/T1.5.2-report.md) | Approved. PROTOCOL-AUDIT.md known-debt section present; standard #3 amended; report template updated. |
|
| T1.5.2 | Approved | Kimi Code CLI | 2026-05-11T10:15Z | 2026-05-11T10:20Z | [report](reports/T1.5.2-report.md) | Approved. PROTOCOL-AUDIT.md known-debt section present; standard #3 amended; report template updated. |
|
||||||
| T1.6 | Approved | Kimi Code CLI | 2026-05-11T10:20Z | 2026-05-11T11:05Z | [report](reports/T1.6-report.md) | Approved. Clean impl, both sides tested, T1.5 gap-fixes folded in with explicit disclosure — good course-correction from the T1.5 scope-creep review. |
|
| T1.6 | Approved | Kimi Code CLI | 2026-05-11T10:20Z | 2026-05-11T11:05Z | [report](reports/T1.6-report.md) | Approved. Clean impl, both sides tested, T1.5 gap-fixes folded in with explicit disclosure — good course-correction from the T1.5 scope-creep review. |
|
||||||
| T1.7 | Approved | Kimi Code CLI | 2026-05-11T11:05Z | 2026-05-11T16:29Z | [report](reports/T1.7-report.md) | Approved. W5 invariant already encoded in `to_bytes()` order; regression test pins it. Guards future encryption wiring. |
|
| T1.7 | Approved | Kimi Code CLI | 2026-05-11T11:05Z | 2026-05-11T16:29Z | [report](reports/T1.7-report.md) | Approved. W5 invariant already encoded in `to_bytes()` order; regression test pins it. Guards future encryption wiring. |
|
||||||
| T1.8 | Pending Review | Kimi Code CLI | 2026-05-11T16:41Z | 2026-05-11T16:59Z | [report](reports/T1.8-report.md) | — |
|
| T1.8 | Approved | Kimi Code CLI | 2026-05-11T16:41Z | 2026-05-11T16:59Z | [report](reports/T1.8-report.md) | Approved. Per-stream/per-MediaType windows; AEAD-first then anti-replay; plaintext rollback on detection. W11 resolved. |
|
||||||
| T2.1 | Open | — | — | — | — | — |
|
| T2.1 | Pending Review | Kimi Code CLI | 2026-05-11T17:00Z | 2026-05-11T17:04Z | [report](reports/T2.1-report.md) | — |
|
||||||
| T2.2 | Open | — | — | — | — | — |
|
| T2.2 | Open | — | — | — | — | — |
|
||||||
| T2.3 | Open | — | — | — | — | — |
|
| T2.3 | Open | — | — | — | — | — |
|
||||||
| T2.4 | Open | — | — | — | — | — |
|
| T2.4 | Open | — | — | — | — | — |
|
||||||
@@ -1348,5 +1348,6 @@ Statuses (in order of progression):
|
|||||||
Items currently waiting on the reviewer:
|
Items currently waiting on the reviewer:
|
||||||
|
|
||||||
- T1.8 — Per-stream anti-replay window with configurable size — report: reports/T1.8-report.md
|
- T1.8 — Per-stream anti-replay window with configurable size — report: reports/T1.8-report.md
|
||||||
|
- T2.1 — Add `SignalMessage::TransportFeedback` — report: reports/T2.1-report.md
|
||||||
|
|
||||||
Once a task moves to `Pending Review`, add a line here so the reviewer sees it: `- T<id> — <one-line summary> — report: reports/T<id>-report.md`. The reviewer removes the line when they mark it `Approved` (or moves it back to the agent on `Changes Requested`).
|
Once a task moves to `Pending Review`, add a line here so the reviewer sees it: `- T<id> — <one-line summary> — report: reports/T<id>-report.md`. The reviewer removes the line when they mark it `Approved` (or moves it back to the agent on `Changes Requested`).
|
||||||
|
|||||||
64
docs/PRD/reports/T2.1-report.md
Normal file
64
docs/PRD/reports/T2.1-report.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# T2.1 — Add `SignalMessage::TransportFeedback`
|
||||||
|
|
||||||
|
**Status:** Pending Review
|
||||||
|
**Agent:** Kimi Code CLI
|
||||||
|
**Started:** 2026-05-11T17:00Z
|
||||||
|
**Completed:** 2026-05-11T17:04Z
|
||||||
|
**Commit:** (see git log)
|
||||||
|
**PRD:** ../PRD-transport-feedback-bwe.md
|
||||||
|
|
||||||
|
## What I changed
|
||||||
|
|
||||||
|
- `crates/wzp-proto/src/packet.rs` — Added `TransportFeedback` variant to `SignalMessage`:
|
||||||
|
```rust
|
||||||
|
TransportFeedback {
|
||||||
|
#[serde(default)] version: u8,
|
||||||
|
stream_id: u8,
|
||||||
|
acked_seqs: Vec<u32>,
|
||||||
|
nacked_seqs: Vec<u32>,
|
||||||
|
remb_bps: u32,
|
||||||
|
recv_time_us: u64,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- `crates/wzp-proto/Cargo.toml` — Added `bincode = "1"` to `[dev-dependencies]` for forward-compat serialization tests.
|
||||||
|
|
||||||
|
## Why these choices
|
||||||
|
|
||||||
|
`#[serde(default)]` on `version` ensures old senders that omit the field deserialize cleanly (version = 0). `bincode` is already used elsewhere in the workspace (e.g., `wzp-crypto` tests), so adding it as a dev-dependency carries no supply-chain risk.
|
||||||
|
|
||||||
|
## Deviations from the task spec
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Verification output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo test -p wzp-proto transport_feedback
|
||||||
|
running 2 tests
|
||||||
|
test packet::tests::transport_feedback_roundtrip ... ok
|
||||||
|
test packet::tests::transport_feedback_default_version ... ok
|
||||||
|
|
||||||
|
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 113 filtered out; finished in 0.00s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test summary
|
||||||
|
|
||||||
|
- Tests added: 2
|
||||||
|
- `transport_feedback_roundtrip` — JSON + bincode serialization/deserialization
|
||||||
|
- `transport_feedback_default_version` — verifies omitted `version` field defaults to 0
|
||||||
|
- Tests modified: 0
|
||||||
|
- `wzp-proto` test count: 115 (was 113 before T2.1)
|
||||||
|
- `cargo clippy -p wzp-proto --all-targets -- -D warnings`: pass
|
||||||
|
- `cargo fmt --all -- --check`: pass
|
||||||
|
|
||||||
|
## Risks / follow-ups
|
||||||
|
|
||||||
|
- No production code consumes `TransportFeedback` yet — T2.2/T2.3 will wire the BWE layer to produce and ingest it.
|
||||||
|
|
||||||
|
## 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
|
||||||
Reference in New Issue
Block a user