Skip to content

fix: resolve WhatsApp phone-JID/LID alias when looking up handover state - #5

Merged
keiravoss94 merged 1 commit into
mainfrom
fix/handover-lid-phone-key-mismatch
Apr 27, 2026
Merged

keiravoss94 merged 1 commit into
mainfrom
fix/handover-lid-phone-key-mismatch

Conversation

@keiravoss94

@keiravoss94 keiravoss94 commented Apr 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Handover rows were keyed by (platform, chat_id) and the WhatsApp bridge can surface the same human under either a phone JID (60173380115@s.whatsapp.net) or a LID (122299244130458@lid). A row stored under one form was invisible to lookups using the other.
  • Live impact: the basketball-jersey-demo bot replied to a customer twice mid-handover ("the handover is active") because the row was keyed by phone JID while the inbound surfaced the LID, so is_active returned False and the bot dispatched.
  • Fix: walk gateway.whatsapp_identity.expand_whatsapp_aliases (the same helper core uses for session keys), enumerate every JID variant, and probe all of them. Pin subsequent writes to the row's stored chat_id so state never fragments across variants. The trigger_handover tool also reuses an existing alias row instead of creating a sibling.

Root cause

Live SQLite row at the time of incident:

whatsapp|60173380115@s.whatsapp.net|agent_tool:Customer asked for designer handover (smoke)|trigger_handover_tool|expires 2026-04-27 16:35:54

Inbound gateway.log lines for the same chat at 16:30/16:31:

inbound message: platform=whatsapp user=Kong chat=122299244130458@lid msg='hi hi'
response ready:  platform=whatsapp chat=122299244130458@lid time=14.8s

60173380115 and 122299244130458 are the two forms of the same chat (verified via whatsapp/session/lid-mapping-122299244130458_reverse.json → 60173380115). Pre-fix is_active keyed by the LID form missed the row. No gateway-policy skip log was emitted for either inbound — confirms the rule returned None and the bot ran.

Test plan

  • python -m pytest tests/ -o 'addopts=' -q (48 passed, +4 new tests covering LID→phone lookup, /takeback across alias, owner-extend across alias, trigger_handover reusing an alias row)
  • Synced built plugin into profiles/basketball-jersey-demo/plugins/gateway_policy/ and restarted gateway
  • Verified post-fix the existing live row is now reachable from LID-form inbounds (no schema migration needed — alias-aware lookup finds the existing row in place)

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Enhanced WhatsApp handover state management to properly recognize and reuse active handovers regardless of the identifier format used, preventing duplicate or fragmented handover states.
  • Tests

    • Added test coverage for WhatsApp chat identifier handling in handover scenarios.

Handover rows are keyed by `(platform, chat_id)` and the WhatsApp
bridge surfaces the same human under either a phone-JID
(`60173380115@s.whatsapp.net`) or a LID (`122299244130458@lid`)
depending on protocol negotiation. A row stored under one form was
silently invisible to lookups using the other, so the bot bypassed an
active handover whenever the bridge flipped variants — which is how
Kong got two "the handover is active" replies from the bot mid-handover.

The keying mismatch is fixed by walking
`gateway.whatsapp_identity.expand_whatsapp_aliases` (the same helper
Hermes core uses for session keys), enumerating every JID variant for
the inbound chat_id, and probing all of them in `is_active` /
`/takeback` / owner-extend paths. Subsequent writes pin to the row's
stored chat_id so we don't fragment state across variants. The
`trigger_handover` tool also reuses an existing alias row instead of
creating a sibling under the inbound form.
@keiravoss94
keiravoss94 merged commit a80e282 into main Apr 27, 2026
@coderabbitai

coderabbitai Bot commented Apr 27, 2026 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95713948-3286-453d-ad7d-c80dcf2b7642

📥 Commits

Reviewing files that changed from the base of the PR and between 6922514 and b5536bd.

📒 Files selected for processing (4)
  • rules/handover.py
  • state.py
  • tests/test_rules.py
  • tools/trigger_handover.py

Walkthrough

The changes introduce WhatsApp chat ID alias resolution to the handover system. Instead of a simple platform+chat_id lookup, the system now expands a chat ID into multiple alias forms (numeric, numeric@s.whatsapp.net, numeric@lid) and searches for an active handover across all forms. Upon finding an active handover, the system uses the persisted chat ID from that row for all subsequent operations, preventing state fragmentation.

Changes

Cohort / File(s) Summary
State layer & alias expansion
state.py
Adds whatsapp_alias_chat_ids to generate multiple JID variants from a numeric ID, alias_chat_ids wrapper for platform-specific expansion, and HandoverStore.find_active method to search for active handovers across candidate chat-id forms with lazy expiration cleanup.
Handover rule & trigger handler
rules/handover.py, tools/trigger_handover.py
Updated to resolve a canonical active_chat_id using find_active + alias_chat_ids before checking active status, setting deactivation targets, and formatting response payloads; ensures all operations target the persisted row instead of the inbound alias form.
Test coverage
tests/test_rules.py
New test suite verifying handover_rule and trigger_handover correctly operate on already-active handovers when inbound chat_id is an alias variant, including control-flow outputs, state updates, deactivation semantics, and TTL extension.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant HandoverRule as Handover Rule
    participant State
    participant Storage as HandoverStore
    participant TriggerHandler as Trigger Handler

    Client->>HandoverRule: Inbound message (chat_id alias variant)
    HandoverRule->>State: alias_chat_ids(platform, chat_id)
    State-->>HandoverRule: [numeric, numeric@s.whatsapp.net, numeric@lid]
    HandoverRule->>Storage: find_active(platform, candidates)
    Storage->>Storage: Search for active row across aliases
    Storage->>Storage: Deactivate expired matches lazily
    Storage-->>HandoverRule: HandoverRow (with canonical chat_id)
    HandoverRule->>HandoverRule: Set is_active, stored_chat_id
    
    rect rgba(100, 150, 200, 0.5)
    Note over HandoverRule,Storage: Deactivation & Extension Operations
    HandoverRule->>Storage: Deactivate/extend using stored_chat_id
    Storage-->>HandoverRule: Success
    end
    
    HandoverRule-->>Client: Response (using canonical chat_id)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through aliases so fine,
Finding the real chat, each JID line,
Numeric, netted, or LID in form—
One canonical row keeps the handover warm! ✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/handover-lid-phone-key-mismatch

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 and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant