fix: resolve all code quality issues from #108 review#146
Merged
Conversation
…dex (#121) Replace the `Vec<MessageId>` channel index (which called `sort_by` on every insert, giving O(N log N) per insert / O(N² log N) overall) with `BTreeMap<HlcTimestamp, Vec<MessageId>>`. BTreeMap keeps entries sorted by key, so insert becomes O(log N) and `list_channel` iterates in order with no sorting step. `Vec<MessageId>` as the value type gracefully handles the rare case of two messages with identical HLC timestamps. Add two new tests: - `messages_returned_in_hlc_order_after_random_insertion`: inserts five messages with explicitly scrambled HLC timestamps and asserts the exact ascending order returned by `list_channel`. - `duplicate_hlc_timestamps_handled_gracefully`: inserts two messages with the same HLC timestamp and asserts both are returned. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The placeholder implementation always returned a never-yielding stream and was never wired into the client. Remove ConnectionEvent, ConnectionEventStream, and the connection_events() trait method from all four files (traits, iroh, mem, lib re-exports) and replace with a TODO(#119) comment in the trait so the intent is preserved for the real implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…123) Issue #122: Add `message_index: BTreeMap<EventHash, usize>` to `ServerState` that maps each message hash to its position in `state.messages`. Populate the index on every `Message` event insertion and rebuild it after `DeleteChannel` (which shifts indexes via `retain`). Replace the three O(N) linear scans in `EditMessage`, `DeleteMessage`, and `Reaction` handlers with O(1) `message_index.get()` lookups. Issue #123: Capture the return value of `evict_to()` in `buffer_for_prev` and emit `tracing::warn!` when events are dropped from the pending buffer, including the evicted count and current buffer size. Add `tracing` dependency to willow-state. Add tests covering: - `message_index` is populated on insert - reaction on a 1000-message state finds the right message - index is correctly rebuilt after `DeleteChannel` - `PendingBuffer::with_capacity` never exceeds the cap after any insertion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes #120. `derive_message_key` previously replayed the ratchet from counter=1 on every decryption call. `RatchetCache` caches derived keys in a BTreeMap<(epoch, counter), ChannelKey> and saves ratchet state at the highest counter per epoch so subsequent calls resume from there rather than rewinding. Cache is bounded to max_entries with eviction of the oldest (lowest-counter) entry. `evict_epoch` clears state on key rotation. 9 new tests cover correctness, epoch isolation, eviction, size bound, and the O(1) cache-hit performance property. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… map (#141) Issue #130: add `ActorTimeout` variant to `ClientError`, a `with_timeout` helper in `util.rs` (5 s on native, no-op on WASM), and wrap the hot-path `build_event`, `resolve_channel_id`, `apply_event` calls in mutations.rs plus `event_messages`, `peers`, `is_connected`, `has_permission`, and `is_admin` in accessors.rs so a hung actor surfaces a clear error instead of blocking indefinitely. Issue #141: add `reconcile_topic_map` to lib.rs that skips unparseable channel-ID strings with a `tracing::warn` rather than silently substituting a random UUID (which would split-brain the client from the network). Regression test verifies malformed IDs are dropped and valid ones survive. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap all worker gossip messages in a WireMessage::Worker(WorkerWireMessage)
signed envelope, matching the existing pattern used for server ops.
- Add Worker(WorkerWireMessage) variant to WireMessage in willow-common
- NetworkActor, HeartbeatActor, SyncActor: accept Identity, sign outbound
messages via pack_wire, verify inbound via unpack_wire
- parse_worker_message: replace unsigned bincode::unpack with unpack_wire;
unsigned bytes are now rejected as DeserializeError
- Client listeners: add Worker(_) => {} arm to the exhaustive WireMessage match
- Integration tests: update to use signed messages and unpack_wire decoding
- Add unsigned_bytes_rejected and non_worker_wire_message_ignored tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…#129) Scope: voice.rs critical paths, file_share.rs upload/download chain, and call_page.rs window access. Context .unwrap()s (use_context) are left for a follow-up as they represent programmer error, not runtime browser failure. - voice.rs: start_polling — early return if window or set_interval fails - voice.rs: ontrack handler — early return if MediaStream::new fails - voice.rs: get_or_create_connection — .unwrap() → .expect("key just inserted") - file_share.rs: FileReader::new and result chain — log errors and return early - file_share.rs: FileCard download — replace unwrap chain with let Ok/let Some guards - call_page.rs: camera and screen-share click handlers — early return on missing window - actor/runtime.rs: remove identity map_err (pre-existing clippy warning) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all `let _ = expr` patterns on must-use types (Results, Promises) with `.ok()` or `drop(...)`, and add the lint as `deny` in workspace lints. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…19 signing The comment previously stated messages were unsigned; they are now signed via pack_wire/unpack_wire as implemented in #117. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- voice.rs:531: replace .unwrap() after HashMap insert with .expect() - client/util.rs: silence unused-variable warning for label on WASM build Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on timeout - state: change message_index from BTreeMap to HashMap<EventHash, usize> for true O(1) lookup; document deserialization invariant - crypto: guard derive_or_cached against target_counter=0 (latent infinite loop sentinel); move ratchet save to final loop step only (avoids per-step clone overhead) - client: add tracing::warn when actor call times out for observability Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- common/wire.rs: Worker round-trip pack/unpack test - crypto: counter=0 sentinel and backwards-counter tests for RatchetCache - state/tests: DeleteMessage-via-index test for #122 - client/util.rs: with_timeout fires on stall (native only) - client/lib.rs: document reconcile_topic_map as pre-wired utility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This was referenced Apr 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves all open child issues from the #108 code quality review. Eight feature branches were developed in parallel in isolated git worktrees, merged into this integration branch, and verified by 7 audit agents covering security, test coverage, performance/design, and full-codebase review.
CI Status — all green
Changes by issue
WorkerWireMessagewith Ed25519 — all worker gossip messages now wrapped inWireMessage::Workerand signed viapack_wire/unpack_wire; unsigned/tampered messages rejectedconnection_events()stub fromNetworktrait and both implementationsRatchetCache— hybrid BTreeMap + saved-ratchet-state cache that eliminates O(counter) replay on repeated key derivation; bounded bymax_entriesInMemoryStorewithBTreeMap<HlcTimestamp, Vec<MessageId>>— O(log N) insert, naturally sorted iterationHashMap<EventHash, usize>index toServerStatefor O(1)EditMessage/DeleteMessage/Reactionlookupsevict_toreturn value inPendingBufferand emittracing::warn!when events are dropped.unwrap()calls invoice.rs,file_share.rs,call_page.rswith gracefullet Some/let Okearly-returnswith_timeout(label, future)utility and wrap hot-path actor calls inmutations.rs/accessors.rswith 5-second timeout +tracing::warnon fireclippy::let_underscore_must_use = "deny"workspace-wide; fix all violations across the workspacereconcile_topic_mapto skip malformed channel IDs with a warning instead of generating random UUIDs (which would split-brain the client from the network)crates/appno longer exists in the workspaceFixes discovered during audit and applied
voice.rs:531— stray.unwrap()onHashMap::get()after insertion (→.expect("key just inserted"))worker_types.rssecurity doc comment updated to reflect Ed25519 signingmessage_indextype corrected fromBTreeMaptoHashMapfor true O(1) lookupderive_or_cached— addedtarget_counter=0guard preventing latent infinite loop on "no ratchet" sentinelwith_timeout— moved per-step ratchet clone to final loop step only (avoids redundant clones)client/util.rsWASM build — suppress unusedlabelvariable warningSecurity findings filed as follow-up issues
WorkerAnnouncement.peer_idnot verified against Ed25519 signer (no live impact today;WireMessage::Workerarm is a no-op in client listeners)UserProfilebroadcasts are unsigned — display names can be spoofed (pre-existing)Test coverage added
New tests across 5 crates covering:
WireMessage::Workerround-trip pack/unpack and tampered-signature rejectionRatchetCachecounter=0 sentinel, backwards-counter request, epoch isolation, eviction, and size boundDeleteMessageviamessage_index(O(1) path)with_timeoutfires on stall (native)reconcile_topic_mapdrops malformed IDs and preserves valid onesAll 33 test suites pass with zero failures.
Test plan
cargo test --workspace --exclude willow-web— 0 failures across 33 suitescargo clippy --workspace --exclude willow-web -- -D warnings— 0 warningscargo check --target wasm32-unknown-unknown -p willow-web— cleancargo fmt --check— cleanCloses #108, #117, #119, #120, #121, #122, #123, #128, #129, #130, #131, #141