← Back to PRs

#17453: fix(signal): make group reactions deterministic from inbound sender metadata

by akalypse open 2026-02-15 19:36 View on GitHub →
channel: signal stale size: M
## Summary Describe the problem and fix in 2–5 bullets: - Problem: Signal group reactions were unreliable because targetAuthor/targetAuthorUuid was often missing or model- guessed, causing reactions to fail or be accepted without visible UI effect. - Why it matters: Group reaction behavior must deterministically target the exact inbound message author; otherwise reactions are flaky in real group chats. - What changed: Added an inbound reaction-target cache (groupId + messageId -> sender uuid/e164), populated it in Signal inbound handling, and used it in outbound react action prep when targetAuthor* is not provided. Also tightened reaction payload shaping to prefer UUID and set recipients for group reactions/removals. - What did NOT change (scope boundary): No changes to non-Signal channels, no new external services, no schema/API contract changes, and no config migration. ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [x] Memory / storage - [x] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Closes # - Related # ## User-visible / Behavior Changes - Signal group “react to this message” is now more reliable when messageId is known but targetAuthor* is omitted. - For group reactions/removals, UUID targeting is preferred and recipient shaping is more consistent. - No new user-facing config required. ## Security Impact (required) - New permissions/capabilities? (No) - Secrets/tokens handling changed? (No) - New/changed network calls? (No) - Command/tool execution surface changed? (No) - Data access scope changed? (Yes) - If any Yes, explain risk + mitigation: - Added a small in-memory cache of inbound group reaction metadata (sender identifier keyed by group/message) with TTL and size cap. - Mitigations: bounded cache (MAX_ENTRIES), expiration (TTL_MS), no persistence to disk, no secret material. ## Repro + Verification ### Environment - OS: macOS - Runtime/container: Docker Compose (openclaw-gateway + signal-cli-daemon) - Model/provider: OpenAI Codex (gpt-5.3-codex) - Integration/channel (if any): Signal group + direct chat - Relevant config (redacted): channels.signal.reactionLevel=minimal, actions.reactions=true ### Steps 1. Receive a Signal group inbound message and ask agent to react to that message. 2. Trigger message.react without explicitly forcing targetAuthorUuid. 3. Observe gateway logs and Signal UI behavior. ### Expected - Group reaction resolves to exact inbound sender and appears in Signal UI. ### Actual - Before fix: reactions frequently failed (targetAuthor required) or reported success but were not visible. - After fix: deterministic sender targeting is applied via inbound cache; reactions are consistently resolved. ## Evidence Attach at least one: - [x] Failing test/log before + passing after - [x] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) ## Human Verification (required) What you personally verified (not just CI), and how: - Verified scenarios: - Group reaction target hydration from inbound metadata cache. - UUID-preferred target selection. - Group removal payload shaping using resolved target. - Edge cases checked: - Missing targetAuthor* with valid groupId + messageId. - UUID vs E.164 fallback behavior. - What you did not verify: - Full matrix across all Signal daemon versions and multi-device clients. ## Compatibility / Migration - Backward compatible? (Yes) - Config/env changes? (No) - Migration needed? (No) - If yes, exact upgrade steps: ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: - Revert commit cda8b78bbd8d76001d2331596038babb3cb82763. - Files/config to restore: - src/infra/outbound/message-action-runner.ts - src/signal/monitor/event-handler.ts - src/signal/send-reactions.ts - remove src/signal/reaction-target-cache.ts - Known bad symptoms reviewers should watch for: - Group react regression to targetAuthor is required errors. - Group reactions accepted but not visible in Signal UI. ## Risks and Mitigations - Risk: - Stale cache entry could map message->author incorrectly if identifiers collide. - Mitigation: - Keyed by exact groupId + messageId, bounded size, TTL expiry, and no cross-group reuse. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes unreliable Signal group reactions by adding a deterministic sender-resolution mechanism. The core change introduces an in-memory reaction-target cache (`reaction-target-cache.ts`) that maps `(groupId, messageId)` to the inbound sender's UUID/E.164, populated during Signal inbound message handling. When an outbound group reaction is triggered without explicit `targetAuthor`/`targetAuthorUuid`, the cache is consulted to resolve the correct author. - **New cache module** (`src/signal/reaction-target-cache.ts`): bounded (5000 entries), TTL-expiring (24h), properly normalized keys. No persistence, no secret material. - **Outbound hydration** (`src/infra/outbound/message-action-runner.ts`): Signal react actions now auto-resolve `targetAuthorUuid` from the cache when the agent doesn't explicitly provide it. - **UUID preference** (`src/signal/send-reactions.ts`): `resolveTargetAuthorParams` now prefers UUID over phone number, and group reactions/removals set `recipients` from the resolved target author when no explicit recipient is provided. - **Inbound recording** (`src/signal/monitor/event-handler.ts`): group messages now record sender metadata into the cache for later reaction-target resolution. - Tests cover cache operations, UUID preference, group removal scenarios, and end-to-end hydration flow. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge — it adds a well-bounded, non-persistent in-memory cache with no external side effects and tightens existing reaction payload logic. - All changes are narrowly scoped to Signal group reaction handling. The new cache is bounded (5000 entries, 24h TTL), normalization is idempotent and consistent between record and resolve paths, Map deletion during iteration is spec-safe, and the UUID-preference reorder in resolveTargetAuthorParams is correct. Tests cover core scenarios including hydration, UUID preference, phone fallback, prefix normalization, and group removals. No new external network calls, no schema changes, no security surface expansion. - No files require special attention <sub>Last reviewed commit: 2ecb41e</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs