#20406: fix(slack): respect replyToMode when computing statusThreadTs in DMs
channel: slack
size: XS
Cluster:
Slack Thread Management Fixes
## Problem
When `replyToMode` is set to `"off"` for Slack DMs (via `replyToModeByChatType.direct: "off"`), typing indicators and intermediate status/narration messages were still being posted as **threaded replies** to the user's message.
This caused agent workflows with multiple tool calls and narration text to create large numbers of unwanted threaded messages in DMs — in one reported case, a single interaction produced **28 threaded replies** when the user expected zero.
## Root Cause
In `src/slack/threading.ts`, `resolveSlackThreadTargets` computes two values:
- `replyThreadTs` — correctly respects `replyToMode`: returns `undefined` when mode is `"off"`
- `statusThreadTs` — used for typing indicators and status updates — had a problematic fallback:
```typescript
// Before (buggy):
const statusThreadTs = replyThreadTs ?? messageTs;
```
When `replyToMode` is `"off"`, `replyThreadTs` is `undefined`, so `statusThreadTs` **always fell back to `messageTs`**, forcing every status update into a thread regardless of configuration.
## Fix
Remove the fallback so `statusThreadTs` is consistent with `replyThreadTs`:
```typescript
// After (fixed):
const statusThreadTs = replyThreadTs;
```
When `replyToMode` is `"off"` and the message is not already a thread reply, both values are `undefined` and no threading occurs at all.
## Behaviour Matrix
| Scenario | replyThreadTs | statusThreadTs (before) | statusThreadTs (after) |
|---|---|---|---|
| DM, `replyToMode: "off"`, top-level msg | `undefined` | `messageTs` ❌ | `undefined` ✅ |
| DM, `replyToMode: "all"`, top-level msg | `messageTs` | `messageTs` ✅ | `messageTs` ✅ |
| Any channel, incoming thread reply | `thread_ts` | `thread_ts` ✅ | `thread_ts` ✅ |
## Changes
- `src/slack/threading.ts`: Remove `?? messageTs` fallback from `statusThreadTs`
- `src/slack/threading.test.ts`: Update existing test that was asserting the buggy behaviour; rename it to reflect correct intent
## Testing
Existing test suite updated. The test `"keeps status threading even when reply threading is off"` was asserting the buggy behaviour — it now correctly expects `statusThreadTs` to be `undefined` when `replyToMode` is `"off"` and the message has no `thread_ts`.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes a bug where Slack typing indicators and status messages were posted as threaded replies in DMs even when `replyToMode` was set to `"off"`. The root cause was a `?? messageTs` fallback on `statusThreadTs` that always forced threading when `replyThreadTs` was `undefined`. The fix removes this fallback so `statusThreadTs` follows `replyThreadTs` consistently.
- **`src/slack/threading.ts`**: Removed `?? messageTs` fallback from `statusThreadTs` computation, making it consistent with `replyThreadTs`
- **`src/slack/threading.test.ts`**: Updated the test that was asserting the buggy behavior to now correctly expect `statusThreadTs` to be `undefined` when `replyToMode` is `"off"` for a top-level message
- Downstream callers (`setSlackThreadStatus` in `context.ts`) already handle `undefined` `threadTs` with an early return, so no additional changes are needed
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge — it's a minimal, well-scoped one-line bug fix with an updated test.
- The change is a single line removal of a fallback operator (`?? messageTs`) that was causing the documented bug. The fix is logically sound: it aligns `statusThreadTs` with `replyThreadTs`, and the downstream consumer (`setSlackThreadStatus`) already handles `undefined` with an early return. The test suite has been updated to reflect the correct behavior. No new code paths are introduced, no edge cases are missed, and the behavior matrix in the PR description accurately captures all scenarios.
- No files require special attention
<sub>Last reviewed commit: 0847588</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#23320: fix(slack): respect replyToMode when incomingThreadTs is auto-created
by dorukardahan · 2026-02-22
88.0%
#12244: fix(slack): preserve thread context for DM thread replies
by junhoyeo · 2026-02-09
87.9%
#23799: fix(slack): finalize replyToMode off threading behavior
by vincentkoc · 2026-02-22
87.7%
#19083: Slack: preserve per-thread context and consistent thread replies
by jkimbo · 2026-02-17
86.5%
#4749: fix: handle string thread IDs in queue drain for Slack
by nvonpentz · 2026-01-30
86.3%
#23804: fix(slack): preserve string thread context in queue + DM route
by vincentkoc · 2026-02-22
84.0%
#22485: fix(slack): use threadId from delivery context as threadTs fallback...
by dorukardahan · 2026-02-21
83.8%
#23513: fix(slack): respect replyToMode=off for inline directive reply tags
by dorukardahan · 2026-02-22
83.7%
#20389: fix(slack): inject thread history on first thread turn, not only on...
by lafawnduh1966 · 2026-02-18
83.5%
#14720: fix(slack): pass threadId in plugin read action (#14706)
by lailoo · 2026-02-12
83.3%