NousResearch/hermes-agent

Discord thread history backfill decoupling fixes

Cluster 33666. Data as of

Nothing open on GitHub. Fix merged in #33827 on 28 May 2026. Check it covers your case.

The short answer

0open issues
0open PRs
4items grouped
Known?
Yes. 4 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #33827. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #33827. That is separate from the fix event.
Still open
Nothing open on GitHub.

These issues and pull requests address a bug where Discord thread history backfill was being skipped or incorrectly gated by mention or bot-thread conditions. The work involves decoupling backfill logic from mention requirements and related gateway context handling.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #33827 fix(discord): decouple thread history backfill from mention gating

    merged to main

    files: plugins/platforms/discord/adapter.py, scripts/release.py, tests/gateway/test_discord_free_response.py, tests/gateway/test_discord_slash_commands.py

    Show the diff
    • plugins/platforms/discord/adapter.py
      @@ -4811,14 +4811,19 @@ async def _handle_message(self, message: DiscordMessage) -> None:
               # to keep the partition rule clean.
               _channel_context = None
               _is_dm = isinstance(message.channel, discord.DMChannel)
      -        if not _is_dm:
      -            _needed_mention = (
      -                require_mention
      -                and not is_free_channel
      -                and not in_bot_thread
      -            )
      -            _backfill_enabled = self._discord_history_backfill()
      -            if _needed_mention and _backfill_enabled:
      +        if not _is_dm and self._discord_history_backfill():
      +            # Run backfill when there's a real gap to fill:
      +            #   - mention-gated channels with no free-response override
      +            #     (messages between bot turns aren't in the transcript)
      +            #   - any thread (in_bot_thread bypasses the mention check, but
      +            #     processing-window gaps and post-restart context still need
      +            #     recovery)
      +            # DMs skip entirely because every DM message triggers the bot,
      +            # so the session transcript already has everything.
      +            # Auto-threaded messages also skip — we just created the thread,
      +            # there's nothing prior to backfill.
      +            _has_mention_gap = require_mention and not is_free_channel and not in_bot_thread
      +            if (_has_mention_gap or is_thread) and auto_threaded_channel is None:
                       _backfill_text = await self._fetch_channel_context(
                           message.channel, before=message,
                       )
    • scripts/release.py
      @@ -52,6 +52,7 @@
           "[email protected]": "superearn-fisher",
           "[email protected]": "kpadilha",
           "[email protected]": "chaconne67",
      +    "[email protected]": "Pluviobyte",
           "[redacted:email]": "sanghyuk-seo-nexcube",
           "[redacted:email]": "Brixyy",
           "[redacted:email]": "wangpuv",
    • tests/gateway/test_discord_free_response.py
      @@ -851,6 +851,27 @@ async def test_discord_per_user_channel_backfills_too(adapter, monkeypatch):
           assert event.channel_context == "[Recent channel messages]\n[Alice] context"
       
       
      [redacted:email]
      +async def test_discord_participated_thread_backfills_without_mention(adapter, monkeypatch):
      +    """Known threads still need recent thread context when mention gating is bypassed."""
      +    monkeypatch.setenv("DISCORD_REQUIRE_MENTION", "true")
      +    monkeypatch.delenv("DISCORD_FREE_RESPONSE_CHANNELS", raising=False)
      +    monkeypatch.delenv("DISCORD_THREAD_REQUIRE_MENTION", raising=False)
      +    adapter.config.extra["history_backfill"] = True
      +    adapter._fetch_channel_context = AsyncMock(return_value="[Recent channel messages]\n[Alice] thread context")
      +
      +    thread = FakeThread(channel_id=456, name="follow-up")
      +    adapter._threads.mark("456")
      +
      +    message = make_message(channel=thread, content="follow-up without mention")
      +    await adapter._handle_message(message)
      +
      +    adapter._fetch_channel_context.assert_awaited_once()
      +    event = adapter.handle_message.await_args.args[0]
      +    assert event.text == "follow-up without mention"
      +    assert event.channel_context == "[Recent channel messages]\n[Alice] thread context"
      +
      +
       @pytest.mark.asyncio
       async def test_discord_dm_does_not_backfill(adapter, monkeypatch):
           """DMs skip backfill — every DM triggers the bot, so there's no mention gap."""
      @@ -884,3 +905,25 @@ async def test_discord_dm_does_not_backfill(adapter, monkeypatch):
               assert event.channel_context is None
       
       
      [redacted:email]
      +async def test_discord_auto_thread_skips_backfill(adapter, monkeypatch):
      +    """Auto-created threads skip backfill — the thread is brand new with no prior context."""
      +    monkeypatch.setenv("DISCORD_REQUIRE_MENTION", "true")
      +    monkeypatch.setenv("DISCORD_AUTO_THREAD", "true")
      +    monkeypatch.delenv("DISCORD_NO_THREAD_CHANNELS", raising=False)
      +    monkeypatch.delenv("DISCORD_FREE_RESPONSE_CHANNELS", raising=False)
      +    adapter.config.extra["history_backfill"] = True
      +
      +    fake_thread = FakeThread(channel_id=777, name="auto-thread")
      +    adapter._auto_create_thread = AsyncMock(return_value=fake_thread)
      +    adapter._fetch_channel_context = AsyncMock(return_value="[Recent channel messages]\n[Alice] noise")
      +
      +    bot_user = adapter._client.user
      +    parent = FakeTextChannel(channel_id=200, name="general")
      +    message = make_message(channel=parent, content="hello", mentions=[bot_user])
      +    await adapter._handle_message(message)
      +
      +    adapter._auto_create_thread.assert_awaited_once()
      +    adapter._fetch_channel_context.assert_not_awaited()
      +
      +
    • tests/gateway/test_discord_slash_commands.py
      @@ -624,6 +624,13 @@ def __init__(self, channel_id=100, name="general", guild_name="TestGuild"):
               self.guild = SimpleNamespace(name=guild_name, id=1)
               self.topic = None
       
      +    def history(self, *args, **kwargs):
      +        async def _empty():
      +            return
      +            yield  # pragma: no cover — make this an async generator
      +
      +        return _empty()
      +
       
       class _FakeThreadChannel(_discord_mod.Thread):
           """isinstance(ch, discord.Thread) → True."""
      @@ -636,6 +643,13 @@ def __init__(self, channel_id=200, name="existing-thread", guild_name="TestGuild
               self.topic = None
               self.parent = SimpleNamespace(id=parent_id, name="general", guild=SimpleNamespace(name=guild_name, id=1))
       
      +    def history(self, *args, **kwargs):
      +        async def _empty():
      +            return
      +            yield  # pragma: no cover — make this an async generator
      +
      +        return _empty()
      +
       
       def _fake_message(channel, *, content="Hello", author_id=42, display_name="Jezza"):
           return SimpleNamespace(

2 PRs closed without merging

From the close comments: merged via another change 2

  • #33684 fix(gateway): backfill Discord thread context · merged via another change → #33827, #33705, #31467
  • #33705 fix(discord): decouple thread history backfill from mention gating · merged via another change → #33684, #33827
Closed issues · 1
  • Closed#33666Discord thread backfill skipped when in_bot_thread is true (thread_require_mention=false)
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.50: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Merged PRs
#33827
Closed issues
1
Attempts
2 PRs closed without merging
Fix link checked
28 May 2026

For your agent

The same facts, typed.