fix(api): preserve message identities and report pending push-name sync
Blocklist lookup currently treats equal PN/LID user strings as the same identity, message app-state actions reconstruct raw addressing tuples, and push-name updates hide failed app-state synchronization. This change addresses report findings F05, F09, F10 and F13 together, including their existing consumer and E2E call sites.
Behavior and migration
blocking().is_blocked(&jid)validates PN/LID before lookup/IQ, ignores device IDs while preserving the server, and considers the alternate namespace only through an explicit mapping. Mapping backend errors remain typed causes.star_message/unstar_messagenow take&MessageRef;delete_message_for_metakes&MessageRef, delete_media, timestamp. Use a received/sent reference directly. The positional APIs are removed. Syncd indices retain chat/content ID/from-me/participant order; incoming group authors use bare PN/LID account identities, while DM and own-message participants project to"0". Newsletter references remain a separate domain.- Message-label methods take
&MessageIdafter label and chat. Their existing schema and fixed"0", "0"tail remain unchanged. Client::generate_message_id() -> MessageId;MessageRetransmission::newaccepts/storesMessageId, and its getter returns&MessageId. Existing raw boundaries useas_str()/into_string(). Generation bytes and the distinction between original content IDs and new operationStanzaIds remain unchanged. Rustdoc usesClient::send/SendRequest.set_push_name(&str) -> Result<PushNameOutcome, ProfileError>, withSyncedorSyncPending { source: AppStateError }. Presence transport failures still return errors.sync_push_name(&str) -> Result<(), AppStateError>retries app-state only. Successful presence means transport acceptance, not remote display or durable storage confirmation; pending sync remains usable during pairing.
External documentation migration is coordinated by the report campaign's docs owner. No compatibility aliases, dependencies, manifests, workflows, or reserved optimization files are added/changed.
Protocol evidence
Static inspection of original WhatsApp Web 2.3000.1045368834, indexed by whatspec 1a441f0, schema 4.3.0. Verified all 516 JS files, 77,347,149 bytes, against the same lock. Archive SHA-256 2edd4b3f8dae50b503e0b15920adc774e2680cdb542396d57608bb0374196ee1. Modules extracted with Acorn AST; offsets below are original bytes, end-exclusive.
WAWebBlocklistCollection: bundleffe69f61924eb23c1d65cb89a152e4ce35ee404bd52a7edcced9c037b236f490, 4025020..4027615. Full Wid keys and explicit PN/LID conversion.WAWebSyncdIndexUtils: same bundle, 1593317..1595162. Received non-user messages need a valid participant.WAWebSyncdActionUtils: bundle657a32007737e50b0cb0292ce9c364e21e48291b558129753b25e3ec2a4c13b7, 1851726..1852909. Delete-for-me index projection.WAWebStarMessageSyncusesWAWebSyncdUtils.constructMsgKeySegmentsFromMsgKeywith the same author tail.WAWebStarMessageSync: bundle034314e49fa571a8e2e8491843a15536326b4a47d1f66ac7704d2332ee1b391e, 4280701..4286392; delete-for-me 4185669..4193279. Their duplicate definitions were also extracted.WAWebPushNameBridge: bundle469e8e15743d3d0d694e5157379c9d8e7b55ab57a790d1b129112021619f7e02, 764244..764730, calls presence and returns the sync lock promise.WAWebPushNameSyncand presence builder dependencies were checked too. This is version-specific static evidence, not an authenticated server test.
Validation
Regressions exercise public blocking operations with synthetic IQ replies, backend mapping failure, invalid-target zero-send behavior, decoded syncd patches across DM/group PN/LID/from-me/device variants, generated ID format, downstream typed retransmission, push-name partial and transport failures, and sync-only retry without a second presence. Existing message-label tests retain exact wire indices. The device-qualified-author regression failed before the review fix and passes afterward. The consumer retains the complete PushNameOutcome and pending cause.
At final head 5b1752fb9a6d251d99cfe273ab58205ab616a1fd:
- Rust CI passes all jobs: 6,915 workspace tests, doctests, MSRV, Clippy, all-feature build/lint/tests, Rustdoc, formatting, generated artifacts, serde representations, and every feature matrix.
- API Consumers, WASM, Miri, CodSpeed, autofix, and Supply Chain conclude successfully. Local validation also passed 2,482 library tests, 57 consumer tests/doctests, Clippy, official Rust 1.94.1 and WASM consumer checks. Local WASM retains two pre-existing qualification warnings in send/mod.rs and upload.rs.
- E2E passes all 168 tests (11 existing skips) after one same-head rerun. The first attempt passed 167 and timed out in
test_fresh_chatstate_delivered_on_reconnect; the rerun passed it in 1.408 s without code changes. Its cause remains unconfirmed. These are established mock-server E2E tests, not authenticated production-server evidence. - Binary Size passes the budget against validated main: total stripped binary 11.03 MiB and total text 8.91 MiB, both unchanged; 607 dependencies, unchanged; SDK library LLVM lines decrease by 58. The job tested merge commit
a47730bf6, containing this head and base8f711a9bf. This measures the native fixture, not ESP32 image size.
Both Greptile findings are fixed and their threads resolved; Greptile re-reviewed this head with confidence 5/5. CodeRabbit completed a full review of all 24 files at the same head with no actionable findings or concrete merge-blocking defect. Its retained limits match the documented static protocol evidence and caller-owned retry supersession.
Supply Chain's existing informational SemVer job fails against published 0.7.0: 23 lint categories for wacore, 3 for wacore-binary, and 7 for waproto. Those crate trees, manifests, and lockfile are unchanged from base 8f711a9bf, so these reported failures are baseline differences. The job only checks those three crates; it does not validate this PR's intentional SDK API breakages documented above. No gate is weakened.
An earlier local full run hit repeat_dm_sends_serve_memoized_signal_addressing once; it passed isolated and in three subsequent full runs without changes to the test or cache implementation. Its cause remains unconfirmed. Independent static review also caught an extra typed-to-string allocation, removed from internal raw generation sites.