mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-12 20:29:10 +02:00
In a Lark group with multiple Multica bots installed, the bot whose WS
received the event sometimes failed to recognize that it was the @-target
while the OTHER bot's supervisor falsely fired. Bohan's controlled three-
message test (only @A, only @B, @both) hit this: @A and @B alone went
unanswered, @both got picked up by A only.
Root cause: the `mentions[].id.open_id` field Lark puts on the WS event
is structurally INVERSE to `/bot/v3/info`'s `bot.open_id` across the two
WSes. From A's WS perspective, the wire-form open_id for "A was @-ed"
is NOT equal to A's API-side open_id, but IS equal to what B's WS sees
on its side, and vice versa. The decoder's `mention.open_id ==
inst.BotOpenID` match therefore fires on the wrong bot in multi-bot
groups. Only `union_id` (the Lark-tenant-scoped stable identifier) is
consistent across both WSes.
Changes:
- migration 112 adds nullable `lark_installation.bot_union_id`
- sqlc query exposes UpsertLarkInstallation/CreateLarkInstallation
with bot_union_id, plus a focused SetLarkInstallationBotUnionID for
the backfill path
- httpAPIClient.GetBotInfo now follows /bot/v3/info with /contact/v3/
users/{open_id}?user_id_type=open_id and returns both identifiers
on BotInfo. Soft-fails on contact-scope denial: install still
succeeds with an empty UnionID, and the decoder falls back to the
legacy open_id match for single-bot deployments.
- RegistrationService.finishSuccess persists union_id alongside
open_id during the device-flow finalize.
- ws_frame_decoder.containsMention prefers union_id and only walks
open_id when the installation row has not been backfilled yet.
- BackfillBotUnionIDs runs once at server boot for installations
created before migration 112; bounded per-row 10s timeout and a
pure soft-fail policy so a slow Lark round-trip cannot block
startup.
- regression tests cover the three decoder paths: union_id match
wins over open_id mismatch, union_id mismatch overrides open_id
match, and open_id fallback when union_id is unknown.
Co-authored-by: multica-agent <github@multica.ai>