#23804: fix(slack): preserve string thread context in queue + DM route
channel: slack
maintainer
size: XS
dedupe:parent
Cluster:
Slack Thread Management Fixes
## Summary
- rebases the `#11934` Slack queue thread-context fix on latest `main`
- preserves string Slack `thread_ts` during collect-mode queue grouping (`queue/drain.ts`)
- keeps DM `deliveryContext.threadId` updates in Slack prepare/dispatch route persistence
- adds an Unreleased changelog fix line
## Linked Issue/PR
- Related #10837
- Supersedes #11934
## Verification
- `pnpm test src/auto-reply/reply/reply-flow.test.ts`
- test passes locally
## Scope boundary
- does not include broader `replyToMode: off` threading behavior changes (handled in #23799)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes Slack thread context being lost during collect-mode queue draining and DM route persistence, preventing threaded follow-up messages from leaking to the main channel.
- **Queue drain thread grouping (`drain.ts`)**: Adds empty-string guards to `originatingThreadId` checks so that empty-string Slack `thread_ts` values are treated the same as `null`/`undefined`, preventing them from producing distinct routing keys that split same-thread messages into different delivery batches. Also fixes a stale debug comment pointing to a renamed test file.
- **DM route persistence (`prepare.ts`)**: Adds `threadId: threadContext.messageThreadId` to the `updateLastRoute` call in `recordInboundSession`, so Slack DM sessions record thread context during inbound message preparation — matching the pattern already used by Telegram and other channels.
- **Dispatch route persistence (`dispatch.ts`)**: Adds `threadId: prepared.ctxPayload.MessageThreadId` to the `deliveryContext` in the dispatch-time `updateLastRoute` call, ensuring thread context survives through the full prepare→dispatch lifecycle.
- **Changelog**: Adds an Unreleased fix entry referencing #11934.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge — it adds defensive guards and missing field propagation without changing existing control flow.
- All changes are additive/defensive: empty-string guards on existing null checks, and adding a missing `threadId` field to two `updateLastRoute` call sites. The types (`DeliveryContext.threadId: string | number`, `FollowupRun.originatingThreadId: string | number`) already support the values being passed. The fix follows established patterns used by other channels (Telegram). No existing behavior is altered for non-empty thread IDs.
- No files require special attention
<sub>Last reviewed commit: 0b2d845</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#11194: fix(slack): queue drain drops string thread_ts — replies leak to ma...
by Lukavyi · 2026-02-07
87.3%
#12244: fix(slack): preserve thread context for DM thread replies
by junhoyeo · 2026-02-09
86.8%
#4749: fix: handle string thread IDs in queue drain for Slack
by nvonpentz · 2026-01-30
85.6%
#23799: fix(slack): finalize replyToMode off threading behavior
by vincentkoc · 2026-02-22
85.5%
#19083: Slack: preserve per-thread context and consistent thread replies
by jkimbo · 2026-02-17
84.6%
#20406: fix(slack): respect replyToMode when computing statusThreadTs in DMs
by QuinnYates · 2026-02-18
84.0%
#22433: Slack: fix thread context loss after session reset
by stgarrity · 2026-02-21
82.0%
#22485: fix(slack): use threadId from delivery context as threadTs fallback...
by dorukardahan · 2026-02-21
81.8%
#23513: fix(slack): respect replyToMode=off for inline directive reply tags
by dorukardahan · 2026-02-22
81.7%
#23320: fix(slack): respect replyToMode when incomingThreadTs is auto-created
by dorukardahan · 2026-02-22
81.3%