# NousResearch/hermes-agent · Fix redaction masking Discord mention snowflake IDs

## Open now

- 0 issues, 1 PR open on GitHub.
- PR #35929: fix(gateway): mention-lint guardrail for Discord <@...> literals

These items address a bug where the redact_sensitive_text() function incorrectly masks Discord @-mention snowflake IDs, breaking multi-bot mentions. The work focuses on stopping this unintended masking at the persistence boundary.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 1 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #35665. Verify it addresses your case.
- **Recommended:** read PR #35665 (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-35665`. 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

- #35611: \[Bug\]: redact_sensitive_text() unconditionally masks Discord <@snowflake> mentions, breaking multi-bot @-pings

## Prior & competing PRs

- **#35665: fix(redact): stop masking Discord mention snowflakes**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `agent/redact.py`, `tests/agent/test_redact.py`
  - fix hunks:
  - `agent/redact.py`

    ```diff
    @@ -150,10 +150,6 @@
         r"(?:\.[A-Za-z0-9_=-]{4,}){0,2}"   # Optional payload and/or signature
     )
     
    -# Discord user/role mentions: <@123456789012345678> or <@!123456789012345678>
    -# Snowflake IDs are 17-20 digit integers that resolve to specific Discord accounts.
    -_DISCORD_MENTION_RE = re.compile(r"<@!?(\d{17,20})>")
    -
     # E.164 phone numbers: +<country><number>, 7-15 digits
     # Negative lookahead prevents matching hex strings or identifiers
     _SIGNAL_PHONE_RE = re.compile(r"(\+[1-9]\d{6,14})(?![A-Za-z0-9])")
    ```

    ```diff
    @@ -419,10 +415,6 @@ def _redact_telegram(m):
         if "&" in text and "=" in text:
             text = _redact_form_body(text)
     
    -    # Discord user/role mentions (<@snowflake_id>)
    -    if "<@" in text:
    -        text = _DISCORD_MENTION_RE.sub(lambda m: f"<@{'!' if '!' in m.group(0) else ''}***>", text)
    -
         # E.164 phone numbers (Signal, WhatsApp)
         if "+" in text:
             def _redact_phone(m):
    ```
  - `tests/agent/test_redact.py`

    ```diff
    @@ -342,39 +342,33 @@ def test_home_assistant_jwt_in_memory(self):
     
     
     class TestDiscordMentions:
    -    """Discord snowflake IDs in <@ID> or <@!ID> format."""
    +    """Discord mention snowflakes (<@ID> / <@!ID>) are public syntax, not
    +    secrets — they must pass through the redactor unchanged so multi-bot
    +    @-pings (DISCORD_ALLOW_BOTS=mentions) keep resolving. See issue #35611."""
     
    -    def test_normal_mention(self):
    -        result = redact_sensitive_text("Hello <@222589316709220353>")
    -        assert "222589316709220353" not in result
    -        assert "<@***>" in result
    +    def test_normal_mention_passes_through(self):
    +        text = "Hello <@222589316709220353>"
    +        assert redact_sensitive_text(text) == text
     
    -    def test_nickname_mention(self):
    -        result = redact_sensitive_text("Ping <@!1331549159177846844>")
    -        assert "1331549159177846844" not in result
    -        assert "<@!***>" in result
    +    def test_nickname_mention_passes_through(self):
    +        text = "Ping <@!1331549159177846844>"
    +        assert redact_sensitive_text(text) == text
     
    -    def test_multiple_mentions(self):
    +    def test_multiple_mentions_pass_through(self):
             text = "<@111111111111111111> and <@222222222222222222>"
    -        result = redact_sensitive_text(text)
    -        assert "111111111111111111" not in result
    -        assert "222222222222222222" not in result
    +        assert redact_sensitive_text(text) == text
     
    -    def test_short_id_not_matched(self):
    -        """IDs shorter than 17 digits are not Discord snowflakes."""
    +    def test_short_id_passes_through(self):
             text = "<@12345>"
             assert redact_sensitive_text(text) == text
     
    -    def test_slack_mention_not_matched(self):
    -        """Slack mentions use letters, not pure digits."""
    +    def test_slack_mention_passes_through(self):
             text = "<@U024BE7LH>"
             assert redact_sensitive_text(text) == text
     
         def test_preserves_surrounding_text(self):
             text = "User <@222589316709220353> said hello"
    -        result = redact_sensitive_text(text)
    -        assert result.startswith("User ")
    -        assert result.endswith(" said hello")
    +        assert redact_sensitive_text(text) == text
     
     
     class TestWebUrlsNotRedacted:
    ```
- **#35615: fix(redact): stop masking Discord mention snowflake IDs at persistence boundary**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#32259`, `#35611`
- **#35929: fix(gateway): mention-lint guardrail for Discord <@...> literals**. open
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `c2cbe2c97`, `#35611`, `#35665`, `#125`
  - files: `agent/mention_lint.py`, `agent/redact.py`, `plugins/platforms/discord/adapter.py`, `scripts/audit-mentions.py`, `tests/agent/test_mention_lint.py`, `tests/agent/test_mention_lint_integration.py`, `tests/agent/test_redact.py`, `tools/cronjob_tools.py`, `tools/skill_manager_tool.py`

## Fix status

- Merged to main in PR #35665 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-31`.
- Subsystem: `agent`
