Skip to content

feat(core): add typed USync query engine - #1063

Merged
jlucaso1 merged 8 commits into
mainfrom
agent/typed-usync-core
Jul 21, 2026
Merged

feat(core): add typed USync query engine#1063
jlucaso1 merged 8 commits into
mainfrom
agent/typed-usync-core

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a validated, typed USync request/response model for every protocol observed in the captured client
  • generate USync session IDs inside the client and preserve protocol errors, refresh hints, optional status timestamps, device key-index metadata, and hosted addressing
  • make existing specialized specs and legacy helpers reuse the canonical builder/parser instead of maintaining parallel wire logic
  • expose the existing USync model directly through a format-agnostic Serde contract; deserialized queries always pass through the canonical validator and responses serialize without projection trees or cloned collections
  • derive adjacent tagged protocol serialization and parser dispatch from the same explicit wire tags while preserving the existing Rust enum shapes
  • serialize binary fields by reference and deserialize them into their final buffers, preserving absent, empty, and non-empty values without intermediate encodings
  • deserialize known wire enums from borrowed strings without heap allocation, while retaining owned unknown values for forward-compatible enums
  • canonicalize digit-only phone inputs and reject malformed or out-of-range international numbers before building a request
  • persist hosted device metadata with backward-compatible deserialization, migrate both regular and hosted Signal namespaces, and reject unrepresentable persisted device IDs without truncation

Protocol evidence

The request builders and response parsers were checked against the captured USync modules under docs/captured-js/WAWeb/Usync/, including the distinct t and ts attributes, bot profile versioning, LID attributes, sparse results, per-protocol errors, strict picture/device parsing, and hosted device markers. Hosted Signal addressing follows the captured WAWeb/Signal/Address.js namespace mapping.

Compatibility

Persisted DeviceInfo values that predate is_hosted deserialize as regular devices. Constructors were added for DeviceInfo and UsyncDevice so callers do not need to repeat defaults. The public UsyncProtocol::Features(Vec<_>) shape and adjacent type/data representation are preserved while their discriminators now share the protocol wire-tag source of truth.

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all --tests -- -D warnings
  • 46 focused USync tests
  • 12 wire-enum Serde contract tests
  • 53 device-registry tests
  • 46 LID/PN migration tests
  • complete core library suite: 1,178 passed, 1 intentionally ignored
  • workspace unit and integration suites excluding E2E (all executed tests passed)

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds typed USync query and response models, persists hosted-device metadata, and propagates hosting-aware addressing through device parsing, client processing, registry updates, session cleanup, and JID reconstruction. Tests migrate to constructor-based device fixtures.

Changes

USync and hosted device support

Layer / File(s) Summary
Hosting metadata and JID addressing
wacore/src/store/traits.rs, wacore/binary/src/jid.rs, wacore/src/usync.rs
Device records and parsed USync devices gain hosting flags and constructors; JIDs switch between standard and hosted PN/LID servers.
Typed USync wire model and projections
wacore/src/iq/usync/query.rs, wacore/src/iq/usync.rs
Typed query validation, response parsing, protocol projections, device-list parsing, delta mode, VoIP context, and subprotocol error handling replace manual XML parsing.
USync client and registry integration
src/usync.rs, src/client/device_registry.rs, src/handlers/notification/device.rs
USync execution, device ingestion, registry reconstruction, session deletion, and sender-key cleanup preserve hosted namespace metadata.
DeviceInfo fixture migration
src/**, storages/sqlite-storage/src/sqlite_store.rs, wacore/src/adv.rs
Tests and seeded records use DeviceInfo::new and related constructors.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant TypedUsync
  participant DeviceRegistry
  participant Jid
  Client->>TypedUsync: Build and execute UsyncQuerySpec
  TypedUsync-->>Client: Return parsed device-list response
  Client->>DeviceRegistry: Store DeviceInfo with is_hosted
  DeviceRegistry->>Jid: Reconstruct device JIDs with hosting
  Jid-->>Client: Return standard or hosted JIDs
Loading

Possibly related PRs

Suggested labels: api-design, breaking-change, size-increase-ok

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: introducing a typed USync query engine.
Description check ✅ Passed The description is directly about the typed USync model and related device/session changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/typed-usync-core

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a typed USync query engine and extends device handling across regular and hosted addresses. The main changes are:

  • Validated request and response models for observed USync protocols.
  • Canonical builders and parsers shared by specialized APIs.
  • Serde support for typed queries, responses, binary fields, and wire enums.
  • Hosted-device persistence, Signal namespace migration, and safe device-ID conversion.

Confidence Score: 4/5

The latest changes do not add another distinct blocking issue.

  • The typed parser and legacy projection continue to share the known malformed-device failure path.
  • That behavior is already covered by the existing review comment, so no duplicate comment is included.
  • No separate failure requiring an additional fix was identified.

wacore/src/iq/usync/query.rs

Important Files Changed

Filename Overview
wacore/src/iq/usync/query.rs Adds typed USync validation, wire models, protocol parsing, and Serde support.
wacore/src/iq/usync.rs Routes specialized USync operations through the canonical typed builder and parser.
src/client/device_registry.rs Persists hosted-device metadata and expands Signal cleanup across all address namespaces.
src/client/lid_pn.rs Migrates regular and hosted Signal sessions when PN-to-LID mappings are discovered.
wacore/derive/src/lib.rs Extends generated wire-enum support for adjacent tagged representations.

Reviews (8): Last reviewed commit: "fix(core): harden typed USync contracts" | Re-trigger Greptile

Comment thread wacore/src/iq/usync/query.rs Outdated
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 9.62 MiB 9.66 MiB +37.47 KiB (+0.38%) 🔺
bin .text 7.69 MiB 7.72 MiB +31.31 KiB (+0.40%) 🔺
bin allocated (text+data+bss) 9.62 MiB 9.66 MiB +40.97 KiB (+0.42%) 🔺
llvm-lines wacore 478,645 490,781 +12,136 (+2.54%) ⚠️
llvm-lines wacore copies 15,826 16,226 +400 (+2.53%) ⚠️
llvm-lines whatsapp-rust lib 674,878 675,939 +1,061 (+0.16%) 🔺
llvm-lines whatsapp-rust lib copies 21,719 21,762 +43 (+0.20%) 🔺
deps crates (Cargo.lock) 470 470 0
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.69 MiB 1.68 MiB -9.89 KiB (-0.57%) 🔽
.text wacore 596.35 KiB 635.75 KiB +39.40 KiB (+6.61%) ⚠️
.text wacore_binary 87.04 KiB 87.02 KiB -16 B (-0.02%) 🔽
.text wacore_libsignal 160.96 KiB 160.96 KiB 0
.text wacore_appstate 22.36 KiB 22.36 KiB 0
.text wacore_noise 22.98 KiB 22.98 KiB 0
.text waproto 1.74 MiB 1.74 MiB 0
.text whatsapp_rust_sqlite_storage 510.12 KiB 510.58 KiB +477 B (+0.09%) 🔺
.text whatsapp_rust_tokio_transport 39.84 KiB 39.84 KiB 0
.text whatsapp_rust_ureq_http_client 10.28 KiB 10.28 KiB 0
.text std 949.82 KiB 950.91 KiB +1.08 KiB (+0.11%) 🔺
.text other deps 1.88 MiB 1.88 MiB +56 B (+0.00%) 🔺
Top movers (cargo-bloat attribution)
Crate main PR Δ
wacore 596.35 KiB 635.75 KiB +39.40 KiB (+6.61%)
whatsapp_rust 1.69 MiB 1.68 MiB -9.89 KiB (-0.57%)
std 949.82 KiB 950.91 KiB +1.08 KiB (+0.11%)

Baseline: 87fbcb555 (latest main run) · Head: 818233795 · Graphs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/device_registry.rs`:
- Around line 639-641: Update the primary-device insertion in the device-record
reconstruction flow to derive the hosted status from the incoming
device.jid.server and pass that value to DeviceInfo::new instead of always using
None. Ensure reconstruct_device_jids preserves hosted accounts while retaining
the existing primary-device handling for non-hosted devices.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8679c340-2935-4cd4-9030-8ceb2fb20287

📥 Commits

Reviewing files that changed from the base of the PR and between 87fbcb5 and 3015133.

📒 Files selected for processing (14)
  • src/client/device_registry.rs
  • src/client/lid_pn.rs
  • src/features/signal.rs
  • src/handlers/notification/device.rs
  • src/handlers/notification/mod.rs
  • src/send/mod.rs
  • src/usync.rs
  • storages/sqlite-storage/src/sqlite_store.rs
  • wacore/binary/src/jid.rs
  • wacore/src/adv.rs
  • wacore/src/iq/usync.rs
  • wacore/src/iq/usync/query.rs
  • wacore/src/store/traits.rs
  • wacore/src/usync.rs

Comment thread src/client/device_registry.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3015133d01

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/client/device_registry.rs Outdated
Comment thread wacore/src/iq/usync/query.rs
Comment thread wacore/src/iq/usync/query.rs
Comment thread wacore/src/iq/usync/query.rs Outdated
greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 21, 2026
@greptile-apps
greptile-apps Bot dismissed their stale review July 21, 2026 16:50

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/client/device_registry.rs (2)

814-841: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Sender-key row cleanup didn't get the hosted-namespace memo.

We just widened delete_sessions_for_devices to sweep Server::Hosted/Server::HostedLid via the new signal_namespaces(), but delete_sender_key_rows_for_device right next to it still hardcodes [Server::Lid, Server::Pn]. If a companion device's JID was rendered under @hosted/@hosted.lid (which the new hosting bit makes possible), its sender_key_devices row will never match these two candidate servers, so patch_device_remove leaves a stale row behind. That's exactly the kind of half-cleaned-up state this PR is supposed to be eliminating for hosted accounts — let's not leave a gap right next to the fix.

🛠️ Proposed fix: reuse the same namespace set used for session cleanup
         let lookup = self.resolve_lookup_keys(user).await;
-        let servers = [wacore_binary::Server::Lid, wacore_binary::Server::Pn];
-        let mut candidates: Vec<String> = Vec::with_capacity(4);
-        for server in servers {
-            for key in lookup.all_keys() {
-                let mut jid = Jid::new(key, server);
-                jid.device = device_id;
-                candidates.push(jid.to_string());
-            }
+        let mut candidates: Vec<String> = Vec::with_capacity(4);
+        for (key, server) in lookup.signal_namespaces() {
+            let mut jid = Jid::new(key, server);
+            jid.device = device_id;
+            candidates.push(jid.to_string());
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/device_registry.rs` around lines 814 - 841, Update
delete_sender_key_rows_for_device to use the shared signal_namespaces() server
set, including hosted and hosted-lid namespaces, instead of the hardcoded Lid/Pn
array; keep candidate construction, persistence deletion, and cache invalidation
unchanged.

605-662: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Primary device's hosted flag still gets wiped on a raw_id mismatch — need to fix this for real.

Look, I appreciate the comment explaining why we can't classify the primary from device.jid.server — that's the right call, and the new hosted_companion_does_not_reclassify_primary_device test proves it works for the normal companion-add path. But there's a second way this breaks: when record.raw_id mismatches and record.devices.clear() fires, we throw away the existing device-0 entry — including whatever is_hosted it had — and then unconditionally re-add it at Line 661 with DeviceInfo::new(0, None) (hosted defaults to false). For an actually-hosted primary going through an identity-change event, that flips it back to non-hosted addressing until the next full device sync heals it. Not good enough — capture the prior bit before the clear and carry it through.

🛠️ Proposed fix to preserve the primary's hosted bit across a raw_id-mismatch clear
+        let primary_was_hosted = record
+            .devices
+            .iter()
+            .any(|d| d.device_id == 0 && d.is_hosted);
+
         if let Some(bytes) = signed_bytes {
             if let Some(decoded) = wacore::adv::decode_key_index_list(bytes) {
                 if let Some(stored_raw_id) = record.raw_id
                     && stored_raw_id != decoded.raw_id
                 {
                     ...
                     record.devices.clear();
                 }
                 ...
             }
             ...
         }
         ...
         if !record.devices.iter().any(|d| d.device_id == 0) {
             record
                 .devices
-                .push(wacore::store::traits::DeviceInfo::new(0, None));
+                .push(wacore::store::traits::DeviceInfo::new(0, None).with_hosting(primary_was_hosted));
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/device_registry.rs` around lines 605 - 662, Preserve the primary
device’s existing hosted flag when the raw_id mismatch branch in the
device-update flow clears record.devices. Capture whether device 0 was hosted
before record.devices.clear(), carry that value through the rebuild, and use it
when re-adding device 0 in the final record.devices fallback instead of always
constructing it with the default hosted=false; retain the current false default
when no prior primary entry exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/client/device_registry.rs`:
- Around line 814-841: Update delete_sender_key_rows_for_device to use the
shared signal_namespaces() server set, including hosted and hosted-lid
namespaces, instead of the hardcoded Lid/Pn array; keep candidate construction,
persistence deletion, and cache invalidation unchanged.
- Around line 605-662: Preserve the primary device’s existing hosted flag when
the raw_id mismatch branch in the device-update flow clears record.devices.
Capture whether device 0 was hosted before record.devices.clear(), carry that
value through the rebuild, and use it when re-adding device 0 in the final
record.devices fallback instead of always constructing it with the default
hosted=false; retain the current false default when no prior primary entry
exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cdbc02ac-4325-487b-acf7-000348daa11b

📥 Commits

Reviewing files that changed from the base of the PR and between 3015133 and f9cff9b.

📒 Files selected for processing (3)
  • src/client/device_registry.rs
  • wacore/src/iq/usync.rs
  • wacore/src/iq/usync/query.rs

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c88d25160

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wacore/src/iq/usync/query.rs
Comment thread src/client/device_registry.rs
Comment thread wacore/src/iq/usync/query.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wacore/src/iq/usync.rs (1)

120-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clean up the error message formatting.

Look, this formatting leaves a dangling colon and space when the error text is empty, giving us sloppy logs like usync status error 401: . Our systems need to be pristine and professional. Conditionally format this so we don't have trailing artifacts when the server omits the text.

✨ Proposed fix for cleaner logs
 fn usync_subprotocol_error_message(tag: &str, error: &UsyncSubprotocolError) -> String {
     let code = error
         .code
         .map(|code| code.to_string())
         .unwrap_or_else(|| "unknown".to_string());
-    let text = error.text.as_deref().unwrap_or("");
-    format!("usync {tag} error {code}: {text}")
+    if let Some(text) = error.text.as_deref().filter(|s| !s.is_empty()) {
+        format!("usync {tag} error {code}: {text}")
+    } else {
+        format!("usync {tag} error {code}")
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacore/src/iq/usync.rs` around lines 120 - 127, Update
usync_subprotocol_error_message so the formatted error includes the colon and
space only when error.text is present and non-empty; otherwise return the
message ending after the error code without trailing whitespace or punctuation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wacore/src/iq/usync/query.rs`:
- Around line 1432-1439: Update the generic helper required_u64 to remove the
attrs.finish()? call after extracting the required attribute, while preserving
the existing missing-attribute error and u64 parsing behavior so additional node
attributes are ignored.

---

Outside diff comments:
In `@wacore/src/iq/usync.rs`:
- Around line 120-127: Update usync_subprotocol_error_message so the formatted
error includes the colon and space only when error.text is present and
non-empty; otherwise return the message ending after the error code without
trailing whitespace or punctuation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 918c105b-9192-4dd7-8c2d-e0c4f4485a3d

📥 Commits

Reviewing files that changed from the base of the PR and between f9cff9b and 4c88d25.

📒 Files selected for processing (2)
  • wacore/src/iq/usync.rs
  • wacore/src/iq/usync/query.rs

Comment thread wacore/src/iq/usync/query.rs
@greptile-apps
greptile-apps Bot dismissed their stale review July 21, 2026 17:02

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@coderabbitai coderabbitai Bot added the size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning label Jul 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/client/device_registry.rs (1)

648-661: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Preserve the hosted status of the primary device.

Look, I already called this out in a previous review. I see you added a comment arguing that the companion notification shouldn't classify the primary device. But pragmatically, if we unconditionally push a neutral entry with is_hosted = false for a hosted account here, reconstruct_device_jids will forcefully downgrade its server to PN or LID, and message delivery to the primary device will completely break. We can't let philosophical purity break core functionality. If the companion is hosted, the safest and most accurate bet is that the primary is too. Revert to extracting the hosted status from the incoming device.jid.server. We need this working correctly.

🐛 Proposed fix to preserve hosted status
-        // WA Web `AdvDeviceNotificationApi.handleDeviceAddNotification` re-adds the
-        // primary (device 0) to the rebuilt list unconditionally. Preserve an
-        // existing primary and its metadata across a raw_id reset; restore a
-        // neutral entry only when the input record did not contain one.
-        //
-        // The primary's key_index is never read (`filter_devices_by_key_index` keeps
-        // device 0 regardless and `is_key_index_valid` is not applied to it), so store
-        // `None` to match how device 0 is recorded everywhere else. Hosting belongs
-        // to each device-list entry, so the companion notification cannot classify
-        // the primary.
-        if !record.devices.iter().any(|d| d.device_id == 0) {
-            record
-                .devices
-                .push(wacore::store::traits::DeviceInfo::new(0, None));
-        }
+        // WA Web `AdvDeviceNotificationApi.handleDeviceAddNotification` re-adds the
+        // primary (device 0) to the rebuilt list unconditionally. Preserve an
+        // existing primary and its metadata across a raw_id reset; restore an
+        // entry with matching hosted status when the input record did not contain one.
+        //
+        // The primary's key_index is never read (`filter_devices_by_key_index` keeps
+        // device 0 regardless and `is_key_index_valid` is not applied to it), so store
+        // `None` to match how device 0 is recorded everywhere else.
+        if !record.devices.iter().any(|d| d.device_id == 0) {
+            let primary_hosted = matches!(
+                device.jid.server,
+                wacore_binary::Server::Hosted | wacore_binary::Server::HostedLid
+            );
+            record
+                .devices
+                .push(wacore::store::traits::DeviceInfo::new(0, None).with_hosting(primary_hosted));
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/device_registry.rs` around lines 648 - 661, Update the
primary-device fallback in the device reconstruction flow to preserve hosted
status: when adding device ID 0, derive its hosted flag from the incoming
primary device JID server instead of creating an unconditionally neutral entry.
Ensure reconstruct_device_jids receives the correct hosted classification while
retaining existing primary metadata when present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/client/device_registry.rs`:
- Around line 648-661: Update the primary-device fallback in the device
reconstruction flow to preserve hosted status: when adding device ID 0, derive
its hosted flag from the incoming primary device JID server instead of creating
an unconditionally neutral entry. Ensure reconstruct_device_jids receives the
correct hosted classification while retaining existing primary metadata when
present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1028707d-3648-4914-9b21-571d3881fc87

📥 Commits

Reviewing files that changed from the base of the PR and between 4c88d25 and f55a0c2.

📒 Files selected for processing (1)
  • src/client/device_registry.rs

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 21, 2026
@greptile-apps
greptile-apps Bot dismissed their stale review July 21, 2026 17:15

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 21, 2026
@greptile-apps
greptile-apps Bot dismissed their stale review July 21, 2026 17:21

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/device_registry.rs (1)

969-988: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Silent truncation risk on reconstruct_device_jids's device_id as u16 cast.

This file already knows how to handle a device_id that doesn't fit in u16patch_device_remove explicitly does u16::try_from(device_id) with a warn! + graceful skip. Here we instead rely on debug_assert!, which is compiled out in release. If this invariant is ever violated in production, we don't get a panic or a warning — we get a silently wrong device JID reconstructed for message routing. I don't love "trust me, it's fine" logic guarding something this central to hosted addressing. Even if today's upstream writers (checked_u16 in the parser, jid.device: u16 on the wire) keep this bounded, let's match the pattern we already trust elsewhere in this exact file instead of leaving two different safety postures for the same class of data.

🛠️ Proposed fix to mirror patch_device_remove's graceful handling
     fn reconstruct_device_jids(
         query_jid: &Jid,
         record: &wacore::store::traits::DeviceListRecord,
     ) -> Vec<Jid> {
         let base = query_jid.to_non_ad();
         record
             .devices
             .iter()
-            .map(|d| {
-                debug_assert!(
-                    d.device_id <= u16::MAX as u32,
-                    "device_id {} overflows u16",
-                    d.device_id
-                );
-                base.with_device_hosting(d.device_id as u16, d.is_hosted)
-            })
+            .filter_map(|d| match u16::try_from(d.device_id) {
+                Ok(device_id) => Some(base.with_device_hosting(device_id, d.is_hosted)),
+                Err(_) => {
+                    warn!("reconstruct_device_jids: device_id {} overflows u16 — skipping", d.device_id);
+                    None
+                }
+            })
             .collect()
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/device_registry.rs` around lines 969 - 988, Update
reconstruct_device_jids to handle device IDs that exceed u16 without relying on
debug_assert! or silently truncating with as u16. Mirror patch_device_remove by
attempting u16::try_from, logging a warning, and skipping invalid devices while
preserving reconstruction for valid records.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wacore/src/iq/usync/query.rs`:
- Around line 1658-1677: Update the test phone literals used by UsyncQuery,
including agent_qualified_bot and the affected tests in the indicated range, to
use a real NPA followed by the fictional 555 exchange and a line number from
0100–0199. Update the corresponding expected InvalidUserJid values consistently
while preserving each test’s validation behavior.

---

Outside diff comments:
In `@src/client/device_registry.rs`:
- Around line 969-988: Update reconstruct_device_jids to handle device IDs that
exceed u16 without relying on debug_assert! or silently truncating with as u16.
Mirror patch_device_remove by attempting u16::try_from, logging a warning, and
skipping invalid devices while preserving reconstruction for valid records.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6052f613-7517-42e2-afaf-b45f6888b1e2

📥 Commits

Reviewing files that changed from the base of the PR and between f55a0c2 and 0f3089f.

📒 Files selected for processing (4)
  • src/client/device_registry.rs
  • wacore/src/iq/usync.rs
  • wacore/src/iq/usync/query.rs
  • wacore/src/store/in_memory.rs

Comment thread wacore/src/iq/usync/query.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37133ee799

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/client/device_registry.rs Outdated
Comment thread wacore/src/iq/usync.rs Outdated
@greptile-apps
greptile-apps Bot dismissed their stale review July 21, 2026 18:01

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63d8315e99

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wacore/src/iq/usync/query.rs Outdated
Comment thread wacore/src/iq/usync/query.rs
Comment thread wacore/src/iq/usync/query.rs
Comment thread wacore/src/iq/usync/query.rs
@greptile-apps
greptile-apps Bot dismissed their stale review July 21, 2026 18:28

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@jlucaso1
jlucaso1 merged commit 8b82df0 into main Jul 21, 2026
24 checks passed
@jlucaso1
jlucaso1 deleted the agent/typed-usync-core branch July 21, 2026 22:24
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
jlucaso1 added a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-design breaking-change size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant