T1.4: Add v2 MiniHeader with seq_delta
This commit is contained in:
@@ -31,8 +31,9 @@ pub use error::*;
|
|||||||
pub use media_type::MediaType;
|
pub use media_type::MediaType;
|
||||||
pub use packet::{
|
pub use packet::{
|
||||||
CallAcceptMode, FRAME_TYPE_FULL, FRAME_TYPE_MINI, HangupReason, MediaHeader, MediaHeaderV1,
|
CallAcceptMode, FRAME_TYPE_FULL, FRAME_TYPE_MINI, HangupReason, MediaHeader, MediaHeaderV1,
|
||||||
MediaHeaderV2, MediaPacket, MiniFrameContext, MiniHeader, PresenceUser, QualityReport,
|
MediaHeaderV2, MediaPacket, MiniFrameContext, MiniFrameContextV1, MiniFrameContextV2,
|
||||||
RoomParticipant, SignalMessage, TrunkEntry, TrunkFrame,
|
MiniHeader, MiniHeaderV1, MiniHeaderV2, PresenceUser, QualityReport, RoomParticipant,
|
||||||
|
SignalMessage, TrunkEntry, TrunkFrame,
|
||||||
};
|
};
|
||||||
pub use quality::{AdaptiveQualityController, NetworkContext, Tier};
|
pub use quality::{AdaptiveQualityController, NetworkContext, Tier};
|
||||||
pub use session::{Session, SessionEvent, SessionState};
|
pub use session::{Session, SessionEvent, SessionState};
|
||||||
|
|||||||
@@ -577,18 +577,18 @@ pub const FRAME_TYPE_FULL: u8 = 0x00;
|
|||||||
/// Frame type tag: MiniHeader follows (requires prior baseline).
|
/// Frame type tag: MiniHeader follows (requires prior baseline).
|
||||||
pub const FRAME_TYPE_MINI: u8 = 0x01;
|
pub const FRAME_TYPE_MINI: u8 = 0x01;
|
||||||
|
|
||||||
/// Compact 4-byte header used after a full MediaHeader baseline has been
|
/// Compact 4-byte v1 header used after a full MediaHeader baseline has been
|
||||||
/// established. Only the timestamp delta and payload length are transmitted;
|
/// established. Only the timestamp delta and payload length are transmitted;
|
||||||
/// all other fields are inherited from the last full header.
|
/// all other fields are inherited from the last full header.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct MiniHeader {
|
pub struct MiniHeaderV1 {
|
||||||
/// Milliseconds elapsed since the last header's timestamp.
|
/// Milliseconds elapsed since the last header's timestamp.
|
||||||
pub timestamp_delta_ms: u16,
|
pub timestamp_delta_ms: u16,
|
||||||
/// Length of the payload that follows this header.
|
/// Length of the payload that follows this header.
|
||||||
pub payload_len: u16,
|
pub payload_len: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MiniHeader {
|
impl MiniHeaderV1 {
|
||||||
/// Header size in bytes on the wire.
|
/// Header size in bytes on the wire.
|
||||||
pub const WIRE_SIZE: usize = 4;
|
pub const WIRE_SIZE: usize = 4;
|
||||||
|
|
||||||
@@ -610,14 +610,47 @@ impl MiniHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stateful context that expands [`MiniHeader`]s back into full
|
/// Temporary alias so existing code continues to compile.
|
||||||
|
/// Removed in T1.5 once all emit/parse sites migrate to v2.
|
||||||
|
pub type MiniHeader = MiniHeaderV1;
|
||||||
|
|
||||||
|
/// Compact 5-byte v2 mini header with explicit `seq_delta`.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct MiniHeaderV2 {
|
||||||
|
pub seq_delta: u8,
|
||||||
|
pub timestamp_delta_ms: u16,
|
||||||
|
pub payload_len: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MiniHeaderV2 {
|
||||||
|
pub const WIRE_SIZE: usize = 5;
|
||||||
|
|
||||||
|
pub fn write_to(&self, buf: &mut impl BufMut) {
|
||||||
|
buf.put_u8(self.seq_delta);
|
||||||
|
buf.put_u16(self.timestamp_delta_ms);
|
||||||
|
buf.put_u16(self.payload_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from(buf: &mut impl Buf) -> Option<Self> {
|
||||||
|
if buf.remaining() < Self::WIRE_SIZE {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self {
|
||||||
|
seq_delta: buf.get_u8(),
|
||||||
|
timestamp_delta_ms: buf.get_u16(),
|
||||||
|
payload_len: buf.get_u16(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stateful v1 context that expands [`MiniHeaderV1`]s back into full
|
||||||
/// [`MediaHeader`]s by tracking the last baseline header.
|
/// [`MediaHeader`]s by tracking the last baseline header.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct MiniFrameContext {
|
pub struct MiniFrameContextV1 {
|
||||||
last_header: Option<MediaHeader>,
|
last_header: Option<MediaHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MiniFrameContext {
|
impl MiniFrameContextV1 {
|
||||||
/// Record a full header as the new baseline for subsequent mini-frames.
|
/// Record a full header as the new baseline for subsequent mini-frames.
|
||||||
pub fn update(&mut self, header: &MediaHeader) {
|
pub fn update(&mut self, header: &MediaHeader) {
|
||||||
self.last_header = Some(*header);
|
self.last_header = Some(*header);
|
||||||
@@ -635,6 +668,32 @@ impl MiniFrameContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Temporary alias so existing code continues to compile.
|
||||||
|
/// Removed in T1.5 once all emit/parse sites migrate to v2.
|
||||||
|
pub type MiniFrameContext = MiniFrameContextV1;
|
||||||
|
|
||||||
|
/// Stateful v2 context that expands [`MiniHeaderV2`]s back into full
|
||||||
|
/// [`MediaHeaderV2`]s by tracking the last baseline header.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct MiniFrameContextV2 {
|
||||||
|
last: Option<MediaHeaderV2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MiniFrameContextV2 {
|
||||||
|
pub fn update(&mut self, h: &MediaHeaderV2) {
|
||||||
|
self.last = Some(*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand(&mut self, m: &MiniHeaderV2) -> Option<MediaHeaderV2> {
|
||||||
|
let base = self.last.as_ref()?;
|
||||||
|
let mut e = *base;
|
||||||
|
e.seq = base.seq.wrapping_add(m.seq_delta as u32);
|
||||||
|
e.timestamp = base.timestamp.wrapping_add(m.timestamp_delta_ms as u32);
|
||||||
|
self.last = Some(e);
|
||||||
|
Some(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Signaling messages sent over the reliable QUIC stream.
|
/// Signaling messages sent over the reliable QUIC stream.
|
||||||
///
|
///
|
||||||
/// Compatible with Warzone messenger's identity model:
|
/// Compatible with Warzone messenger's identity model:
|
||||||
@@ -1906,6 +1965,72 @@ mod tests {
|
|||||||
assert!(ctx.expand(&mini).is_none());
|
assert!(ctx.expand(&mini).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mini_header_v2_roundtrip() {
|
||||||
|
let mini = MiniHeaderV2 {
|
||||||
|
seq_delta: 3,
|
||||||
|
timestamp_delta_ms: 20,
|
||||||
|
payload_len: 160,
|
||||||
|
};
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
mini.write_to(&mut buf);
|
||||||
|
assert_eq!(buf.len(), 5);
|
||||||
|
|
||||||
|
let mut cursor = &buf[..];
|
||||||
|
let decoded = MiniHeaderV2::read_from(&mut cursor).unwrap();
|
||||||
|
assert_eq!(mini, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mini_frame_context_v2_expand() {
|
||||||
|
let baseline = MediaHeaderV2 {
|
||||||
|
version: 2,
|
||||||
|
flags: 0,
|
||||||
|
media_type: MediaType::Audio,
|
||||||
|
codec_id: CodecId::Opus24k,
|
||||||
|
stream_id: 0,
|
||||||
|
fec_ratio: 50,
|
||||||
|
seq: 100,
|
||||||
|
timestamp: 1000,
|
||||||
|
fec_block: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ctx = MiniFrameContextV2::default();
|
||||||
|
ctx.update(&baseline);
|
||||||
|
|
||||||
|
let mini = MiniHeaderV2 {
|
||||||
|
seq_delta: 3,
|
||||||
|
timestamp_delta_ms: 20,
|
||||||
|
payload_len: 80,
|
||||||
|
};
|
||||||
|
let h1 = ctx.expand(&mini).unwrap();
|
||||||
|
assert_eq!(h1.seq, 103);
|
||||||
|
assert_eq!(h1.timestamp, 1020);
|
||||||
|
assert_eq!(h1.codec_id, CodecId::Opus24k);
|
||||||
|
assert_eq!(h1.fec_block, 5);
|
||||||
|
|
||||||
|
// Second expansion — builds on expanded h1
|
||||||
|
let mini2 = MiniHeaderV2 {
|
||||||
|
seq_delta: 1,
|
||||||
|
timestamp_delta_ms: 20,
|
||||||
|
payload_len: 80,
|
||||||
|
};
|
||||||
|
let h2 = ctx.expand(&mini2).unwrap();
|
||||||
|
assert_eq!(h2.seq, 104);
|
||||||
|
assert_eq!(h2.timestamp, 1040);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mini_frame_context_v2_no_baseline() {
|
||||||
|
let mut ctx = MiniFrameContextV2::default();
|
||||||
|
let mini = MiniHeaderV2 {
|
||||||
|
seq_delta: 1,
|
||||||
|
timestamp_delta_ms: 20,
|
||||||
|
payload_len: 80,
|
||||||
|
};
|
||||||
|
assert!(ctx.expand(&mini).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_vs_mini_size_comparison() {
|
fn full_vs_mini_size_comparison() {
|
||||||
// Full frame on wire: 1 byte type tag + 12 byte MediaHeader = 13
|
// Full frame on wire: 1 byte type tag + 12 byte MediaHeader = 13
|
||||||
|
|||||||
@@ -437,6 +437,47 @@ cargo test -p wzp-proto
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## T1.2.1 — Add rustdoc on `MediaType` variants and methods
|
||||||
|
|
||||||
|
- **Parent:** T1.2 (Approved)
|
||||||
|
- **PRD:** `PRD-wire-format-v2.md`
|
||||||
|
- **Effort:** 10 min
|
||||||
|
- **Files:**
|
||||||
|
- `crates/wzp-proto/src/media_type.rs`
|
||||||
|
|
||||||
|
### Context
|
||||||
|
T1.2 created `MediaType` with a one-line top-level doc comment but no `///` rustdoc on the variants (`Audio`, `Video`, `Data`, `Control`) or methods (`to_wire`, `from_wire`). Coding standard #9 — public items need rustdoc. Same shape of follow-up as T1.1.1.
|
||||||
|
|
||||||
|
### Steps
|
||||||
|
|
||||||
|
1. Open `crates/wzp-proto/src/media_type.rs`.
|
||||||
|
2. Add a `///` doc comment to each variant. Examples (do not just copy — pick what's accurate):
|
||||||
|
```rust
|
||||||
|
pub enum MediaType {
|
||||||
|
/// Encoded speech / music (Opus, Codec2, ComfortNoise).
|
||||||
|
Audio = 0,
|
||||||
|
/// Encoded video access unit (H.264, H.265, AV1; PRD-video-multicodec).
|
||||||
|
Video = 1,
|
||||||
|
/// Opaque payload not interpreted by the relay (reserved).
|
||||||
|
Data = 2,
|
||||||
|
/// In-band control message carried on the media plane (reserved).
|
||||||
|
Control = 3,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Add a `///` doc on `to_wire` and `from_wire`. One line each is fine — explain the wire byte mapping and the `None` case.
|
||||||
|
|
||||||
|
### Verify
|
||||||
|
```bash
|
||||||
|
cargo doc -p wzp-proto --no-deps 2>&1 | grep -i "missing" || echo "no missing-doc warnings"
|
||||||
|
cargo clippy -p wzp-proto --all-targets -- -D warnings -W missing_docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Done when
|
||||||
|
- All variants and methods on `MediaType` carry `///` doc comments.
|
||||||
|
- `cargo doc -p wzp-proto --no-deps` emits no "missing documentation" warnings for `MediaType`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## T1.3 — Widen `CodecId` wire representation to u8
|
## T1.3 — Widen `CodecId` wire representation to u8
|
||||||
|
|
||||||
- **PRD:** `PRD-wire-format-v2.md` (resolves audit W9)
|
- **PRD:** `PRD-wire-format-v2.md` (resolves audit W9)
|
||||||
@@ -1163,9 +1204,10 @@ Statuses (in order of progression):
|
|||||||
| T1.1 | Approved | Kimi Code CLI | 2026-05-11T06:09Z | 2026-05-11T06:54Z | [report](reports/T1.1-report.md) | Approved 2026-05-11. Spawned T1.1.1 (field rustdoc) and T1.1.2 (refresh stale test-count). |
|
| T1.1 | Approved | Kimi Code CLI | 2026-05-11T06:09Z | 2026-05-11T06:54Z | [report](reports/T1.1-report.md) | Approved 2026-05-11. Spawned T1.1.1 (field rustdoc) and T1.1.2 (refresh stale test-count). |
|
||||||
| T1.1.1 | Open | — | — | — | — | Spawned from T1.1 review; non-blocking, claim after current in-flight |
|
| T1.1.1 | Open | — | — | — | — | Spawned from T1.1 review; non-blocking, claim after current in-flight |
|
||||||
| T1.1.2 | Open | — | — | — | — | Spawned from T1.1 review; non-blocking, claim after current in-flight |
|
| T1.1.2 | Open | — | — | — | — | Spawned from T1.1 review; non-blocking, claim after current in-flight |
|
||||||
| T1.2 | Pending Review | Kimi Code CLI | 2026-05-11T06:55Z | 2026-05-11T07:08Z | reports/T1.2-report.md | — |
|
| T1.2 | Approved | Kimi Code CLI | 2026-05-11T06:55Z | 2026-05-11T07:08Z | [report](reports/T1.2-report.md) | Approved 2026-05-11. Spawned T1.2.1 (rustdoc on MediaType variants/methods). Agent also resolved the T1.2 TODO inside MediaHeaderV2 — good call. |
|
||||||
| T1.3 | In Progress | Kimi Code CLI | 2026-05-11T07:10Z | — | — | — |
|
| T1.2.1 | Open | — | — | — | — | Spawned from T1.2 review; non-blocking |
|
||||||
| T1.4 | Open | — | — | — | — | — |
|
| T1.3 | Approved | Kimi Code CLI | 2026-05-11T07:10Z | 2026-05-11T07:11Z | [report](reports/T1.3-report.md) | Approved 2026-05-11. No follow-ups; docs-and-test-only change. |
|
||||||
|
| T1.4 | Pending Review | Kimi Code CLI | 2026-05-11T07:12Z | 2026-05-11T07:16Z | reports/T1.4-report.md | — |
|
||||||
| T1.5 | Open | — | — | — | — | — |
|
| T1.5 | Open | — | — | — | — | — |
|
||||||
| T1.6 | Open | — | — | — | — | — |
|
| T1.6 | Open | — | — | — | — | — |
|
||||||
| T1.7 | Open | — | — | — | — | — |
|
| T1.7 | Open | — | — | — | — | — |
|
||||||
@@ -1205,5 +1247,7 @@ Statuses (in order of progression):
|
|||||||
Items currently waiting on the reviewer:
|
Items currently waiting on the reviewer:
|
||||||
|
|
||||||
- T1.2 — Add MediaType enum — report: reports/T1.2-report.md
|
- T1.2 — Add MediaType enum — report: reports/T1.2-report.md
|
||||||
|
- T1.3 — Widen CodecId wire representation to u8 — report: reports/T1.3-report.md
|
||||||
|
- T1.4 — Add v2 MiniHeader with seq_delta — report: reports/T1.4-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`).
|
||||||
|
|||||||
92
docs/PRD/reports/T1.4-report.md
Normal file
92
docs/PRD/reports/T1.4-report.md
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# T1.4 — Add v2 `MiniHeader` with `seq_delta`
|
||||||
|
|
||||||
|
**Status:** Pending Review
|
||||||
|
**Agent:** Kimi Code CLI
|
||||||
|
**Started:** 2026-05-11T07:12Z
|
||||||
|
**Completed:** 2026-05-11T07:16Z
|
||||||
|
**Commit:** see git log
|
||||||
|
**PRD:** ../PRD-wire-format-v2.md
|
||||||
|
|
||||||
|
## What I changed
|
||||||
|
|
||||||
|
- `crates/wzp-proto/src/packet.rs:580-611` — renamed `MiniHeader` → `MiniHeaderV1`, kept all impls intact
|
||||||
|
- `crates/wzp-proto/src/packet.rs:613` — added `pub type MiniHeader = MiniHeaderV1;` backward-compat alias
|
||||||
|
- `crates/wzp-proto/src/packet.rs:616-640` — added new `MiniHeaderV2` struct (5 bytes: `seq_delta` + `timestamp_delta_ms` + `payload_len`) with `write_to`/`read_from`
|
||||||
|
- `crates/wzp-proto/src/packet.rs:642-666` — renamed `MiniFrameContext` → `MiniFrameContextV1`, kept all impls intact
|
||||||
|
- `crates/wzp-proto/src/packet.rs:668` — added `pub type MiniFrameContext = MiniFrameContextV1;` backward-compat alias
|
||||||
|
- `crates/wzp-proto/src/packet.rs:670-695` — added new `MiniFrameContextV2` tracking `MediaHeaderV2` baseline, with `update` and `expand` using explicit `seq_delta`
|
||||||
|
- `crates/wzp-proto/src/lib.rs:31` — re-exported `MiniHeaderV1`, `MiniHeaderV2`, `MiniFrameContextV1`, `MiniFrameContextV2`
|
||||||
|
- `crates/wzp-proto/src/packet.rs:1968-2014` — added 3 v2 tests: `mini_header_v2_roundtrip`, `mini_frame_context_v2_expand`, `mini_frame_context_v2_no_baseline`
|
||||||
|
|
||||||
|
## Why these choices
|
||||||
|
|
||||||
|
Same naming collision as T1.1: Rust does not allow a type alias and a struct with the same name in the same module. The new structs are named `MiniHeaderV2` and `MiniFrameContextV2` with temporary aliases preserving the old names; T1.5 will delete the v1 types and rename.
|
||||||
|
|
||||||
|
The v2 `MiniFrameContextV2::expand` uses `base.seq.wrapping_add(m.seq_delta as u32)` instead of the hard-coded `wrapping_add(1)` from v1, which resolves audit W4 (a missed full header no longer desyncs the sequence).
|
||||||
|
|
||||||
|
## Deviations from the task spec
|
||||||
|
|
||||||
|
1. **Step 2 / Step 3 (struct names):** The new mini struct is `MiniHeaderV2` and the new context is `MiniFrameContextV2` instead of `MiniHeader` / `MiniFrameContext`. Required because `pub type MiniHeader = MiniHeaderV1;` and `pub type MiniFrameContext = MiniFrameContextV1;` occupy the base names. T1.5 will resolve.
|
||||||
|
|
||||||
|
## Verification output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo test -p wzp-proto mini
|
||||||
|
running 12 tests
|
||||||
|
test packet::tests::full_vs_mini_size_comparison ... ok
|
||||||
|
test packet::tests::mini_frame_context_expand ... ok
|
||||||
|
test packet::tests::mini_frame_context_no_baseline ... ok
|
||||||
|
test packet::tests::mini_frame_context_v2_expand ... ok
|
||||||
|
test packet::tests::mini_frame_context_v2_no_baseline ... ok
|
||||||
|
test packet::tests::mini_frame_disabled ... ok
|
||||||
|
test packet::tests::mini_frame_encode_decode_sequence ... ok
|
||||||
|
test packet::tests::mini_frame_periodic_full ... ok
|
||||||
|
test packet::tests::mini_header_encode_decode ... ok
|
||||||
|
test packet::tests::mini_header_v2_roundtrip ... ok
|
||||||
|
test packet::tests::mini_header_wire_size ... ok
|
||||||
|
test packet::tests::candidate_update_minimal_roundtrip ... ok
|
||||||
|
|
||||||
|
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 100 filtered out; finished in 0.00s
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo build -p wzp-proto -p wzp-codec -p wzp-fec -p wzp-crypto -p wzp-transport -p wzp-relay -p wzp-client -p wzp-web -p wzp-native
|
||||||
|
Finished `dev` profile [unoptimized + debuginfo] target(s) in 15.71s
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo test -p wzp-proto -p wzp-codec -p wzp-fec -p wzp-crypto -p wzp-transport -p wzp-relay -p wzp-client -p wzp-web -p wzp-native --no-fail-fast
|
||||||
|
...
|
||||||
|
test result: ok. 571 passed; 0 failed; ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo clippy -p wzp-proto --all-targets -- -D warnings
|
||||||
|
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.19s
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo fmt --all -- --check
|
||||||
|
# (clean)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test summary
|
||||||
|
|
||||||
|
- Tests added: 3 (`mini_header_v2_roundtrip`, `mini_frame_context_v2_expand`, `mini_frame_context_v2_no_baseline`)
|
||||||
|
- Tests modified: 0
|
||||||
|
- Workspace test count before: 568 pass / 0 fail (non-Android subset)
|
||||||
|
- Workspace test count after: 571 pass / 0 fail (non-Android subset)
|
||||||
|
- `cargo clippy -p wzp-proto --all-targets -- -D warnings`: pass
|
||||||
|
- `cargo fmt --all -- --check`: pass
|
||||||
|
|
||||||
|
## Risks / follow-ups
|
||||||
|
|
||||||
|
- `MiniHeaderV2` / `MiniFrameContextV2` must be renamed to `MiniHeader` / `MiniFrameContext` in T1.5 after v1 types are deleted.
|
||||||
|
|
||||||
|
## 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