# Haiku-Ready Task Breakdown > Companion to `docs/PRD/README.md`. Every task here is sized for an agent with limited context to pick up cold: it names exact files, exact symbols, and exact verification commands. Do tasks in order within a wave; waves are dependency-ordered. --- ## Agent operating instructions — read first You are an implementing agent. The human is the reviewer. **Your job is not done when the code compiles; it is done when the reviewer has approved your report.** Read this section before touching any task. ### Workflow per task 1. **Claim the task.** Move its status in the [Status board](#status-board) at the bottom of this file from `Open` → `In Progress`. Add your handle / model name and a UTC timestamp. 2. **Implement.** Follow the steps in the task block exactly. If the steps don't fit reality (e.g. line numbers shifted, a referenced symbol doesn't exist, the API has evolved), **stop and surface the mismatch in your report** — do not improvise silently. 3. **Verify.** Run the exact commands in the task's `Verify` block. Capture their output verbatim — the reviewer will read it. 4. **Write the report.** Create `docs/PRD/reports/T-report.md` using the template below. One report per task. No exceptions. 5. **Commit.** One commit per task. Message: `T: `. The report file is part of the same commit. 6. **Move to review.** Update the [Status board](#status-board): `In Progress` → `Pending Review`. Add a link to the report path. 7. **Stop.** Do NOT start the next task until the reviewer marks the previous one `Approved`. If they mark it `Changes Requested`, address the feedback in a follow-up commit, update the report, and move back to `Pending Review`. ### Follow-up tasks (`T.`) When the reviewer approves a task but finds small non-blocking issues (missing docs, stale comments, minor cleanups), they **spawn new follow-up tasks** instead of carrying the work forward into an unrelated task. The parent task stays `Approved` and closed. Follow-up IDs extend the parent: `T1.1.1`, `T1.1.2`, etc. They are first-class tasks — full block in this file with `Files`, `Steps`, `Verify`, `Done when` — and they show up in the status board between the parent and the next sibling (`T1.1.1` sits between `T1.1` and `T1.2`). Agents pick up follow-ups in the same order they pick up wave tasks. A follow-up never blocks the next wave task: e.g. `T1.2` is claimable even if `T1.1.1` is still `Open`, unless the follow-up's body explicitly says otherwise (it usually doesn't). Reviewers, when spawning a follow-up: 1. Add a numbered task block in the right section of this file (just below the parent). 2. Add a status-board row between the parent and the next sibling. 3. Reference the follow-up in the parent report's reviewer notes (e.g. "Spawned T1.1.1, T1.1.2 to track follow-ups."). ### Report template Every report lives at `docs/PRD/reports/T-report.md` and uses this template: ```markdown # T **Status:** Pending Review **Agent:** **Started:** **Completed:** **Commit:** **PRD:** ../.md ## What I changed - `:` — - `:` — - (etc.) ## Why these choices <2-6 sentences explaining any non-obvious decision: why this signature, why this default, why this error type, why a deviation from the task steps if any. If you followed the steps verbatim, say "Followed steps T.1 through T.N without deviation." and that's enough.> ## Deviations from the task spec ## Verification output For each `Verify` command in the task block, paste the actual output. Trim benign noise (warnings already present on main) but never trim test failure output. ``` $ cargo test -p wzp-proto media_header_v2_roundtrip running 1 test test packet::tests::media_header_v2_roundtrip ... ok test result: ok. 1 passed; 0 failed; ... ``` ## Test summary - Tests added: - Tests modified: - Workspace test count before: / after: - `cargo clippy --workspace --all-targets -- -D warnings`: pass / fail (or N known-debt errors in ; see PROTOCOL-AUDIT.md) - `cargo fmt --all -- --check`: pass / fail ## Risks / follow-ups ## 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 ``` ### Coding standards — non-negotiable These apply to every task. They are NOT repeated in each task block. Violating them is grounds for `Changes Requested` even if the code works. 1. **Rust edition 2024** (set in workspace root). No exceptions. 2. **`cargo fmt --all`** must produce a clean diff before commit. CI will reject otherwise. 3. **`cargo clippy --workspace --all-targets -- -D warnings`** must pass in crates you touch. Do not `#[allow(...)]` to silence — fix the root cause. If a lint is genuinely wrong, justify the allow in the report. Pre-existing debt in other crates (documented in `PROTOCOL-AUDIT.md`) is not your problem. 4. **No `unwrap()` / `expect()` in production code paths.** Tests are fine. Production: return a typed error. 5. **No `println!` / `eprintln!`.** Use `tracing::{debug,info,warn,error}!`. The crates are already wired for tracing. 6. **No new dependencies without justification.** If a task forces a new crate, list it under "Risks / follow-ups" in the report so the reviewer can sanity-check the supply chain. 7. **One commit per task** — see workflow. Don't squash multiple tasks. Don't split a task across commits unless the task itself instructs you to. 8. **Never modify `Cargo.lock` by hand.** Run a real build; commit the resulting lockfile delta. 9. **Public API changes need rustdoc.** Every new `pub fn`, `pub struct`, `pub enum`, or `pub trait` gets a `///` doc comment. Private items: doc only when non-obvious. 10. **Tests live with code.** `#[cfg(test)] mod tests { ... }` next to the code under test. Integration tests in `crates//tests/.rs` only when they exercise multiple modules end-to-end. 11. **Async: tokio only.** Do not introduce `async-std` or `smol`. Spawn via `tokio::spawn`, not raw futures. 12. **Wire format types live in `wzp-proto`.** Do not redefine `MediaHeader`, `SignalMessage`, or codec/quality types in another crate. Re-export if needed. 13. **No emoji in code or commit messages** unless the surrounding context already uses them. 14. **No AI-attribution lines in commit messages.** Plain `T: ` body, that's it. 15. **Comments:** comment WHY, never WHAT. If the code needs a WHAT comment, rename the symbol instead. See repo-root CLAUDE.md (if present) for global guidance. 16. **Don't take destructive actions.** Specifically: never `git reset --hard`, `git push --force`, drop database tables, delete branches, or touch CI configs without the reviewer asking. If you think you need to, stop and ask in your report. 17. **Auto mode is not a license to skip these.** Even when the harness is set to autonomous execution, the workflow (report → Pending Review → wait for Approved) is mandatory. ### When to stop and ask Stop and write a report with status `Blocked` (not `Pending Review`) if any of these happen: - A task step references code that doesn't exist. - A test fails for reasons unrelated to your change. - The workspace doesn't build at HEAD before you started (the baseline is dirty). - You need to make a meaningful design decision the task didn't anticipate. - A "Verify" command produces output you don't understand. A `Blocked` report is not a failure — it is the correct outcome when the task spec is wrong or incomplete. --- ## How to read a task Each task block has: - **ID & title** — `T.` like `T1.1`. - **PRD** — link to the parent PRD for the "why". - **Effort** — rough hours for a junior dev with this doc + the repo. - **Files** — exact paths you will edit. - **Context** — 2-4 lines on what's there today. - **Steps** — numbered, do them in order. - **Verify** — exact commands; output must match. - **Done when** — single-line acceptance. --- ## Environment setup (do this once) ```bash # All commands assume CWD = /Users/manwe/CascadeProjects/warzonePhone cargo build --workspace # baseline: must succeed cargo test --workspace --no-fail-fast # baseline: should be 571 pass / 0 fail (non-Android subset) ``` If either fails before you start a task, stop and report — the tree is dirty. ### Conventions - Format on save: `cargo fmt --all` after any code change. - Lints: `cargo clippy --workspace --all-targets -- -D warnings` must pass in crates you touch before commit. Pre-existing debt in other crates is documented in `PROTOCOL-AUDIT.md`. - Tests live next to code under `#[cfg(test)]` modules, or in `crates//tests/`. - Wire format types: `crates/wzp-proto/src/packet.rs` is authoritative. Do not duplicate field semantics elsewhere. - Commit one task per commit. Reference task ID in commit message: `T1.1: widen MediaHeader to v2`. ### Useful greps ```bash grep -rn "MediaHeader::" --include="*.rs" # 6 files outside tests grep -rn "MiniHeader::" --include="*.rs" grep -rn "SignalMessage::" --include="*.rs" grep -rn "CodecId::" --include="*.rs" ``` --- # Wave 1 — Foundation (target: 1 week) Goal: v2 wire format lands cleanly. Audio works under v2. Old clients are politely rejected. --- ## T1.1 — Add v2 `MediaHeader` type - **PRD:** `PRD-wire-format-v2.md` - **Effort:** 3 h - **Files:** - `crates/wzp-proto/src/packet.rs` ### Context Today `MediaHeader` is defined at line 20 of `packet.rs` with `WIRE_SIZE = 12` (line 47). Fields are bit-packed across the first two bytes. It is constructed in tests starting around line 1229. ### Steps 1. Open `crates/wzp-proto/src/packet.rs`. 2. **Do not delete** the existing `MediaHeader`. Rename it in-place to `MediaHeaderV1` (also rename `WIRE_SIZE` consts only on that struct). Keep all impls. 3. Below the `MediaHeaderV1` block, add a new `MediaHeader` struct (16 bytes, byte-aligned): ```rust /// 16-byte v2 media header. See docs/PRD/PRD-wire-format-v2.md. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct MediaHeader { pub version: u8, // always 2 pub flags: u8, // bit 7 T, bit 6 Q, bit 5 KeyFrame, bit 4 FrameEnd pub media_type: MediaType, // u8 wire repr pub codec_id: CodecId, pub stream_id: u8, pub fec_ratio: u8, // 0..200 → 0.0..2.0 pub seq: u32, pub timestamp: u32, pub fec_block: u16, } impl MediaHeader { pub const WIRE_SIZE: usize = 16; pub const VERSION: u8 = 2; pub fn write_to(&self, buf: &mut impl BufMut) { buf.put_u8(self.version); buf.put_u8(self.flags); buf.put_u8(self.media_type.to_wire()); buf.put_u8(self.codec_id.to_wire()); buf.put_u8(self.stream_id); buf.put_u8(self.fec_ratio); buf.put_u32(self.seq); buf.put_u32(self.timestamp); buf.put_u16(self.fec_block); } pub fn read_from(buf: &mut impl Buf) -> Option { if buf.remaining() < Self::WIRE_SIZE { return None; } let version = buf.get_u8(); if version != Self::VERSION { return None; } let flags = buf.get_u8(); let media_type = MediaType::from_wire(buf.get_u8())?; let codec_id = CodecId::from_wire(buf.get_u8())?; let stream_id = buf.get_u8(); let fec_ratio = buf.get_u8(); let seq = buf.get_u32(); let timestamp = buf.get_u32(); let fec_block = buf.get_u16(); Some(Self { version, flags, media_type, codec_id, stream_id, fec_ratio, seq, timestamp, fec_block }) } pub const FLAG_REPAIR: u8 = 0b1000_0000; pub const FLAG_QUALITY: u8 = 0b0100_0000; pub const FLAG_KEYFRAME: u8 = 0b0010_0000; pub const FLAG_FRAME_END: u8 = 0b0001_0000; pub fn is_repair(&self) -> bool { self.flags & Self::FLAG_REPAIR != 0 } pub fn has_quality(&self) -> bool { self.flags & Self::FLAG_QUALITY != 0 } pub fn is_keyframe(&self) -> bool { self.flags & Self::FLAG_KEYFRAME != 0 } pub fn is_frame_end(&self) -> bool { self.flags & Self::FLAG_FRAME_END != 0 } } ``` 4. `MediaType` and `CodecId::to_wire` (8-bit) come from T1.2 and T1.3 — add a `// TODO(T1.2)` placeholder if those aren't merged yet (use `u8` directly). 5. Add a round-trip test next to the existing tests: ```rust #[test] fn media_header_v2_roundtrip() { let h = MediaHeader { version: 2, flags: MediaHeader::FLAG_QUALITY, media_type: MediaType::Audio, codec_id: CodecId::Opus24k, stream_id: 0, fec_ratio: 50, seq: 0xDEAD_BEEF, timestamp: 0x1234_5678, fec_block: 0xABCD, }; let mut buf = BytesMut::with_capacity(MediaHeader::WIRE_SIZE); h.write_to(&mut buf); assert_eq!(buf.len(), 16); let mut cursor = std::io::Cursor::new(&buf[..]); let parsed = MediaHeader::read_from(&mut cursor).unwrap(); assert_eq!(h, parsed); } ``` ### Verify ```bash cargo test -p wzp-proto media_header_v2_roundtrip cargo build --workspace ``` ### Done when - New test passes. Workspace still builds. `MediaHeaderV1` still exists (we delete it later in T1.5). --- ## T1.1.1 — Add rustdoc on `MediaHeaderV2` public fields - **Parent:** T1.1 (Approved) - **PRD:** `PRD-wire-format-v2.md` - **Effort:** 15 min - **Files:** - `crates/wzp-proto/src/packet.rs` ### Context T1.1 added `MediaHeaderV2` with inline `//` comments on the public fields. The pre-existing `MediaHeaderV1` uses `///` rustdoc on every public field (coding standard #9 — public items need rustdoc). Match the existing pattern. ### Steps 1. Open `crates/wzp-proto/src/packet.rs`. Find `pub struct MediaHeaderV2`. 2. For each public field, replace the trailing `//` comment with a leading `///` doc comment. Example transformation: Before: ```rust pub struct MediaHeaderV2 { pub version: u8, // always 2 pub flags: u8, // bit 7 T, bit 6 Q, bit 5 KeyFrame, bit 4 FrameEnd ... } ``` After: ```rust pub struct MediaHeaderV2 { /// Protocol version. Always `2` on the wire; `read_from` rejects anything else. pub version: u8, /// Bit-packed flags. See `FLAG_REPAIR`, `FLAG_QUALITY`, `FLAG_KEYFRAME`, `FLAG_FRAME_END`. pub flags: u8, ... } ``` 3. Document the four `FLAG_*` constants with `///` too. One line each is fine. 4. Document the four `is_*` / `has_*` accessor methods with `///`. One line each. 5. The `media_type: u8` field gets a doc comment that mentions the `TODO(T1.2)` — keep that TODO inline. ### Verify ```bash cargo doc -p wzp-proto --no-deps 2>&1 | grep -i "missing" # should be empty cargo clippy -p wzp-proto --all-targets -- -D warnings -W missing_docs # should pass ``` ### Done when - All public items on `MediaHeaderV2` carry `///` doc comments. - `cargo doc -p wzp-proto --no-deps` emits no "missing documentation" warnings for `MediaHeaderV2`. --- ## T1.1.2 — Refresh stale test-count figures in docs - **Parent:** T1.1 (Approved) - **PRD:** `PRD-wire-format-v2.md` (housekeeping) - **Effort:** 30 min - **Files:** - `docs/ARCHITECTURE.md` - `docs/PRD/TASKS.md` (the Environment setup block) - Any other doc referencing "272 tests" ### Context The original audit and the TASKS environment-setup block reference a workspace test count of **272**. The actual non-Android workspace baseline measured during T1.1 is **564** (with 1 added test → 565 after T1.1). The 272 figure is stale. ### Steps 1. Grep for the stale figure across the docs: ```bash grep -rn "272 tests\|272 pass\|272 total" docs/ ``` 2. For each hit, replace with the current count. **Re-measure before writing the number.** ```bash cargo test --workspace --no-fail-fast 2>&1 | grep "test result:" | awk '{s+=$4} END {print s}' # ... this gives a rough total; sanity-check against per-crate output ``` 3. If `wzp-android` cannot build on the dev machine (no NDK), note that the count excludes `wzp-android` and is the "non-Android subset". 4. Update the per-crate Test Coverage table in `docs/ARCHITECTURE.md` (search for "## Test Coverage") with the new per-crate counts. ### Verify ```bash grep -rn "272 tests\|272 pass" docs/ # should be empty ``` ### Done when - No doc references the stale 272 figure. - ARCHITECTURE.md test coverage table reflects current per-crate counts. --- ## T1.2 — Add `MediaType` enum - **PRD:** `PRD-wire-format-v2.md` - **Effort:** 1 h - **Files:** - `crates/wzp-proto/src/codec_id.rs` (or new sibling file `media_type.rs`) - `crates/wzp-proto/src/lib.rs` (re-export) ### Steps 1. Create `crates/wzp-proto/src/media_type.rs`: ```rust use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u8)] pub enum MediaType { Audio = 0, Video = 1, Data = 2, Control = 3, } impl MediaType { pub const fn to_wire(self) -> u8 { self as u8 } pub const fn from_wire(v: u8) -> Option { match v { 0 => Some(Self::Audio), 1 => Some(Self::Video), 2 => Some(Self::Data), 3 => Some(Self::Control), _ => None, } } } ``` 2. In `crates/wzp-proto/src/lib.rs`, add `pub mod media_type;` and `pub use media_type::MediaType;`. ### Verify ```bash cargo build -p wzp-proto cargo test -p wzp-proto ``` ### Done when `MediaType` is importable as `wzp_proto::MediaType`. --- ## 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 - **PRD:** `PRD-wire-format-v2.md` (resolves audit W9) - **Effort:** 1 h - **Files:** - `crates/wzp-proto/src/codec_id.rs` ### Context `CodecId::to_wire` returns `self as u8` (already u8 in memory). The "4 bits on wire" is enforced by how `MediaHeaderV1` packs it. With v2 the wire byte is full 8-bit — so reserve more IDs without touching `to_wire`/`from_wire` for the existing variants. ### Steps 1. In `codec_id.rs`, **reserve** (but do not implement) future codec IDs by adding doc comments after `Opus64k = 8`: ```rust // Reserved for video codecs; implementations land in PRD-video-multicodec. // 9 => H264 baseline // 10 => H264 main // 11 => H265 main // 12 => AV1 // 13 => VP9 ``` 2. **Do not** add new variants yet — that happens in T4.x once `wzp-video` exists. 3. Add a regression test confirming `from_wire(9..=255)` returns `None`: ```rust #[test] fn codec_id_unknown_values_rejected() { for v in 9u8..=255 { assert!(CodecId::from_wire(v).is_none(), "v={v}"); } } ``` ### Verify ```bash cargo test -p wzp-proto codec_id_unknown_values_rejected ``` ### Done when Test passes. Existing audio tests still pass. --- ## T1.4 — Add v2 `MiniHeader` with `seq_delta` - **PRD:** `PRD-wire-format-v2.md` (resolves audit W4) - **Effort:** 2 h - **Files:** - `crates/wzp-proto/src/packet.rs` ### Context Existing `MiniHeader` is 4 bytes at line 501. `MiniFrameContext::expand` infers `seq` by `wrapping_add(1)` (line ~553) — a missed full header desyncs. v2 carries explicit `seq_delta`. ### Steps 1. Rename existing `MiniHeader` → `MiniHeaderV1` and `MiniFrameContext` → `MiniFrameContextV1`. Keep impls intact. 2. Add new `MiniHeader` (5 bytes): ```rust #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct MiniHeader { pub seq_delta: u8, // packets since baseline; 1 in steady state pub timestamp_delta_ms: u16, pub payload_len: u16, } impl MiniHeader { 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 { 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(), }) } } ``` 3. Add `MiniFrameContext` (no `V1` suffix) tracking v2 `MediaHeader`: ```rust #[derive(Clone, Debug, Default)] pub struct MiniFrameContext { last: Option, } impl MiniFrameContext { pub fn update(&mut self, h: &MediaHeader) { self.last = Some(*h); } pub fn expand(&mut self, m: &MiniHeader) -> Option { 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) } } ``` 4. Add round-trip test mirroring `T1.1`. ### Verify ```bash cargo test -p wzp-proto mini ``` ### Done when v2 mini header round-trips. v1 type still compiles. --- ## T1.4.1 — Add rustdoc on `MiniHeaderV2` / `MiniFrameContextV2` public items - **Parent:** T1.4 (Approved) - **PRD:** `PRD-wire-format-v2.md` - **Effort:** 15 min - **Files:** - `crates/wzp-proto/src/packet.rs` ### Context T1.4 added v2 types with top-level `///` docs on the structs themselves but no `///` rustdoc on the fields or methods. Same shape of follow-up as T1.1.1 and T1.2.1 — coding standard #9. ### Steps 1. Open `crates/wzp-proto/src/packet.rs`. Find `pub struct MiniHeaderV2`. 2. Add `///` doc comments to each public field: - `seq_delta` — explain it's the count of packets since the baseline (typically 1), and that explicit deltas resolve audit W4 (one missed full header no longer desyncs). - `timestamp_delta_ms` — milliseconds since baseline's `timestamp`. - `payload_len` — bytes of payload following the mini header. 3. Document `WIRE_SIZE`, `write_to`, `read_from`. One line each. Mention that `read_from` returns `None` on short buffer. 4. Same treatment for `MiniFrameContextV2`: doc the `update` and `expand` methods. `expand` should note that it returns `None` if no baseline has been set. ### 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 public items on `MiniHeaderV2` and `MiniFrameContextV2` carry `///` doc comments. - `cargo doc -p wzp-proto --no-deps` emits no "missing documentation" warnings for these types. --- ## T1.5 — Migrate emit/parse sites to v2 - **PRD:** `PRD-wire-format-v2.md` - **Effort:** 4 h - **Files (touch all that use `MediaHeader::`):** - `crates/wzp-proto/src/packet.rs` - `crates/wzp-client/src/call.rs` - `crates/wzp-relay/src/room.rs` - `crates/wzp-relay/src/pipeline.rs` - `crates/wzp-android/src/engine.rs` ### Context Only 6 production files outside `packet.rs` reference `MediaHeader::`. Confirm with: ```bash grep -rln "MediaHeader::" crates/ | grep -v target ``` ### Steps 1. For each file in the list above, replace v1 construction patterns with v2. The audio fields are unchanged in semantics; new fields default as follows: - `version: 2` - `flags: 0` (set `FLAG_QUALITY` where the v1 code set `has_quality_report = true`, etc.) - `media_type: MediaType::Audio` - `stream_id: 0` - `fec_ratio: ` (convert range) - `seq: old_seq as u32` - `timestamp` unchanged - `fec_block: u16::from(old_fec_block) | (u16::from(old_fec_symbol) << 8)` for audio (low byte block_id, high byte symbol_idx) 2. Update `MediaHeaderV1`-using parse code identically — convert from u16 seq/u8 block_id to v2 layout at parse boundary. 3. Search for `WIRE_SIZE` arithmetic and update buffer sizes (12 → 16, 4 → 5). 4. Delete `MediaHeaderV1`, `MiniHeaderV1`, `MiniFrameContextV1` once everything builds. ### Verify ```bash cargo build --workspace cargo test --workspace --no-fail-fast # Expected: all 571 tests still pass ``` ### Done when - Workspace builds clean. - All audio tests pass. - No reference to `MediaHeaderV1` / `MiniHeaderV1` anywhere. --- ## T1.5.1 — Remove `unwrap()` from `encode_compact` - **Parent:** T1.5 (Approved) - **PRD:** `PRD-wire-format-v2.md` (cleanup) - **Effort:** 20 min - **Files:** - `crates/wzp-proto/src/packet.rs` ### Context `encode_compact` calls `ctx.last_header().unwrap()` at line ~262. The invariant ("a full header is forced on the first frame and every `MINI_FRAME_FULL_INTERVAL` frames") makes it logically safe, but standard #4 forbids `unwrap()` in production paths. Carried over from v1. ### Steps 1. Open `crates/wzp-proto/src/packet.rs`. Find `pub fn encode_compact`. 2. Replace the `unwrap()` with one of: - **Recommended:** when `ctx.last_header()` is `None`, fall back to emitting a full frame and force `frames_since_full = 0`. This makes the invariant explicit in the code rather than implicit. - Alternative: return `Result` with a typed `NoBaselineHeader` variant. More invasive (changes the public signature). 3. Add a test that constructs a fresh `MiniFrameContext` and calls `encode_compact` immediately — without the existing fix, this would panic; with the fix, it should emit a full frame. ### Verify ```bash cargo test -p wzp-proto encode_compact cargo clippy -p wzp-proto --all-targets -- -D warnings grep -n "\.unwrap()" crates/wzp-proto/src/packet.rs | grep -v "#\[cfg(test)\]\|^[[:space:]]*//\|tests::" # the unwrap on line ~262 should be gone; only test-code unwraps remain. ``` ### Done when - No `unwrap()` in `encode_compact` or anywhere else in non-test code in `packet.rs`. - New test passes; existing `encode_compact` tests still pass. --- ## T1.5.2 — Workspace clippy hygiene + document pre-existing debt - **Parent:** T1.5 (Approved) - **PRD:** `PRD-wire-format-v2.md` (process) - **Effort:** 30 min - **Files:** - `docs/PROTOCOL-AUDIT.md` (add a "Known pre-existing clippy debt" section) - This file (TASKS.md) — update report template instruction to require workspace clippy ### Context T1.5 review revealed two issues: (1) the agent ran only `-p wzp-proto` clippy, not workspace; (2) workspace clippy fails with 9 `wzp-codec` errors and 3 `warzone-protocol` errors. Both are pre-existing (verified against HEAD~1). Need to capture these as known debt so they don't stay invisible, and tighten the report template to require workspace clippy on every task. ### Steps 1. Run `cargo clippy --workspace --all-targets -- -D warnings 2>&1 | grep -E "^error\[|could not compile" | head -50` and capture the output. 2. Add a section to `docs/PROTOCOL-AUDIT.md` named **"Known pre-existing clippy debt (as of T1.5.2)"** listing the failing crates and a brief description per error category (manual ASCII case-cmp, manual arithmetic check, loop index, etc.). Reference the commit SHA of HEAD at time of measurement. 3. In `docs/PRD/TASKS.md`, update the report template's "Test summary" section: change `cargo clippy --workspace --all-targets -- -D warnings: pass / fail` to `cargo clippy --workspace --all-targets -- -D warnings: pass / fail (or N known-debt errors in ; see PROTOCOL-AUDIT.md)`. This makes the expectation explicit and gives agents a way to acknowledge known debt without re-discussing it every task. 4. Optional: add a `make clippy-baseline` or similar script to `tools/` that prints expected-error count so agents can detect regressions. ### Verify ```bash grep -c "Known pre-existing clippy debt" docs/PROTOCOL-AUDIT.md # >= 1 grep -c "or N known-debt errors" docs/PRD/TASKS.md # >= 1 ``` ### Done when - PROTOCOL-AUDIT.md has the known-debt section with current error counts and categories. - TASKS.md report template reflects the new expectation. - A follow-up cleanup task is created in the audit (separate from this one) to actually fix the pre-existing debt over time. --- ## T1.6 — Protocol version negotiation in handshake - **PRD:** `PRD-wire-format-v2.md` + `PRD-protocol-hardening.md` (W12) - **Effort:** 3 h - **Files:** - `crates/wzp-proto/src/packet.rs` (extend `SignalMessage`) - `crates/wzp-client/src/handshake.rs` - `crates/wzp-relay/src/handshake.rs` ### Steps 1. In `packet.rs`, add to `CallOffer`: ```rust #[serde(default = "default_proto_version")] pub protocol_version: u8, #[serde(default = "default_supported_versions")] pub supported_versions: Vec, ``` Helpers: ```rust fn default_proto_version() -> u8 { 2 } fn default_supported_versions() -> Vec { vec![2] } ``` 2. Add a new `Hangup` reason variant. Find `SignalMessage::Hangup` (look for the `Hangup` variant in the enum near the bottom) and add to the reason enum / fields: ```rust ProtocolVersionMismatch { server_supported: Vec }, ``` If `reason` is a `String`, instead add a structured variant `SignalMessage::ProtocolVersionMismatch { server_supported: Vec }` and use that. 3. In `crates/wzp-relay/src/handshake.rs`, after parsing `CallOffer`, check `protocol_version == 2`. If not, send `ProtocolVersionMismatch` and close. 4. In `crates/wzp-client/src/handshake.rs`, set the field on outgoing `CallOffer`; on receiving the mismatch variant, return a typed error. ### Verify ```bash cargo test -p wzp-relay handshake cargo test -p wzp-client handshake ``` ### Done when A v1-style offer (missing `protocol_version` field — serde default makes it 2 in this codebase, so explicitly test with `protocol_version: 1`) is rejected with the typed signal. --- ## T1.7 — Move `QualityReport` trailer inside AEAD payload - **PRD:** `PRD-protocol-hardening.md` (W5) - **Effort:** 2 h - **Files:** - `crates/wzp-client/src/call.rs` (encode/decode paths) - `crates/wzp-crypto/src/session.rs` (verify AEAD boundary) ### Context A `QualityReport` (4 bytes) is appended to media packets when the `Q` flag is set. The flag is in the (plaintext, AAD-bound) header; the trailer must sit **inside** the AEAD payload so stripping it corrupts decryption. ### Steps 1. Grep for the encode site: ```bash grep -rn "has_quality_report\|FLAG_QUALITY\|QualityReport" crates/wzp-client/src/call.rs ``` 2. Find where `QualityReport::write_to` (or `put_*` calls) writes the 4 bytes. Confirm it writes into the buffer that is **then** passed to `encrypt_in_place` / `seal` — not after. 3. If currently appended *after* AEAD seal: refactor so the order is: - Write `MediaHeader` (becomes AAD). - Write payload. - Write `QualityReport` trailer if Q flag set. - AEAD-seal the (payload + trailer) bytes with header as AAD. 4. Mirror on decode side. 5. Add a test that tampers with the trailer post-encrypt and asserts decrypt fails. ### Verify ```bash cargo test -p wzp-client quality_report_aead cargo test -p wzp-crypto ``` ### Done when - Tamper test passes (decryption fails on trailer tamper). - Round-trip with quality flag set still works. --- ## T1.8 — Per-stream anti-replay window with configurable size - **PRD:** `PRD-protocol-hardening.md` (W11) - **Effort:** 2 h - **Files:** - `crates/wzp-crypto/src/anti_replay.rs` - `crates/wzp-crypto/src/session.rs` (or wherever the window is owned) ### Steps 1. Today the window is fixed 64 packets. Make it constructible with size: ```rust impl AntiReplay { pub fn with_window(size: usize) -> Self { ... } } ``` 2. The session owner (search `AntiReplay::new`) is updated to allocate per `(stream_id, MediaType)`. Use a `HashMap<(u8, MediaType), AntiReplay>` keyed on the v2 header fields. 3. Default sizes: - `Audio`: 64 - `Video`: 1024 - `Data`: 256 - `Control`: 32 ### Verify ```bash cargo test -p wzp-crypto anti_replay ``` ### Done when - A new test confirms a 200-packet video burst with one reorder doesn't drop any. - Existing audio anti-replay tests pass. --- # Wave 2 — Feedback + abuse mitigation (target: 1 week) Goal: BWE drives adaptation. Tier A/B/C conformance running in observe-only mode at the relay. --- ## T2.1 — Add `SignalMessage::TransportFeedback` - **PRD:** `PRD-transport-feedback-bwe.md` - **Effort:** 2 h - **Files:** - `crates/wzp-proto/src/packet.rs` ### Steps 1. Add to the `SignalMessage` enum: ```rust TransportFeedback { #[serde(default)] version: u8, // = 1 stream_id: u8, acked_seqs: Vec, nacked_seqs: Vec, remb_bps: u32, recv_time_us: u64, }, ``` 2. Add a unit test serializing/deserializing with `bincode` to ensure forward/backward compat. ### Verify ```bash cargo test -p wzp-proto transport_feedback ``` ### Done when Variant round-trips. No other code consumes it yet — that's T2.2/T2.3. --- ## T2.2 — `BandwidthEstimator` in `wzp-proto::bandwidth` - **PRD:** `PRD-transport-feedback-bwe.md` - **Effort:** 4 h - **Files:** - `crates/wzp-proto/src/bandwidth.rs` (already exists — extend, don't replace) - `crates/wzp-transport/src/path_monitor.rs` (read existing cwnd/RTT exposure) ### Context `bandwidth.rs` already exists (14 KB). Read it first. The `QuinnPathSnapshot` type exposes `loss_pct`, `rtt_ms` today; add `cwnd_bps`, `bytes_in_flight` if missing. ### Steps 1. Read `crates/wzp-transport/src/path_monitor.rs` to find how Quinn `PathStats` are exposed. 2. Add to `QuinnPathSnapshot`: ```rust pub cwnd_bytes: u64, pub bytes_in_flight: u64, ``` Populate from `quinn::Connection::stats().path`. 3. In `wzp-proto/src/bandwidth.rs`, add: ```rust pub struct BandwidthEstimator { cwnd_bps: AtomicU64, peer_remb_bps: AtomicU64, smoothed_bps: AtomicU64, } impl BandwidthEstimator { pub fn new() -> Self { ... default ... } pub fn update_from_quinn(&self, snap: &QuinnPathSnapshot) { /* compute cwnd_bps = cwnd_bytes * 8 / rtt_s */ } pub fn update_from_peer(&self, fb_remb_bps: u32) { ... } pub fn target_send_bps(&self) -> u64 { let m = self.cwnd_bps.load(Relaxed).min(self.peer_remb_bps.load(Relaxed)); (m as f64 * 0.9) as u64 } } ``` 4. EWMA smoothing: half-life 2 s. Update `smoothed_bps` from input on each tick. ### Verify ```bash cargo test -p wzp-proto bandwidth cargo test -p wzp-transport ``` ### Done when - Unit test: feed scripted cwnd + remb values, assert `target_send_bps` smooths correctly. --- ## T2.3 — Plumb BWE into adaptive controller - **PRD:** `PRD-transport-feedback-bwe.md` - **Effort:** 3 h - **Files:** - `crates/wzp-proto/src/quality.rs` (`AdaptiveQualityController`) - `crates/wzp-client/src/call.rs` (instantiate + feed) ### Steps 1. Add a setter to `AdaptiveQualityController`: ```rust pub fn set_bandwidth_estimator(&mut self, bwe: Arc) { self.bwe = Some(bwe); } ``` 2. In the controller's upgrade decision (search for "consecutive_good_reports" or similar threshold logic), add a guard: ```rust if let Some(bwe) = &self.bwe { if bwe.target_send_bps() < self.current_tier_ceiling_bps() * 130 / 100 { return; } } ``` 3. In `call.rs`, instantiate one `Arc` per session, feed it from both send loop (`update_from_quinn` from path snapshot) and recv loop (`update_from_peer` from incoming TransportFeedback), pass to the controller. ### Verify ```bash cargo test -p wzp-proto quality ``` ### Done when Existing quality tests pass with BWE attached. New test: scripted "loss = 0, cwnd = 50 kbps" never upgrades past Opus 24k. --- ## T2.4 — Relay conformance: Tier A (bitrate ceiling) - **PRD:** `PRD-relay-conformance.md` - **Effort:** 3 h - **Files:** - `crates/wzp-relay/src/conformance.rs` (new) - `crates/wzp-relay/src/room.rs` (call site) ### Steps 1. Create `crates/wzp-relay/src/conformance.rs`: ```rust use std::sync::atomic::{AtomicU64, Ordering::Relaxed}; use std::time::Instant; use wzp_proto::{CodecId, MediaHeader, MediaType}; pub struct ConformanceMeter { window_start: parking_lot::Mutex, bytes_in_window: AtomicU64, packets_in_window: AtomicU64, last_seq: AtomicU64, last_ts: AtomicU64, } #[derive(Debug)] pub enum Violation { BitrateExceeded, PacketRateExceeded, TimestampDrift } impl ConformanceMeter { pub fn new() -> Self { ... } pub fn observe(&self, h: &MediaHeader, payload_len: usize, now: Instant) -> Result<(), Violation> { // Tier A let window_bytes = self.bytes_in_window.fetch_add((MediaHeader::WIRE_SIZE + payload_len) as u64, Relaxed); // ... compare against ceiling_bps_for(h.codec_id, h.media_type) } } pub fn ceiling_bps(codec: CodecId) -> u64 { let nominal = codec.bitrate_bps() as u64; (nominal * 3 * 115 / 100).max(2_000) // FEC 2.0 + 15% overhead, floor 2 kbps } ``` 2. In `room.rs`, attach one `ConformanceMeter` per participant. Call `observe` on each incoming media packet. 3. **Observe-only mode for now.** Log violations to `tracing::warn!` and bump a Prometheus counter. Do not close session. ### Verify ```bash cargo test -p wzp-relay conformance ``` ### Done when Unit test: synthetic 1 MB/s declared as Opus 24k logs `Violation::BitrateExceeded`. --- ## T2.5 — Tier B (packet-rate) + Tier C (timestamp drift) - **PRD:** `PRD-relay-conformance.md` - **Effort:** 2 h - **Files:** - `crates/wzp-relay/src/conformance.rs` ### Steps 1. Add packet-rate enforcement: `packets_in_window > max_pps(codec) * 1.5` over a 1 s window → `PacketRateExceeded`. 2. `max_pps(codec) = 1000 / codec.frame_duration_ms() * 3` (×3 for FEC). 3. Timestamp drift: track `Δtimestamp / Δseq` over rolling 200-packet window. If outside `frame_duration_ms × [0.5, 2.0]`, log `TimestampDrift`. ### Verify ```bash cargo test -p wzp-relay conformance ``` ### Done when Both new tests pass alongside Tier A test. --- ## T2.6 — Prometheus metrics for conformance - **PRD:** `PRD-relay-conformance.md` - **Effort:** 2 h - **Files:** - `crates/wzp-relay/src/metrics.rs` ### Steps 1. Add counters / histograms: ```rust wzp_relay_conformance_violations_total{tier, codec_id, media_type, verdict} wzp_relay_conformance_bytes_per_session{media_type} histogram wzp_relay_conformance_iat_ms{media_type} histogram ``` 2. Wire `ConformanceMeter` to bump these on `observe`. ### Verify ```bash curl localhost:9090/metrics | grep wzp_relay_conformance ``` (after `cargo run -p wzp-relay -- --listen 127.0.0.1:4433 --no-auth` with a synthetic client) ### Done when Counters increment under abusive traffic; quiet on legitimate audio. --- # Wave 3 — Protocol hardening (target: 3-4 days) --- ## T3.1 — Confirm `RoomManager` concurrency (W13) - **PRD:** `PRD-protocol-hardening.md` - **Effort:** 2 h - **Files:** - `crates/wzp-relay/src/room.rs` ### Context `RoomManager` already uses `DashMap` (verified at line 352). The audit (W13) was based on the older ARCHITECTURE doc which mentioned a single Mutex. The actual remaining contention point is whatever's *inside* `Room` — confirm. ### Steps 1. Read the `Room` struct definition. 2. If `Room` itself uses fine-grained locks or is `Arc>` already, document this in `PROTOCOL-AUDIT.md` and mark W13 resolved. 3. If `Room` has a single per-room `Mutex` held during fan-out, identify the hot path and either: - Split fan-out list into `RwLock>` (read-mostly). - Use `ArcSwap>` for lock-free reads. 4. Run the 40+4 relay integration tests. ### Verify ```bash cargo test -p wzp-relay cargo test -p wzp-relay --test federation cargo test -p wzp-relay --test handshake_integration ``` ### Done when Tests pass + a one-line update in `PROTOCOL-AUDIT.md` noting actual state. --- ## T3.2 — Document `timestamp_ms` rebase across rekey (W3) - **PRD:** `PRD-protocol-hardening.md` - **Effort:** 1 h - **Files:** - `crates/wzp-proto/src/packet.rs` (doc comment on `MediaHeader::timestamp`) - `crates/wzp-crypto/src/rekey.rs` (add comment) - `docs/WZP-SPEC.md` - Add test in `crates/wzp-client/tests/long_session.rs` ### Steps 1. Decision (already made): `timestamp_ms` is monotonic across rekeys. Document inline: ```rust /// Milliseconds since session start. Monotonic for the full session lifetime; /// NOT reset by rekey (rekey changes only key material, not framing state). pub timestamp: u32, ``` 2. In `rekey.rs`, add a comment near the rekey handler confirming sequence + timestamp are untouched. 3. Add a test that performs 2 rekeys mid-session and asserts `timestamp` continues monotonically. ### Verify ```bash cargo test -p wzp-client --test long_session rekey_timestamp_monotonic ``` ### Done when Test passes. --- ## T3.3 — `SignalMessage` version field (W12) - **PRD:** `PRD-protocol-hardening.md` - **Effort:** 2 h - **Files:** - `crates/wzp-proto/src/packet.rs` ### Steps 1. For each variant of `SignalMessage`, add `#[serde(default)] version: u8` as the first field, with helper `fn default_signal_version() -> u8 { 1 }`. 2. Add fallback variant for unknown future signals: ```rust #[serde(other)] Unknown, ``` (Note: bincode + serde `other` may need a wrapper — research before implementing. If not feasible, document the limitation and skip the `Unknown` arm.) 3. Decode path: on `Unknown`, log `tracing::warn!("unknown signal variant")` and **do not** close session. ### Verify ```bash cargo test -p wzp-proto signal_message ``` ### Done when Existing signal tests pass. Old payloads (without `version` field) still deserialize. --- ## T3.4 — Tier D (per-codec packet size sanity) - **PRD:** `PRD-relay-conformance.md` - **Effort:** 2 h - **Files:** - `crates/wzp-relay/src/conformance.rs` ### Steps 1. Add per-codec typical / max payload table: ```rust pub fn payload_size_bound(codec: CodecId) -> usize { match codec { CodecId::Opus64k => 320, CodecId::Opus48k => 240, CodecId::Opus32k => 200, CodecId::Opus24k => 160, CodecId::Opus16k => 100, CodecId::Opus6k => 90, CodecId::Codec2_3200 => 30, CodecId::Codec2_1200 => 30, CodecId::ComfortNoise => 16, } } ``` 2. Maintain EWMA of payload size per meter. Reject if EWMA exceeds 2× typical for declared codec. ### Verify ```bash cargo test -p wzp-relay conformance_tier_d ``` ### Done when Synthetic stream of 1400-byte payloads declared as Codec2_1200 flagged within 5 s. --- ## T3.5 — Tier E (per-fingerprint token bucket) - **PRD:** `PRD-relay-conformance.md` - **Effort:** 4 h - **Files:** - `crates/wzp-relay/src/conformance.rs` (or sibling `quota.rs`) - `crates/wzp-relay/src/auth.rs` (for authed/anon split) ### Steps 1. Implement a simple token bucket per `(fingerprint, src_ip)`: ```rust pub struct TokenBucket { capacity: u64, tokens: AtomicU64, refill_per_sec: u64, last_refill: AtomicU64, } ``` 2. Wire into per-participant forward loop. Refill on each `observe`. 3. Authed/anon split: authenticated quota = 50 GB/month; anon = 1 GB/month. Per-session cap = 256 kbps audio (5 Mbps video reserved for later). 4. **Observe-only:** log + counter; do not throttle yet. ### Verify ```bash cargo test -p wzp-relay token_bucket ``` ### Done when Unit test: 100 KB at 256 kbps cap consumes no tokens; 1 MB exceeds. --- # Wave 4 — Video v1 (3 weeks) Detailed task breakdown deferred until Wave 1-3 land. Skeleton: | Task | Summary | Effort | |---|---|---| | T4.1 | `wzp-video` crate scaffold + H.264 NAL framer + depacketizer (no encoder yet) | 3 d | | T4.2 | VideoToolbox H.264 encoder + decoder (macOS) — minimum viable | 3 d | | T4.3 | MediaCodec H.264 encoder + decoder via JNI (Android) | 5 d | | T4.4 | `SignalMessage::Nack` variant + RTT-gated NACK loop | 2 d | | T4.5 | I-frame FEC ratio boost (encoder hint → FEC layer) | 1 d | | T4.6 | SFU keyframe cache per `(room, sender, stream_id)` | 2 d | | T4.7 | PLI suppression at SFU | 1 d | Each of these will be expanded into the same step-by-step format as T1.x once Wave 3 is in progress. See `PRD-video-v1.md` for design. --- # Wave 5 — Quality, codecs, simulcast (3 weeks) Detailed task breakdown deferred. Skeleton: | Task | Summary | |---|---| | T5.1 | `PriorityMode` enum + `SignalMessage::SetPriorityMode` | | T5.2 | `VideoQualityController` with per-mode allocation gates | | T5.3 | `EncoderMode::SlideFallback` for ScreenShare | | T5.4 | H.265 encoder/decoder (reuse framer from T4.1) | | T5.5 | 3-layer simulcast at sender | | T5.6 | Per-receiver layer selection at SFU | | T5.7 | Tier F audio scorer (entropy/IAT/silence-fraction) | | T5.8 | Tier G response policy (typed Hangup + audit log) | --- # Wave 6 — AV1 + Tier F video (2-3 weeks) | Task | Summary | |---|---| | T6.1 | AV1 encoder/decoder with HW probe + SVT-AV1 SW fallback | | T6.2 | Tier F video scorer (keyframe periodicity, I/P ratio, BWE responsiveness) | | T6.3 | Federated reputation gossip (optional) | --- # Working agreements - **One commit per task.** Message: `T: `. - **Update PRD on deviation.** If you implement something differently than the PRD specifies, edit the PRD in the same commit explaining why. - **Don't merge waves out of order** — dependencies are real. - **Ask before destroying.** Any task that would delete data, drop tables, or force-push: stop and report. - **Auto-mode caveat.** Even in auto mode, if a task description doesn't fit what you find in the code, stop and surface the mismatch before guessing. --- # Status board Edit this table directly when you claim, complete, or get blocked on a task. Keep it sorted by task ID. The reviewer (human) is the only one who flips `Pending Review` → `Approved` or `Changes Requested`. Statuses (in order of progression): - `Open` — not yet picked up - `In Progress` — an agent is working on it - `Blocked` — agent has hit something it can't resolve; see report - `Pending Review` — agent has finished, report filed, awaiting human - `Changes Requested` — reviewer pushed back; back to agent - `Approved` — reviewer signed off; task is closed - `Skipped` — explicitly deferred or made redundant by another task | Task | Status | Agent | Started (UTC) | Completed (UTC) | Report | Reviewer notes | |---|---|---|---|---|---|---| | 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 | Approved | Kimi Code CLI | 2026-05-11T07:17Z | 2026-05-11T07:22Z | [report](reports/T1.1.1-report.md) | Approved after rework. Both Verify commands clean. | | T1.1.2 | Approved | Kimi Code CLI | 2026-05-11T07:19Z | 2026-05-11T07:25Z | [report](reports/T1.1.2-report.md) | Approved after rework. Broader grep clean; remaining matches are self-refs in task spec + frozen historical note. | | 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.2.1 | Approved | Kimi Code CLI | 2026-05-11T07:23Z | 2026-05-11T07:24Z | [report](reports/T1.2.1-report.md) | Approved. Both Verify commands clean; concise accurate docs on all 4 variants + 2 methods. | | 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 | Approved | Kimi Code CLI | 2026-05-11T07:12Z | 2026-05-11T07:16Z | [report](reports/T1.4-report.md) | Approved 2026-05-11. Spawned T1.4.1 (rustdoc on v2 mini types). The two-step expand test catches the W4 desync scenario nicely. | | T1.4.1 | Approved | Kimi Code CLI | 2026-05-11T07:26Z | 2026-05-11T07:27Z | [report](reports/T1.4.1-report.md) | Approved. Closes rustdoc trilogy (T1.1.1/T1.2.1/T1.4.1). | | T1.5 | Approved | Kimi Code CLI | 2026-05-11T07:28Z | 2026-05-11T10:09Z | [report](reports/T1.5-report.md) | Approved with follow-ups. Migration correct; scope creep (120 files) and workspace clippy skipped — spawned T1.5.1 (encode_compact unwrap) and T1.5.2 (clippy hygiene). | | T1.5.1 | Approved | Kimi Code CLI | 2026-05-11T10:09Z | 2026-05-11T10:15Z | [report](reports/T1.5.1-report.md) | Approved. unwrap replaced with `if let Some(base)`; fallback test passes. Cargo.lock churn is legit dep updates. | | 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.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 | 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 | Approved | Kimi Code CLI | 2026-05-11T17:00Z | 2026-05-11T17:06Z | [report](reports/T2.1-report.md) | Approved retroactively. Commit fe1f948 landed; closed by reviewer. | | T2.2 | Approved | Kimi Code CLI | 2026-05-11T17:05Z | 2026-05-11T17:16Z | [report](reports/T2.2-report.md) | Approved. Substance solid; rule #7 violated. Last lenient pass. | | T2.3 | Approved | Kimi Code CLI | 2026-05-11T17:13Z | 2026-05-11T17:20Z | [report](reports/T2.3-report.md) | Substance good (BWE guard); 4 process violations bundled with T2.4-T2.6 in single commit 54c1a35 — see T2.6 report for consolidated notes. | | T2.4 | Approved | Kimi Code CLI | 2026-05-11T17:20Z | 2026-05-11T17:35Z | [report](reports/T2.4-report.md) | Substance good (Tier A); bundled in 54c1a35 — see T2.6 report. | | T2.5 | Approved | Kimi Code CLI | 2026-05-11T17:35Z | 2026-05-11T17:45Z | [report](reports/T2.5-report.md) | Substance good (Tier B+C); bundled in 54c1a35 — see T2.6 report. | | T2.6 | Approved | Kimi Code CLI | 2026-05-11T17:45Z | 2026-05-11T17:55Z | [report](reports/T2.6-report.md) | Substance good (Prom metrics); bundled in 54c1a35. Consolidated reviewer notes here. | | T3.1 | Approved | Kimi Code CLI | 2026-05-11T20:55Z | 2026-05-11T21:05Z | [report](reports/T3.1-report.md) | Approved. DashMap>>; W13 resolved. One commit per task this time — good. Two minor process notes in report. | | T3.2 | Committed | Kimi Code CLI | 2026-05-11T21:15Z | 2026-05-11T21:25Z | [report](reports/T3.2-report.md) | timestamp_ms monotonic across rekey; doc + test. | | T3.3 | Pending Review | Kimi Code CLI | 2026-05-11T16:29Z | 2026-05-11T16:29Z | [report](reports/T3.3-report.md) | — | | T3.4 | Open | — | — | — | — | — | | T3.5 | Open | — | — | — | — | — | | T4.1 | Open | — | — | — | — | Skeleton — expand before claiming | | T4.2 | Open | — | — | — | — | Skeleton — expand before claiming | | T4.3 | Open | — | — | — | — | Skeleton — expand before claiming | | T4.4 | Open | — | — | — | — | Skeleton — expand before claiming | | T4.5 | Open | — | — | — | — | Skeleton — expand before claiming | | T4.6 | Open | — | — | — | — | Skeleton — expand before claiming | | T4.7 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.1 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.2 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.3 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.4 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.5 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.6 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.7 | Open | — | — | — | — | Skeleton — expand before claiming | | T5.8 | Open | — | — | — | — | Skeleton — expand before claiming | | T6.1 | Open | — | — | — | — | Skeleton — expand before claiming | | T6.2 | Open | — | — | — | — | Skeleton — expand before claiming | | T6.3 | Open | — | — | — | — | Skeleton — expand before claiming | ## Review queue (human) Items currently waiting on the reviewer: - 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 - T2.2 — `BandwidthEstimator` in `wzp-proto::bandwidth` — report: reports/T2.2-report.md - T3.3 — SignalMessage version field — report: reports/T3.3-report.md Once a task moves to `Pending Review`, add a line here so the reviewer sees it: `- T — report: reports/T-report.md`. The reviewer removes the line when they mark it `Approved` (or moves it back to the agent on `Changes Requested`).