Skip to content

fix(slack): MPIMs (group DMs) obey shared-surface mention gating + reaction guard - #57564

Merged
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/57339-mpim-shared-surface
Jul 3, 2026
Merged

fix(slack): MPIMs (group DMs) obey shared-surface mention gating + reaction guard#57564
kshitijk4poor merged 2 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/57339-mpim-shared-surface

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

Slack group DMs (MPIMs) now obey the same operator controls as channels — require_mention, strict_mention, allowed_channels, free_response_channels — and only get :eyes:/:white_check_mark: reactions when @mentioned. Previously they were classified as 1:1 DMs and skipped all gating, reacting to (and running the agent on) every unmentioned message — visible noise to the whole group.

Root cause: the adapter classified MPIMs as DMs (is_dm = channel_type in {"im","mpim"}), and that single flag drove both the mention/allowlist gate (if not is_dm) and the reaction guard (is_dm or is_mentioned). A 1:1 IM is a private conversation with one human, so it's legitimately mention-exempt; an MPIM is a shared surface and must obey channel-style controls.

Changes

  • plugins/platforms/slack/adapter.py: add is_one_to_one_dm = channel_type == "im" and key the two exemption sites off it. is_dm (im + mpim) is intentionally retained for session/thread scoping and chat_type labeling, where treating an MPIM as a persistent multi-party conversation is correct.
  • tests/gateway/test_slack_mention.py: extend _would_process to model is_one_to_one_dm gating + the strict_mention branch; 7 regression tests.
  • website/docs/user-guide/messaging/slack.md: distinguish 1:1 DMs (mention-exempt) from group DMs (shared surface).

Validation

Surface Mention required? Reacts when unmentioned? Obeys allowed_channels?
1:1 DM (im) No (unchanged) Yes (unchanged) Exempt (unchanged)
Group DM (mpim) Yes (was: no) No (was: yes) Yes (was: exempt)
Channel Yes (unchanged) No (unchanged) Yes (unchanged)
  • Premise confirmed live on main (both gate + reaction guard driven by is_dm).
  • Plugin-only diff (no core touches). Sibling audit clean — the only other is_dm/is_im sites are session scoping / chat_type labeling and correctly left alone.
  • test_slack.py + test_slack_mention.py = 288 passed; lint clean.
  • Mutation check: reverting the helper to the buggy mpim-exempt behavior flips the 3 MPIM-drop regression tests red, confirming they're genuine (not tautologies).

Salvaged from #57339 by @victor-kyriazakos; authorship preserved via cherry-pick. Closes #57339.

…action guard

Group DMs (MPIMs) were classified as DMs and thereby exempted from every
operator control that shared surfaces are supposed to honor: allowed_channels,
require_mention, strict_mention, free_response_channels, and the reaction
guard. Symptom: the bot added 👀/✅ to unmentioned MPIM
messages and still invoked the agent (which then returned NO_REPLY) instead of
the gateway dropping the event before model execution. Removing an MPIM from
allowed_channels did not disable it.

Root cause is the DM classification at adapter.py:
    is_dm = channel_type in {"im", "mpim"}
used for BOTH routing exemptions and reaction gating. An MPIM is a shared
surface (multiple humans can see and trigger the bot), not a private 1:1 DM,
so it must be gated like a channel.

This behavior was introduced/reinforced by a trail of Slack group-DM PRs:
- NousResearch#4633  fix(slack): treat group DMs (mpim) like DMs + reaction guard
- NousResearch#54632 fix(slack): subscribe to message.mpim + mpim scopes so group DMs work
- NousResearch#54663 fix(slack): group DMs work OOTB + reinstall nudge
NousResearch#54632/NousResearch#54663 correctly made MPIM messages *reachable*; NousResearch#4633 over-reached by
giving them the DM mention/reaction *exemptions*. This corrects only that
over-reach.

Fix (minimal): introduce `is_one_to_one_dm = channel_type == "im"` and key the
two EXEMPTION sites off it instead of `is_dm`:
- mention/allowlist gating block (`if not is_one_to_one_dm and bot_uid:`)
- reaction guard (`(is_one_to_one_dm or is_mentioned)`)
`is_dm` is intentionally retained for session/thread scoping and chat_type
labeling, where treating an MPIM as a persistent multi-party conversation is
correct — only the mention/reaction exemptions were wrong.

Docs: slack.md now distinguishes 1:1 DMs (mention-exempt) from group DMs
(shared surface; obey require_mention/strict_mention/allowed_channels/
free_response_channels; reactions only when @mentioned).

Tests: +7 in test_slack_mention.py (MPIM unmentioned dropped under
require_mention and strict_mention; MPIM mentioned processed; MPIM off
allowed_channels dropped; MPIM in free_response opted in; 1:1 IM still exempt;
reaction guard drops unmentioned MPIM). Updated _would_process to model the
is_one_to_one_dm gating + strict_mention. 72 passed.
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery platform/slack Slack app adapter P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 3, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #57339 (earlier OPEN PR by @victor-kyriazakos with a byte-identical fix; this is a salvage of it), #7294 (OPEN issue requesting exactly this mpim mention-gating), #4633 (closed PR that introduced the DM over-reach). This salvage and #57339 are competing fixes for the same bug via the same mechanism (is_one_to_one_dm split) — a maintainer should pick one and close the other.

The reaction-guard regression test defined a local _should_react lambda and
asserted it against itself — a tautology that would stay green even if the
production guard at _handle_slack_message reverted to (is_dm or is_mentioned),
re-introducing the unmentioned-MPIM reaction spam this PR fixes.

Replace it with a shared _reaction_guard helper plus a source-introspection
test that pins the production expression: asserts (is_one_to_one_dm or
is_mentioned) is present and (is_dm or is_mentioned) is absent. Mutation-checked
— reverting the adapter guard now fails the test.

Follow-up self-review finding on the salvage of NousResearch#57339.
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) July 3, 2026 07:00
@kshitijk4poor
kshitijk4poor merged commit 5e2b051 into NousResearch:main Jul 3, 2026
29 checks passed
@kshitijk4poor
kshitijk4poor deleted the salvage/57339-mpim-shared-surface branch August 5, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/slack Slack app adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants