Skip to content

fix(slack): edit status bubbles in place instead of posting new ones - #68378

Closed
trac3r00 wants to merge 1 commit into
NousResearch:mainfrom
trac3r00:fix/slack-status-dedup
Closed

fix(slack): edit status bubbles in place instead of posting new ones#68378
trac3r00 wants to merge 1 commit into
NousResearch:mainfrom
trac3r00:fix/slack-status-dedup

Conversation

@trac3r00

@trac3r00 trac3r00 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

[Bob]

Symptom

During a compression/fallback retry loop, a Slack thread received a dozen separate, out-of-order status bubbles within ~3 minutes (live capture, 26-07-20):

:arrows_counterclockwise: Primary model failed — switching to fallback: codex/gpt-5.6-sol ...
:compression: Context too large (~355,194 tokens) — compressing (3/3)...
:warning: Model declined to respond (safety refusal) — trying fallback...
:compression: Compressed 331 → 120 messages, retrying...
:compression: Compressed 239 → 233 messages, retrying...
:compression: Context too large (~357,559 tokens) — compressing (2/3)...
:compression: Context too large (~371,951 tokens) — compressing (1/3)...
Context length exceeded: max compression attempts (3) reached.
:compression: Compressed 244 → 242 messages, retrying...

Note the ordering: "(3/3)" arrives before "(2/3)" and "(1/3)" — each event is an independent async chat.postMessage, so arrival order isn't delivery order, which makes the spam actively misleading on top of noisy.

Root cause

gateway/run.py:515 (_send_or_update_status_coro) already implements the right behavior — edit the previous bubble for the same status_key — but only when the adapter implements send_or_update_status:

sender = getattr(adapter, "send_or_update_status", None)
if callable(sender):
    return await sender(chat_id, status_key, content, metadata=metadata)
return await adapter.send(chat_id, content, metadata=metadata)

Only the Telegram adapter implements it (#30045). Slack falls through to adapter.send — one fresh thread message per status event.

Fix

Implement send_or_update_status on the Slack adapter following the Telegram pattern:

  • first call posts and caches the message ts keyed by (channel, thread_ts, status_key) — thread-scoped, unlike Telegram's (chat_id, status_key), because a single Slack channel multiplexes many concurrent sessions as threads; a channel-scoped key would cross-edit status bubbles between unrelated threads
  • subsequent calls edit that message via the existing edit_messagechat.update path (finalize=False, so no Block Kit re-derivation per progress tick)
  • edit failure (message deleted, cant_update_message, too old) drops the cached ts and falls back to a fresh send, re-caching the new ts
  • cache is FIFO-bounded (2000 entries, halved on overflow) so long-lived gateways don't grow it unbounded

No gateway/core changes needed — _send_or_update_status_coro picks the method up via the existing getattr probe, and the status cleanup path (_cleanup_msg_ids) is unaffected since it tracks whatever message_id the adapter returns.

Tests

tests/gateway/test_slack_status_update.py (5 tests):

  • fresh send on first call
  • in-place edit on second call, targeting the first send's ts
  • fallback-to-fresh on edit failure + re-cache of the new ts (third call edits the replacement)
  • no cross-talk between distinct status keys
  • no cross-talk between distinct threads (the Slack-specific hazard)
5 passed

Existing Slack + Telegram gateway suites (263 tests) green on the branch.

Progress/status callbacks (context-pressure, compression retries,
model fallback) route through _send_or_update_status_coro, which
edits the previous bubble for the same status_key when the adapter
implements send_or_update_status — but only Telegram did. On Slack
every status event posted a fresh thread message, so a compression
retry loop spammed a dozen out-of-order bubbles into the thread
('Context too large 1/3... 2/3... 3/3', fallback switches, etc.).

Implement send_or_update_status on the Slack adapter following the
Telegram pattern (NousResearch#30045): first call posts and caches the message ts
per (channel, thread, status_key); subsequent calls edit that message
via chat.update. Edit failure drops the cached ts and falls back to a
fresh send. Cache is FIFO-bounded.
@alt-glitch alt-glitch added type/bug Something isn't working comp/plugins Plugin system and bundled plugins platform/slack Slack app adapter P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 21, 2026
teknium1 added a commit that referenced this pull request Jul 23, 2026
- rt.cms012@gmail.com -> trac3r00 (#68378; commit authored as
  'Minseo-Choi' — trac3r00's display name, same account)
- 15167896+2001Y@users.noreply.github.com -> 2001Y (#64267)
- hello@jeromeiveson.com -> Trantor-develops (#57196)
- boumagent@gmail.com -> patp (#18859)
- dorukardahan@hotmail.com -> dorukardahan (#17184) was already mapped.
teknium1 added a commit that referenced this pull request Jul 23, 2026
- rt.cms012@gmail.com -> trac3r00 (#68378; commit authored as
  'Minseo-Choi' — trac3r00's display name, same account)
- 15167896+2001Y@users.noreply.github.com -> 2001Y (#64267)
- hello@jeromeiveson.com -> Trantor-develops (#57196)
- boumagent@gmail.com -> patp (#18859)
- dorukardahan@hotmail.com -> dorukardahan (#17184) was already mapped.
teknium1 added a commit that referenced this pull request Jul 23, 2026
- rt.cms012@gmail.com -> trac3r00 (#68378; commit authored as
  'Minseo-Choi' — trac3r00's display name, same account)
- 15167896+2001Y@users.noreply.github.com -> 2001Y (#64267)
- hello@jeromeiveson.com -> Trantor-develops (#57196)
- boumagent@gmail.com -> patp (#18859)
- dorukardahan@hotmail.com -> dorukardahan (#17184) was already mapped.
@teknium1

Copy link
Copy Markdown
Contributor

Merged via #70189 — your commit was cherry-picked/reapplied onto current main with your authorship preserved in git history: your send_or_update_status edit-in-place mechanism was cherry-picked as the base.

Thanks for the contribution!

@teknium1 teknium1 closed this Jul 23, 2026
@trac3r00

Copy link
Copy Markdown
Contributor Author

Thanks for carrying this into #70189 and preserving the original authorship. I verified the superseding PR is the landed path, so no further action is needed here.

[bob]

randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
- rt.cms012@gmail.com -> trac3r00 (NousResearch#68378; commit authored as
  'Minseo-Choi' — trac3r00's display name, same account)
- 15167896+2001Y@users.noreply.github.com -> 2001Y (NousResearch#64267)
- hello@jeromeiveson.com -> Trantor-develops (NousResearch#57196)
- boumagent@gmail.com -> patp (NousResearch#18859)
- dorukardahan@hotmail.com -> dorukardahan (NousResearch#17184) was already mapped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have 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