Skip to content

[Bug]: Background watch_match notifications can be injected into the wrong Discord thread #10411

@dvnryu

Description

@dvnryu

Bug Description

Bug summary
I’m seeing background process watch-pattern notifications show up in a different Discord thread than the one that originally started the process.

What happened
A background process emitted a watch-pattern match like:

[SYSTEM: Background process proc_xxx matched watch pattern "signal=" ...]

But that notification appeared in my current thread, even though the process was started from a different thread.

So this looks like cross-thread notification leakage / wrong routing.

Why I think this is a bug
From the code path:

tools/process_registry.py puts watch_match events into a global completion_queue
gateway/run.py drains that queue after an agent turn
then it calls _inject_watch_notification(synth_text, event)
_inject_watch_notification() reuses the current event source
so the watch notification can get injected into whichever thread happens to be active when the queue is drained

That means watch_match events are not necessarily routed back to the original thread that created the background process.

Relevant code locations
tools/process_registry.py around watch_match queue insertion
gateway/run.py around:
_format_gateway_process_notification()
queue drain after agent run
_inject_watch_notification()

Observed behavior
Background process started in thread A
Later, while chatting in thread B
watch-pattern notification appears in thread B

Expected behavior
Watch notifications should go back to the original source thread/chat that launched the process

or at minimum be filtered by original session/thread metadata before injection

Suggested fix
Either:
include routing metadata (platform, chat_id, thread_id, maybe session_key) directly on watch_match events and route from that
or
only inject drained watch events when they match the current session/thread

Impact
This can leak noisy or confusing system notifications across unrelated threads, especially in Discord.

Steps to Reproduce

  1. In Discord, start a background process with a watch pattern, for example watch_patterns=["signal="].

  2. Use a long-running command such as:

    bash
    RUN_DIR="logs/runs/2026-04-15-tonight"; mkdir -p "$RUN_DIR"; PYTHONPATH=. /Users/devin/.local/bin/python3.11 src/intraday_renko_runner.py --tickers SPY,QQQ,NVDA,TSLA,GLD --interval 5m --brick-pct 1.0 --output-dir "$RUN_DIR" --regular-hours-only

  3. Let the process emit output containing signal= lines.

  4. Open a different Discord thread and send a normal message to Hermes.

  5. Observe that Hermes posts a system message like:

    text
    [SYSTEM: Background process proc_2d8c2ff4e8e5 matched watch pattern "signal=" ...]

Observed result:
The watch-pattern notification appears in the wrong Discord thread instead of the original thread where the process was started.

Expected Behavior

Watch-pattern notifications should be delivered only to the original Discord thread that started the background process.

They should not appear in a different thread just because that thread happened to trigger the next agent turn.

Actual Behavior

Example notification received in the wrong thread:

[SYSTEM: Background process proc_2d8c2ff4e8e5 matched watch pattern "signal=".
Command: RUN_DIR="logs/runs/2026-04-15-tonight"; mkdir -p "$RUN_DIR"; PYTHONPATH=. /Users/devin/.local/bin/python3.11 src/intraday_renko_runner.py --tickers SPY,QQQ,NVDA,TSLA,GLD --interval 5m --brick-pct 1.0 --output-dir "$RUN_DIR" --regular-hours-only
Matched output:
[2026-04-14T15:20:09.499949+00:00] TSLA 5m bar=2026-04-14T11:20:00-04:00 close=362.04 bricks=131 latest_brick=up@360.62 signal=buy@346.55 position=long closed_trades=13
...
]

Affected Component

Other

Messaging Platform (if gateway-related)

No response

Debug Report

Bug summary
I’m seeing background process watch-pattern notifications show up in a different Discord thread than the one that originally started the process.

What happened
A background process emitted a watch-pattern match like:

[SYSTEM: Background process proc_xxx matched watch pattern "signal=" ...]


But that notification appeared in my current thread, even though the process was started from a different thread.

So this looks like cross-thread notification leakage / wrong routing.

Why I think this is a bug
From the code path:

tools/process_registry.py puts watch_match events into a global completion_queue
gateway/run.py drains that queue after an agent turn
then it calls _inject_watch_notification(synth_text, event)
_inject_watch_notification() reuses the current event source
so the watch notification can get injected into whichever thread happens to be active when the queue is drained

That means watch_match events are not necessarily routed back to the original thread that created the background process.

Relevant code locations
tools/process_registry.py around watch_match queue insertion
gateway/run.py around:
_format_gateway_process_notification()
queue drain after agent run
_inject_watch_notification()

Observed behavior
Background process started in thread A
Later, while chatting in thread B
watch-pattern notification appears in thread B

Expected behavior
Watch notifications should go back to the original source thread/chat that launched the process
 
or at minimum be filtered by original session/thread metadata before injection

Suggested fix
Either:
include routing metadata (platform, chat_id, thread_id, maybe session_key) directly on watch_match events and route from that
or
only inject drained watch events when they match the current session/thread

Impact
This can leak noisy or confusing system notifications across unrelated threads, especially in Discord.

Operating System

macOS26

Python Version

No response

Hermes Version

No response

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

No response

Proposed Fix (optional)

No response

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    sweeper:implemented-on-mainSweeper: behavior already present on current maintype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions