Skip to content

feat(channels): suppress_placeholder toggle for Discord and Slack#987

Open
tarrencev wants to merge 3 commits intonextlevelbuilder:devfrom
cartridge-gg:feat/suppress-thinking-placeholder
Open

feat(channels): suppress_placeholder toggle for Discord and Slack#987
tarrencev wants to merge 3 commits intonextlevelbuilder:devfrom
cartridge-gg:feat/suppress-thinking-placeholder

Conversation

@tarrencev
Copy link
Copy Markdown

@tarrencev tarrencev commented Apr 21, 2026

Summary

  • Adds a suppress_placeholder boolean to DiscordConfig and SlackConfig (also wired through each channel's DB-instance factory).
  • When enabled on Discord, the "Thinking..." placeholder + later edit is skipped; the typing indicator runs for the full agent turn (TTL raised 60s → 30 min as a safety net) and the final response is posted as a new message.
  • When enabled on Slack, the placeholder is skipped; streaming is force-disabled (Slack streaming edits the placeholder) and the thinking_face reaction (when reaction_level is minimal/full) acts as the in-progress indicator.
  • Extracted placeholderSuppressed() helper on the Slack channel to replace 5 repeated pointer-deref checks.

Why

On channels with a proper presence API, the intermediate "Thinking..." text message + message-edit cycle can feel clunky, especially in shared groups or when users prefer a quieter indicator. WhatsApp and Telegram already skip this pattern; this PR brings the same option to Discord and Slack behind an opt-in flag.

Compatibility

  • Default is unchanged (placeholder + edit); existing deployments see no behavior change.
  • Retry notifications and non-streaming tool-status updates are delivered by editing the placeholder, so both are silently dropped when suppressed. That's the point of the toggle (minimize in-progress chatter) and is documented in docs/05-channels-messaging.md.

Pre-Landing Review

Ran the full review pipeline (4 specialists + red team on a 266-line diff). Findings:

  • Testing specialist (4 informational) — flagged missing handler-level tests for the suppress paths. Skipped this round because the existing TestStreamEnabledSuppressPlaceholder covers the main risk vector; adding handler tests requires a session/postMessage spy harness that doesn't yet exist in the repo, which is larger than this PR's scope.
  • Maintainability specialist (4 informational) — magic number, DRY violation (inverted-nil guard repeated 5 times), factory-duplication, struct tag alignment. Auto-fixed: named the Discord typing TTL constants and the helper. Skipped: factory.go DRY refactor (pre-existing) and struct alignment (gofmt wants to reflow the unrelated TelegramConfig struct — not touching upstream code).
  • Security specialist — NO FINDINGS.
  • Performance specialist — NO FINDINGS.
  • Red Team (4 critical, 4 informational) — flagged the silent-drop of retry/tool-status notifications (intentional per feature design, now called out in docs), the 10-min TTL ceiling for long agent runs (fixed — bumped to 30 min with a named constant), concurrent-typing race (pre-existing, skipped), and a doc undersell (fixed).

PR Quality Score: 8.5/10

Test Plan

  • go build ./... + go build -tags sqliteonly ./...
  • go vet ./...
  • go test ./internal/channels/... ./internal/config/... — all pass
  • New TestStreamEnabledSuppressPlaceholder covers the Slack stream-disable interaction
  • Manual Discord DM + group turn with suppress_placeholder: true — confirm typing runs for full turn, no "Thinking..." message, final response arrives as a new message
  • Manual Slack DM + thread turn with suppress_placeholder: true + reaction_level: minimal — confirm no placeholder, thinking-face reaction fires, final response as a new message

Note: one unrelated pre-existing flaky test in internal/tools/shell_abort_test.go was noticed during /ship (process-group cleanup detecting orphan sleep 60 PIDs in the environment). No code in this PR touches internal/tools/.

🤖 Generated with Claude Code

tarrencev and others added 3 commits April 21, 2026 12:08
Adds a `suppress_placeholder` config option to Discord and Slack channels
that skips sending the "Thinking..." placeholder message and its
subsequent edit-with-final-response pattern. Instead, the channel's
native in-progress indicator (Discord typing indicator, Slack thinking
reaction) is shown for the full duration of the agent turn and the
final response is posted as a new message.

- Discord: typing indicator TTL is raised to 10 minutes when suppressed
  so long agent turns do not prematurely lose the indicator.
- Slack has no bot-typing API, so suppression forces streaming off and
  relies on the reaction system when `reaction_level` is enabled.
- Also plumbs the field through the DB-instance config struct in each
  channel's factory so DB-configured channels can use the toggle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lper

Post-review follow-ups on the suppress_placeholder toggle:

- Discord: raise the suppressed-mode typing TTL from 10 min to 30 min and
  name both the default and suppressed TTLs as constants. 10 min was too
  low — long agent runs (multi-step research, tool chains) can exceed it,
  leaving the user staring at dead typing dots before the final response.
  30 min is a pragmatic safety-net ceiling.
- Slack: extract a `placeholderSuppressed()` helper used by handleMessage,
  handleAppMention, StreamEnabled, and New's streaming-conflict warning.
  Replaces five inline `c.config.SuppressPlaceholder != nil && *c...`
  checks with a single readable method.
- Docs: document the tradeoffs honestly — retry notifications and
  tool-status updates are delivered via placeholder edit and are silently
  dropped when suppressed; the typing indicator is the only in-progress
  signal. Note that Slack's thinking-face reaction requires reaction_level
  to be set (it's off by default).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant