#22101: fix(slack): dedupe mentions by ts fallback for app_mention
channel: slack
size: S
Cluster:
Slack Thread Management Improvements
## Summary
- Fixes duplicated Slack channel replies when a channel message is delivered both via `message` and `app_mention` events.
- Normalize inbound Slack message keys to `ts || event_ts` before dedupe and thread-resolution, so Slack app mention payloads that omit `ts` still map to the same message identity.
- Preserve context IDs when possible by collecting message IDs from `ts || event_ts` for grouped dispatch payloads.
- Add regression tests for both handler dedupe behavior and thread-resolution lookup fallback.
## Root Cause
`createSlackMessageHandler` only passed `message.ts` into dedupe and thread-resolution logic. When `app_mention` events arrive without `ts` but with `event_ts`, the same inbound post can be treated as distinct after the `message` event path, creating two lane enqueues and two embedded-agent runs.
## Fix Details
- `src/slack/monitor/message-handler.ts`
- Deduplicate with `const messageTs = message.ts ?? message.event_ts` before `markMessageSeen`.
- Pass normalized `ts` through thread resolver so `thread_ts` lookup works consistently.
- Track Message IDs from `ts || event_ts` when batching events.
- `src/slack/monitor/thread-resolution.ts`
- Use `ts || event_ts` for thread resolution cache key and API lookup.
- Keep verbose logs aligned to the normalized identifier.
## Testing
- `pnpm vitest run src/slack/monitor/message-handler.test.ts src/slack/monitor/thread-resolution.test.ts`
- `pnpm exec oxlint src/slack/monitor/message-handler.ts src/slack/monitor/thread-resolution.ts src/slack/monitor/message-handler.test.ts src/slack/monitor/thread-resolution.test.ts`
- `pnpm exec oxfmt --check src/slack/monitor/message-handler.ts src/slack/monitor/thread-resolution.ts src/slack/monitor/message-handler.test.ts src/slack/monitor/thread-resolution.test.ts`
## Confidence Score: 5/5
- This change is a narrowly scoped data-id normalization bugfix with deterministic behavior and zero user-facing semantic drift.
- It prevents dual processing by making both Slack event shapes map to the same dedupe identity and ensures thread lookup remains deterministic when `ts` is absent.
- The logic is covered by direct regression tests at both the inbound-message-handler and thread-resolution layers.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Normalizes Slack message identifiers to use `ts ?? event_ts` instead of just `ts`, preventing duplicate processing when `app_mention` events arrive without `ts` but with `event_ts`. The fix ensures both event types map to the same dedupe identity and thread resolution cache key.
- Applied normalization at dedupe check (message-handler.ts:115-120)
- Applied normalization in message ID collection for batched events (message-handler.ts:87-89)
- Applied normalization in thread resolution cache key and API lookup (thread-resolution.ts:81-104)
- Added regression tests for both handler dedupe and thread-resolution fallback scenarios
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- Narrowly scoped data normalization fix with deterministic behavior, covered by regression tests, and zero semantic drift in message processing logic
- No files require special attention
<sub>Last reviewed commit: 1bfd4fc</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20152: fix(slack): allow app_mention events to bypass dedup cache
by nova-openclaw-cgk · 2026-02-18
86.5%
#7719: fix(slack): thread replies with @mentions dropped in requireMention...
by SocialNerd42069 · 2026-02-03
86.4%
#20406: fix(slack): respect replyToMode when computing statusThreadTs in DMs
by QuinnYates · 2026-02-18
82.9%
#23804: fix(slack): preserve string thread context in queue + DM route
by vincentkoc · 2026-02-22
80.5%
#19083: Slack: preserve per-thread context and consistent thread replies
by jkimbo · 2026-02-17
80.0%
#12244: fix(slack): preserve thread context for DM thread replies
by junhoyeo · 2026-02-09
80.0%
#20479: fix(slack): keep replies flowing for oversized file uploads
by olyashok · 2026-02-19
79.9%
#10643: fix(slack): classify D-prefix DMs correctly when channel_type disag...
by mcaxtr · 2026-02-06
79.7%
#22433: Slack: fix thread context loss after session reset
by stgarrity · 2026-02-21
79.4%
#22485: fix(slack): use threadId from delivery context as threadTs fallback...
by dorukardahan · 2026-02-21
79.3%