refactor(gateway): centralize JID normalization in lib/identity.js#2503
Merged
Conversation
added 8 commits
April 14, 2026 15:43
- Pure functional module: isLidJid, isGroupJid, normalizeDeviceScopedJid, extractE164, phoneToJid, resolvePeerId, deriveOwnerJids - No side effects on require, no hidden state, cache passed as param - resolvePeerId honors 5-step heuristic from CONTEXT §A Specifics - Unit tests: 41 assertions across 7 describe blocks
Add import of identity helpers next to existing lib/echo-tracker import. No call-site changes yet — subsequent commits migrate sites one at a time.
Replace inline Set(map(n => n.replace(/^\+/, '') + '@s.whatsapp.net')) with the module helper. Behavior-preserving.
Unify the 3 identical inline copies in sendMessage/sendImage/sendAudio (they were verbatim duplicates; diff confirmed identical). Group JIDs still passthrough, phones still coerced to @s.whatsapp.net form.
…D-01, ID-03)
- Main sender-resolution cascade (~line 1171-1197) replaced with a single
resolvePeerId() call that honors the 5-step heuristic from CONTEXT §A.
- Phone derivation via extractE164() — also fixes latent bug where
device-scoped incoming JIDs ('123:[email protected]') previously yielded
malformed '+123:45' phone strings; now correctly yields '+123'.
- console.warn for unresolved identity becomes structured JSON per ID-03:
{event: 'identity_unresolved', jid, reason, lid_cache_size, confidence}.
- Preserved side effects outside the pure function per §Concerns #1:
senderPn cache write and CS-02 resolveLidProactively both still run
BEFORE resolvePeerId — the function only resolves, it never writes.
- Renamed local 'isLidJid' (shadowed module fn) to 'isLid' per §Concerns
#2 — all downstream references (ownerLidJids check at line ~1211)
updated to the alias.
- isLidJid/isGroupJid module fns replace inline endsWith checks. - phoneToJid replaces inline '+'-strip + '@s.whatsapp.net' append.
Final inline endsWith('@g.us') site removed. Zero inline JID suffix
manipulation remains in index.js.
- 12 equivalence assertions comparing pre-refactor inline logic to lib/identity helpers across: isLid, isGroup, deriveOwnerJids, phoneToJid, resolvePeerId (5 fixture shapes: plain phone, LID+senderPn, LID+cache, LID+participant, LID unresolvable), extractE164 device-strip latent fix, normalizeDeviceScopedJid passthrough. - 1 assertion validating ID-03 identity_unresolved JSON log shape (event, jid, reason, lid_cache_size, confidence all present). - Test count: 129 -> 142.
houko
approved these changes
Apr 14, 2026
houko
left a comment
Contributor
There was a problem hiding this comment.
LGTM. Clean extraction refactor with good equivalence tests and latent bug fix coverage.
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
Phase 4 §A of the openclaw-style WhatsApp gateway overhaul. Pure refactor: extracts every inline JID/LID/E.164 manipulation in `packages/whatsapp-gateway/index.js` (~8 ad-hoc sites) into a single `lib/identity.js` module with a tested, behavior-preserving API. Adds structured logging on identity-resolution failures (ID-03) so unresolvable LIDs surface in logs instead of silently producing a malformed peer string.
The refactor enables the upcoming `lid_cache` SQLite persistence (Phase 4 §B) by giving it a single point to hook write-through.
What `lib/identity.js` exposes
Latent bug closed
Legacy inline `'+' + senderPnJid.replace(/@.*$/, '')` produced malformed `+123:45` for device-scoped JIDs. `extractE164` now correctly returns `+123`. Documented in commit `bd1dcb4f` + asserted in tests.
Behavior preservation
Each refactor commit is bisectable — full test suite passes at every step. Pre-refactor inline logic was reproduced verbatim as `legacyX` helpers inside the equivalence test block, then asserted equal to the new module output across 5 fixture shapes (plain PN, LID+senderPn, LID+cache hit, LID+participant, LID-unresolvable) plus outbound phone-to-JID and owner-derivation.
Test plan
Rollback
Pure refactor — revert PR. No flag needed.
Files
No new dependencies. Phase 4 §B (persisted `lid_cache` table) is the natural follow-up.