## Verdict: **APPROVE**
Fresh different-vendor code review of the frozen candidate (`/tmp/radio-export-pairs.FbuGlp/SOURCE-FREEZE.json`: `blobs.rs` → `7cd0b1bc`, `export_pairs.rs` → `7c0193ee`, `support.rs` → `ef368971`). The patch's index lines (`49e44cda..7cd0b1bc`, `00000000..7c0193ee`, `00000000..ef368971`) and added-line counts (+344 / +184) agree with the freeze record and with the pasted file bodies, so the reviewed bytes are the frozen bytes. No blocking defect found.
---
## Production correctness
**`encode_parent_pair` (`iroh-blobs/src/api/blobs.rs`, module-level private fn above `impl ExportBaoProgress`)**
```rust
let mut data = [0; 2 * hemera::OUTPUT_BYTES];
let (left, right) = data.split_at_mut(hemera::OUTPUT_BYTES);
left.copy_from_slice(pair.0.as_bytes());
right.copy_from_slice(pair.1.as_bytes());
Correct and width-derived, not width-hardcoded. The array type is fixed by the return annotation; both halves are exactly OUTPUT_BYTES, so each copy_from_slice is length-matched by construction — the exact failure mode the baseline exhibited. Order is left || right, matching the consumer: cyber-bao/src/io/slice.rs::decode_slice reads left_bytes = &slice[cursor..cursor+hash_size], right_bytes = &slice[cursor+hash_size..cursor+pair_size] with pair_size = backend.hash_size()*2, and cyber-bao/src/tree.rs sizes the outboard with PAIR_SIZE = OUTPUT_BYTES*2. The exporter is now consistent with the decoder rather than with a 128-byte phantom layout.
Three integrations. All three parent arms are reduced to one call, and nothing else in the arms changed:
write:target.write(&data)—&[u8; 64]coerces to&[u8]; byte count on the wire is 64.write_with_progress:writer.send(&data).await?;thenprogress.log_other_write(data.len()). Credit is 64, is derived from the buffer rather than a literal, and is reached only after a successful send — so a failed or partially-accepted parent write credits nothing. This is the precise 128→64 accounting defect, fixed in the right order.into_byte_stream:Bytes::copy_from_slice(&data)yields a 64-byte chunk; the previousvec![0u8;128].into()padding is gone.
EncodedItem::Size still emits size.to_le_bytes() (LE64) and credits 8; leaf arms, Done/Error arms, and the ?-based error mapping (io::Error → ExportBaoError::ExportBaoIo, EncodeError → ExportBaoInner, ProgressError → ClientError, per api.rs) are untouched. data_to_bytes/data_to_vec still ignore parents. Scope is exactly: one private fn, three call sites, one #[cfg(test)] mod export_pairs; declaration. No public API, hash domain, root, storage, manifest or lock change — consistent with the semver run reporting 202 pass / 58 skip, "no semver update required".
Regression risk from the change itself is structurally bounded: the baseline arms panicked unconditionally on any Parent (confirmed by logs/red-debug.stdout at blobs.rs:1122, :1154, :1181), so no previously-green test could have depended on the 128-byte width. This fix can only unbreak parent-bearing paths — which materially reduces the exposure created by the unfinished broad unit suites.
Independent oracle: geometry and root
support.rs::geometry() pins CHUNK_SIZE == 4096 and IROH_BLOCK_SIZE.bytes() == 65536 with distinct messages, then derives C = 16. Checked against source: tree.rs gives CHUNK_SIZE: usize = 4096, from_chunk_log(log) -> Self(log), bytes() -> CHUNK_SIZE << self.0, and store/mod.rs selects from_chunk_log(4) ⇒ 65536. So body 2*B = 131072 = 32 chunks = 2 blocks, query ChunkNum(16)..ChunkNum(32) = exactly the right block ⇒ one Parent plus the right Leaf, and 8 + 2*32 + 65536 = 65608 with overhead 8 + 64 = 72. Every number in the tests follows from the pinned constants.
right_fixture() is genuinely independent of radio's traversal: 32 chunk_cv(bytes, i, false) with absolute counters, each 16-leaf half reduced by four explicit parent_cv(..., false) levels in half_root, then root = parent_cv(left, right, true), and the wire assembled by hand as LE64 || left || right || data[B..]. Two independent facts are then checked at once: (a) the store accepts that root at all — it must match CompleteStorage::create's PreOrderMemOutboard root or export_bao would take the None arm in readonly_mem.rs and fail with UnexpectedEof; (b) all three exporter methods reproduce the hand-assembled bytes. The decode_slice cross-check is sound for this shape: pre_order_chunks_filtered emits Parent { is_root: true, left: false, right: true } for the root, pushes only right_hash, and hash_block_for_verify reduces 16 chunk CVs (counters 16..32, is_single_chunk == false) through the same 16→8→4→2→1 parent_hash(..., false) ladder as half_root — so the expected [(65536, data[65536..])] is right, not an accidental agreement.
The parent-free control is also correct: 4096 bytes ⇒ blocks() == 1, outboard_size() == 0, single root leaf, chunk_cv(data, 0, true) as root, wire LE64(4096) || data, overhead 8, no Parent. Together with readonly_store_missing and geometry_pins these are the three baseline passes (3 passed; 8 failed), which is exactly the control/trigger split a red-first unit should show.
Error and progress assertions
Each assertion traces to actual source:
writer_failure_accounting—send_transfer_startedis awaited beforewriter.sendin the Size arm, so asserting a non-emptystartswithoverhead == 0for the header-failure case is correct, not a laxity. Overheads 0 / 8 / 72 for header / parent / leaf failure match credit placement precisely, andpayloads.is_empty()holds becausenotify_payload_writefollowssend_bytes.partial_parent_failure—8 + 17 = 25bytes retained,calls == 2, completed-write credit stays 8, no rollback claimed. Matches the trait contracts (partial writes permitted, no rollback promised).encoded_error_propagates— 72 bytes written thenExportBaoInner { source: EncodeError::Io }; byte stream yields the 8- and 64-byte prefixes, thenapi::Error::IopreservingInvalidData/"encoded sentinel"viaimpl From<EncodeError> for Error'sEncodeError::Io(cause) => Self::Io(cause)arm, then terminates. Exactly the documented mapping.readonly_store_missing— the store'sNonearm sends theErroritem beforeSize, sostarts.is_empty(),overhead == 0,bytes.is_empty()andUnexpectedEof/"export task ended unexpectedly"across all three methods is the correct expectation, and the byte-stream variant correctly retains kind and detail.payload_progress_failure—ProgressError::Permission {}andFrom<AbortReason>are as supplied inprovider/events.rs;ClientResult = Result<(), ProgressError>converts throughExportBaoError::ClientError. Leaf on the wire,calls == 3, overhead 72, one callback attempt, second scripted leaf unreached — all match the?return point.
Harness hygiene checks out: Sink implements both AsyncStreamWriter and util::SendStream, unused methods panic! loudly rather than silently succeeding; scripted() uses capacity ≥ 3 and closes the sender; stores are retained and shutdown().await is honored; no network fixture; clippy::unused_async cannot fire because every await-free async fn is a trait-impl item (the lint skips trait impls), consistent with the -Dwarnings runs passing.
Optional nits — do not expand scope
- Receipt labeling of the API smoke run.
logs/api-smoke.jsonran--baseline-root /tmp/.../parent/radio/iroh-blobs, i.e. the base commit tree, not the plan's<tag>/radio/iroh-blobs; and its stderr showsUpdating crates.io index/Locking 451 packages, i.e. a tool-generated scratch resolution, whilelogs/tag-metadatalegitimately refused (cannot update the lock file ... because --locked was passed). Your summary already disclaims this; make sure the audit prose says base-vs-candidate, unlocked scratch resolution and never "tag conformance". - Evidence gaps in this bundle. No
cargo clippy --all-targetsorrustup run 1.89.0 cargo checklog was supplied here. Include them inaudit/or mark them unexecuted/baseline-red explicitly; do not imply a green owning-gate set. - Symmetry.
progress.log_other_write(8)in the Size arm could readsize.to_le_bytes().len()to match the newdata.len()style. Cosmetic only. - Stale neighbors to keep named as open, not fixed here.
store/mod.rs's/// Block size used by iroh, 2^4*1024 = 16KiBis off by 4× (actual 65536);api.rs'sExportBaoError::MpscRecvcarries a garbled#[error("mpsc recv e api.acp.pro-channelsrror")]string; and per research the mutable-import pair stride (store/mem.rs), partial-storage checksum widths andprint_outboardretain 64-byte-era assumptions. This patch makes the exporter wire correct; it does not make the crate width-correct, and the receipt should say so. - Fixture side effect.
readonly_store_right_block'sRADIO_EXPORT_PAIRS_FIXTURE_DIRbranch writes files withunwrap(). Inert by default and useful for the audit artifact; acceptable as written. half_roothardcodesassert_eq!(leaves.len(), 16)whilegeometry()derives C. Guarded bygeometry_pinsfiring first with a named message, as planned.
Root owns the commit, PR, merge and ring/rung adjudication. No network-integration, rung, or complete-conformance claim is implied by this approval.