Skip to content

🐛 fix(channel): explicit group mention no longer suppresses replies#620

Merged
vaayne merged 6 commits into
mainfrom
619
Jul 1, 2026
Merged

🐛 fix(channel): explicit group mention no longer suppresses replies#620
vaayne merged 6 commits into
mainfrom
619

Conversation

@vaayne

@vaayne vaayne commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What

Two changes on this branch:

  1. Fix (🐛 fix: Feishu explicit @mentions can suppress group replies #619) — an explicit @mention that resolves to no member agent no longer silently drops the turn; it falls back to semantic routing instead of producing zero dispatch rows.
  2. Removal — deletes the group_mode concept (always / mention / disabled) entirely across plugins, core dispatch, pkg/channel config, Web UI, docs and skills.

Why

#619: Feishu (and any platform) could ack an @agent mention with the processing reaction yet never reply. decideResponders took the deterministic mention branch and returned immediately, so when no mention resolved to a group member (bot-identity/registry miss, empty mention open_id, a human-only @mention, or a mention of a non-member bot) no dispatch row was created. Explicit mentions were strictly less reliable than no-mention semantic routing.

group_mode removal: The semantic arbiter is always wired in production, so group_mode was effectively never consulted — only disabled had any real effect, and that as a plugin-level ingest gate. Rather than keep a three-valued config that mostly did nothing, we removed it.

How

  • decideResponders returns early on the mention path only when it yields at least one member agent; otherwise it logs a Debug diagnostic (platform, mention/member counts) and falls through to semantic routing. The genuinely anomalous per-mention miss reasons are logged in resolveMentionAgentsWithMembers.
  • resolveMentionAgentsWithMembers gains per-mention debug diagnostics for the three miss reasons: registry miss, channel/agent lookup miss, resolved-to-non-member.
  • Removed effectivePlatformGroupMode / groupModeConfig and every plugin's shouldRespondInGroup / shouldIngestGroup / isBotMentioned / groupMode helper, plus config fields, schema, metadata, Web selects, i18n keys and channel docs.
  • The no-arbiter degraded path now auto-replies only for a single-member web group and stays silent otherwise.

⚠️ Breaking change

There is no per-channel disable switch anymore. To stop a bot participating in a group, remove it from the group. Any stored group_mode (including nested per-group groups.*.group_mode) in channel config is now ignored — behavior no longer depends on it.

Refs

Verified: mise run format && build && test all green; Windows cross-build passes; golangci-lint 0 issues.

…solves to no member

An explicit @mention that resolved to no group-member agent (bot-identity/
registry miss, a human-only @mention, or a mention of a non-member bot) was
silently dropped: the platform acked the message but no dispatch row was
created, making explicit mentions strictly less reliable than no-mention
routing.

decideResponders now returns early only when the mention path yields at least
one member agent; otherwise it warns and falls through to the same no-mention
semantic/group-mode path. Adds per-mention debug diagnostics in
resolveMentionAgentsWithMembers for registry/channel/membership misses.
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

📊 Coverage Report

Total coverage: 49.8% (generated files excluded)

Lowest-covered entries (first 200)
github.com/CherryHQ/stella/api/client/helpers.go:103:        0.0%
github.com/CherryHQ/stella/api/client/helpers.go:111:        0.0%
github.com/CherryHQ/stella/api/client/helpers.go:17:         0.0%
github.com/CherryHQ/stella/api/client/helpers.go:27:         0.0%
github.com/CherryHQ/stella/api/client/helpers.go:35:         0.0%
github.com/CherryHQ/stella/api/client/helpers.go:58:         0.0%
github.com/CherryHQ/stella/api/client/helpers.go:70:         0.0%
github.com/CherryHQ/stella/api/client/helpers.go:89:         0.0%
github.com/CherryHQ/stella/cmd/stella/commands.go:9:         0.0%
github.com/CherryHQ/stella/cmd/stella/db.go:14:              0.0%
github.com/CherryHQ/stella/cmd/stella/db.go:24:              0.0%
github.com/CherryHQ/stella/cmd/stella/email.go:103:          0.0%
github.com/CherryHQ/stella/cmd/stella/email.go:815:          0.0%
github.com/CherryHQ/stella/cmd/stella/email.go:828:          0.0%
github.com/CherryHQ/stella/cmd/stella/email.go:82:           0.0%
github.com/CherryHQ/stella/cmd/stella/email.go:836:          0.0%
github.com/CherryHQ/stella/cmd/stella/email.go:844:          0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:145:           0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:184:           0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:222:           0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:22:            0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:258:           0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:292:           0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:332:           0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:48:            0.0%
github.com/CherryHQ/stella/cmd/stella/goal.go:99:            0.0%
github.com/CherryHQ/stella/cmd/stella/main.go:11:            0.0%
github.com/CherryHQ/stella/cmd/stella/mise.go:13:            0.0%
github.com/CherryHQ/stella/cmd/stella/mise.go:29:            0.0%
github.com/CherryHQ/stella/cmd/stella/oauth.go:102:          0.0%
github.com/CherryHQ/stella/cmd/stella/oauth.go:139:          0.0%
github.com/CherryHQ/stella/cmd/stella/oauth.go:14:           0.0%
github.com/CherryHQ/stella/cmd/stella/oauth.go:31:           0.0%
github.com/CherryHQ/stella/cmd/stella/oauth.go:68:           0.0%
github.com/CherryHQ/stella/cmd/stella/recally_articles.go:403: 0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:146:          0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:15:           0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:172:          0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:183:          0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:33:           0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:64:           0.0%
github.com/CherryHQ/stella/cmd/stella/token.go:89:           0.0%
github.com/CherryHQ/stella/cmd/stella/version.go:11:         0.0%
github.com/CherryHQ/stella/cmd/stellad/commands.go:318:      0.0%
github.com/CherryHQ/stella/cmd/stellad/commands.go:334:      0.0%
github.com/CherryHQ/stella/cmd/stellad/commands.go:355:      0.0%
github.com/CherryHQ/stella/cmd/stellad/commands.go:388:      0.0%
github.com/CherryHQ/stella/cmd/stellad/commands.go:446:      0.0%
github.com/CherryHQ/stella/cmd/stellad/commands.go:89:       0.0%
github.com/CherryHQ/stella/cmd/stellad/debug_dump.go:30:     0.0%
github.com/CherryHQ/stella/cmd/stellad/debug_dump.go:35:     0.0%
github.com/CherryHQ/stella/cmd/stellad/debug_dump.go:58:     0.0%
github.com/CherryHQ/stella/cmd/stellad/debug_dump_signal_unix.go:12: 0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:135:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:362:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:373:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:377:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:384:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:392:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:399:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:410:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:418:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:439:       0.0%
github.com/CherryHQ/stella/cmd/stellad/gateway.go:64:        0.0%
github.com/CherryHQ/stella/cmd/stellad/main.go:11:           0.0%
github.com/CherryHQ/stella/cmd/stellad/models.go:12:         0.0%
github.com/CherryHQ/stella/cmd/stellad/models.go:37:         0.0%
github.com/CherryHQ/stella/cmd/stellad/models.go:41:         0.0%
github.com/CherryHQ/stella/cmd/stellad/models.go:50:         0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:107: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:120: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:141: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:164: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:18: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:190: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:195: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:203: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:225: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:26: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:81: 0.0%
github.com/CherryHQ/stella/cmd/stellad/plugin_services.go:94: 0.0%
github.com/CherryHQ/stella/cmd/stellad/postgres.go:118:      0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:101: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:115: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:119: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:123: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:127: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:131: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:142: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:149: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:153: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:161: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:185: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:198: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:215: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:236: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:245: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:272: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:292: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:374: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:393: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:404: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:414: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:422: 0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:56:  0.0%
github.com/CherryHQ/stella/cmd/stellad/service_linux.go:60:  0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_embedding.go:19: 0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_embedding.go:31: 0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_memory.go:20:   0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_memory.go:70:   0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_memory.go:86:   0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_plugins.go:129: 0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_plugins.go:30:  0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_plugins.go:69:  0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_plugins.go:93:  0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_pool.go:23:     0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_pool.go:31:     0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_pool.go:39:     0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_pool.go:84:     0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_reflect.go:27:  0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_reflect.go:45:  0.0%
github.com/CherryHQ/stella/cmd/stellad/setup_skills.go:26:   0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:181:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:221:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:233:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:238:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:338:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:575:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:598:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:608:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:615:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:692:       0.0%
github.com/CherryHQ/stella/cmd/stellad/version.go:768:       0.0%
github.com/CherryHQ/stella/internal/agent/agentctx/context.go:12: 0.0%
github.com/CherryHQ/stella/internal/agent/agentctx/context.go:20: 0.0%
github.com/CherryHQ/stella/internal/agent/agentctx/context.go:29: 0.0%
github.com/CherryHQ/stella/internal/agent/agentctx/context.go:37: 0.0%
github.com/CherryHQ/stella/internal/agent/agentctx/context.go:46: 0.0%
github.com/CherryHQ/stella/internal/agent/agentctx/context.go:66: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:100: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:104: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:108: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:142: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:156: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:165: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:187: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:204: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:214: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:249: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:270: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:305: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:320: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:348: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:369: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:395: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:402: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:412: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:435: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:44: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:459: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:478: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:48: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:499: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:525: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:52: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:537: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:551: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:56: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:578: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:596: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:60: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:613: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:628: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:645: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:68: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:72: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:76: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:80: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:84: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:88: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:92: 0.0%
github.com/CherryHQ/stella/internal/agent/pool_manager.go:96: 0.0%
github.com/CherryHQ/stella/internal/agent/prompt/prompt.go:271: 0.0%
github.com/CherryHQ/stella/internal/agent/runner_impl.go:303: 0.0%
github.com/CherryHQ/stella/internal/agent/runner_impl.go:313: 0.0%
github.com/CherryHQ/stella/internal/agent/runner_impl.go:434: 0.0%
github.com/CherryHQ/stella/internal/agent/runner_impl.go:442: 0.0%
github.com/CherryHQ/stella/internal/agent/runner_impl.go:449: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/chat.go:258: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/chat.go:436: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/chat.go:458: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/options.go:23: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/options.go:31: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/options.go:38: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/options.go:45: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/options.go:55: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/runner_cache.go:200: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/runner_cache.go:272: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/runtime.go:106: 0.0%
github.com/CherryHQ/stella/internal/agent/runtime/runtime.go:113: 0.0%

vaayne added 5 commits July 1, 2026 18:50
…nd-off explicit

Address review of #620:
- Downgrade the unresolved-mention fall-through from Warn to Debug: @-mentioning
  a human or a non-member bot is ordinary group traffic; a Warn per such message
  would bury the genuinely anomalous registry-hit-but-unresolved case already
  logged per-mention in resolveMentionAgentsWithMembers.
- Drop the redundant mentions_raw / mention_platform_ids log fields (and their
  eager-alloc helpers); the raw text and platform id are already logged where
  resolution happens.
- Clear envelope.Mentions before the shared no-mention path so the hand-off is
  unambiguously mention-free instead of relying on the arbiter to re-derive it.
- Add an always-mode test that positively proves the fall-through path runs
  (pre-#619 code returned zero rows there).
Deletes the always/mention/disabled group_mode config across plugins, core
dispatch, pkg config, Web UI, docs and skills. The semantic arbiter is always
wired in production, so it intercepts before group_mode ever applied — always
vs mention was dead, and only disabled had any effect (an ingest gate).

To stop a bot participating in a group, remove it from the group; there is no
per-channel disable switch anymore. The no-arbiter degraded path now only
auto-replies for a single-member web group and stays silent otherwise.

Refs #619
- decideResponders: fix header comment that still named the deleted
  "platform group-mode policy"; it now describes the real degraded path.
- Drop the dead `allMembersFallback` param from fallbackGroupDecision — after
  group_mode removal every caller passed false and no all-members path remains.
- Remove the now-dead `envelope.Mentions = nil` assignment: the fall-through
  routes purely off groupMembers and never re-reads it (the second
  mention-taking arbiter call it guarded against is gone).
- Docs: group-chat-multi-agent.{md,zh.md} still described pre-#619 mention
  suppression and the deleted always/mention policy; corrected both.

Refs #619
The degraded no-arbiter path had no test for a multi-member platform (non-web)
group — the branch that must stay silent where `group_mode: always` once
broadcast to every member. Add a positive lock so any reintroduced all-members
fallback for platform groups fails the suite.

Refs #619
The prior fix overstated it: a platform single-member group with no semantic
arbiter stays silent without a WARN (the WARN only fires for len(members) > 1).

Refs #619
@vaayne vaayne merged commit 30c3369 into main Jul 1, 2026
5 checks passed
@vaayne vaayne deleted the 619 branch July 1, 2026 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 fix: Feishu explicit @mentions can suppress group replies

1 participant