#19419: feat(slack): track thread participation for implicit mentions
channel: slack
size: XS
Cluster:
Slack Thread Management Improvements
## Summary
- **Problem:** When the bot replies in a Slack thread it didn't start, it loses track of that thread. Future messages in the thread are ignored unless they explicitly @mention the bot.
- **Why it matters:** Users expect the bot to continue participating in conversations it joined. Without this, the bot goes silent mid-thread — confusing and broken-feeling.
- **What changed:** Added in-memory thread participation tracking. When the bot delivers a reply in a thread, it records that thread. Future messages in tracked threads are treated as implicit mentions.
- **What did NOT change (scope boundary):** No persistence (resets on restart). No changes to explicit mention handling, DM logic, channel allowlists, or any other gating. Name-based mention detection (`mentionPatterns`) is unaffected.
## Change Type (select all)
- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
- Related: Slack thread participation tracking request
## User-visible / Behavior Changes
- Bot now continues responding in Slack threads it has previously replied to, without requiring an explicit @mention or being the thread starter.
- Behavior resets on gateway restart (in-memory only).
## 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? `No`
## Repro + Verification
### Environment
- OS: Linux (Railway container)
- Runtime/container: Node v22.22.0
- Model/provider: anthropic/claude-opus-4-6
- Integration/channel: Slack
- Relevant config: `groupPolicy: "open"`, `mentionPatterns: ["draper"]`
### Steps
1. In a Slack channel where the bot is active with `requireMention: true`, have someone start a thread
2. @mention the bot in a reply — bot responds
3. Send a follow-up message in the same thread **without** @mentioning the bot
### Expected
- Bot processes the follow-up message and responds (implicit mention via thread participation)
### Actual (before this PR)
- Bot ignores the follow-up — only tracks threads where `parent_user_id === botUserId`
## Evidence
- Code review of the 3 changed files (context.ts, prepare.ts, dispatch.ts)
- Manual verification: bot was deployed with these changes and successfully tracked thread participation in live Slack conversations
## Human Verification (required)
- **Verified scenarios:** Bot replies in thread → follow-up messages in same thread are processed without @mention
- **Edge cases checked:** Thread the bot started (still works via `parent_user_id`), threads in DMs (not affected — DMs don't check `implicitMention`), multiple threads simultaneously
- **What you did NOT verify:** Behavior under extremely high thread volume (Set memory growth), persistence across restarts (intentionally not implemented)
## Compatibility / Migration
- Backward compatible? `Yes`
- Config/env changes? `No`
- Migration needed? `No`
## Failure Recovery (if this breaks)
- **How to disable/revert:** Revert commit `a894bbe9` — single commit, clean revert
- **Files/config to restore:** `src/slack/monitor/context.ts`, `src/slack/monitor/message-handler/prepare.ts`, `src/slack/monitor/message-handler/dispatch.ts`
- **Known bad symptoms:** Bot responding in threads it shouldn't be in (unlikely — only tracks threads where it actively delivered a reply)
## Risks and Mitigations
- **Risk:** In-memory Set grows unbounded over long uptimes with high thread volume
- **Mitigation:** Set only stores `channelId:threadTs` strings (~50 bytes each). Even 10K threads = ~500KB. Can add TTL/LRU eviction in a follow-up if needed.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR adds in-memory thread participation tracking to the Slack integration so the bot continues responding in threads it has previously replied to, without requiring an explicit `@mention`. When the bot successfully delivers a reply (`anyReplyDelivered = true`), it records the `channelId:threadTs` pair in a `Set<string>` on the monitor context. Future messages in those threads are treated as implicit mentions via a new `hasParticipatedInThread` check added alongside the existing `parent_user_id === botUserId` condition.
The overall approach is sound and the scope is appropriately narrow. Two issues found:
- **Type mismatch in `dispatch.ts`**: `MessageThreadId` is typed as `string | number | undefined`, but the resolved value is passed directly to `recordThreadParticipation` which expects a `string`. The identical pattern in `src/slack/threading-tool-context.ts` (line 20–25) correctly wraps the value with `String(threadId)`. In practice Slack timestamps are always strings, but this is a latent type bug.
- **Implementation detail leaked in public type**: `participatedThreads: Set<string>` is included in `SlackMonitorContext` and the `createSlackMonitorContext` return value, but it is never accessed directly outside `context.ts`. Only `recordThreadParticipation` and `hasParticipatedInThread` are needed in the public interface. Exposing the raw `Set` unnecessarily couples consumers to the internal storage representation.
- No tests were added for the new `participatedThreads` logic or the updated `implicitMention` path in `prepare.ts`.
<h3>Confidence Score: 3/5</h3>
- Safe to merge with minor fixes — the type mismatch is a latent bug that doesn't affect current behavior since Slack timestamps are always strings, and no functional bugs are present.
- The core logic is correct and the feature works as described. The type mismatch (passing `string | number` where `string` is expected) is a real issue that could fail TypeScript strict checking and is inconsistent with how the same pattern is handled elsewhere in the codebase. The raw Set being exposed in the public type is a minor design concern. Missing test coverage for the new flow slightly reduces confidence. No security or behavioral regressions identified.
- src/slack/monitor/message-handler/dispatch.ts — the String() conversion fix is the only change needed before merge.
<sub>Last reviewed commit: cd0cedd</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#21108: slack: keep requireMention thread follow-ups when thread session is...
by JayElRay · 2026-02-19
85.5%
#16223: feat(slack): sticky thread routing — bypass @mention in active threads
by iamfuntime · 2026-02-14
83.9%
#7719: fix(slack): thread replies with @mentions dropped in requireMention...
by SocialNerd42069 · 2026-02-03
82.8%
#23320: fix(slack): respect replyToMode when incomingThreadTs is auto-created
by dorukardahan · 2026-02-22
82.1%
#19083: Slack: preserve per-thread context and consistent thread replies
by jkimbo · 2026-02-17
81.7%
#22433: Slack: fix thread context loss after session reset
by stgarrity · 2026-02-21
81.6%
#19403: feat(slack): add dm.threadSession option for per-message thread ses...
by Vasiliy-Bondarenko · 2026-02-17
81.3%
#19274: feat(mattermost): enable threaded replies in channels
by rockinyp · 2026-02-17
80.1%
#20406: fix(slack): respect replyToMode when computing statusThreadTs in DMs
by QuinnYates · 2026-02-18
79.5%
#2917: Slack: fix thread context + prevent reply spillover
by SocialNerd42069 · 2026-01-27
79.3%