# NousResearch/hermes-agent · Discord thread history backfill decoupling fixes

## Open now

- 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.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #33827. Verify it addresses your case.
- **Recommended:** read PR #33827 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-33827`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.50`: the share of member 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.

## Closed / duplicate issues

- #33666: Discord thread backfill skipped when in_bot_thread is true (thread_require_mention=false)

## Prior & competing PRs

- **#33827: fix(discord): decouple thread history backfill from mention gating**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `plugins/platforms/discord/adapter.py`, `scripts/release.py`, `tests/gateway/test_discord_free_response.py`, `tests/gateway/test_discord_slash_commands.py`
  - fix hunks:
  - `plugins/platforms/discord/adapter.py`

    ```diff
    @@ -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`

    ```diff
    @@ -52,6 +52,7 @@
         "270604154+superearn-fisher@users.noreply.github.com": "superearn-fisher",
         "3540493+kpadilha@users.noreply.github.com": "kpadilha",
         "40378218+chaconne67@users.noreply.github.com": "chaconne67",
    +    "Pluviobyte@users.noreply.github.com": "Pluviobyte",
         "[redacted:email]": "sanghyuk-seo-nexcube",
         "[redacted:email]": "Brixyy",
         "[redacted:email]": "wangpuv",
    ```
  - `tests/gateway/test_discord_free_response.py`

    ```diff
    @@ -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."""
    ```

    ```diff
    @@ -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`

    ```diff
    @@ -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."""
    ```

    ```diff
    @@ -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(
    ```
- **#33684: fix(gateway): backfill Discord thread context**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#33827`, `#33705`, `#31467`
- **#33705: fix(discord): decouple thread history backfill from mention gating**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#33684`, `#33827`

## Fix status

- Merged to main in PR #33827 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-28`.
