fix(relay): rename sibling auto-threads via prospective_thread_id (not per-chat cache) - #77052
Merged
Merged
Conversation
…chat cache The Discord semantic thread-rename lane resolved the target thread from `_relay_auto_thread_info`, which read a single-slot-per-parent-chat cache (`adapter._auto_thread_by_chat[chat_id]`, populated from connector SendResult feedback). When two auto-threads spawned from the SAME parent channel, the second send overwrote the first's slot and the title turn's read raced the write — so only the FIRST thread in a channel ever got its semantic rename. Staging repro 2026-08-02: message A's thread renamed to "A Hundred Word Sword Story", sibling message B's thread stayed stuck at the raw first-words name. The connector now stamps `prospective_thread_id` on the inbound (the anchor message id, which is the id of the thread it will auto-create) — shipped for per-thread session keying. Reuse it here: it is deterministic and per-message, so it names the EXACT thread even when several auto-threads share one channel. `_relay_auto_thread_info` returns it directly (with an empty initial-name marker) and never consults the collision-prone per-chat cache; the connector's own created-name guard (`prefer_connector_created`) still enforces no-clobber, so no initial name is needed gateway-side. The send-result cache path stays as a fallback for older connectors that don't stamp the field. Tests: two new cases in test_relay_threads.py — prospective id wins over a poisoned cache entry, and two sibling threads in one channel each rename to their own thread id. Full gateway session + relay suites green (211 passed).
benbarclay
enabled auto-merge (squash)
August 2, 2026 19:33
Contributor
૮ >ﻌ< ა ci reviewran on d5b66a6 all good! |
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…chat cache (NousResearch#77052) The Discord semantic thread-rename lane resolved the target thread from `_relay_auto_thread_info`, which read a single-slot-per-parent-chat cache (`adapter._auto_thread_by_chat[chat_id]`, populated from connector SendResult feedback). When two auto-threads spawned from the SAME parent channel, the second send overwrote the first's slot and the title turn's read raced the write — so only the FIRST thread in a channel ever got its semantic rename. Staging repro 2026-08-02: message A's thread renamed to "A Hundred Word Sword Story", sibling message B's thread stayed stuck at the raw first-words name. The connector now stamps `prospective_thread_id` on the inbound (the anchor message id, which is the id of the thread it will auto-create) — shipped for per-thread session keying. Reuse it here: it is deterministic and per-message, so it names the EXACT thread even when several auto-threads share one channel. `_relay_auto_thread_info` returns it directly (with an empty initial-name marker) and never consults the collision-prone per-chat cache; the connector's own created-name guard (`prefer_connector_created`) still enforces no-clobber, so no initial name is needed gateway-side. The send-result cache path stays as a fallback for older connectors that don't stamp the field. Tests: two new cases in test_relay_threads.py — prospective id wins over a poisoned cache entry, and two sibling threads in one channel each rename to their own thread id. Full gateway session + relay suites green (211 passed).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The last of the four original Discord auto-thread symptoms: message B's thread doesn't get its semantic rename. In live staging testing, message A's thread renamed correctly (e.g. "A Hundred Word Sword Story") but a second message B in the same channel created a thread that stayed stuck at its raw first-words name.
Root cause
The gateway's semantic thread-rename lane resolves the target thread via
_relay_auto_thread_info(source), which read a single-slot-per-parent-chat cache —adapter._auto_thread_by_chat[chat_id], populated from the connector'sSendResultfeedback (thread_id+auto_thread_name).When two auto-threads spawn from the same parent channel:
cache[chat] = (threadA, "…")→ A's title turn reads it → A renames ✅cache[chat] = (threadB, "…"), and B's title turn read races the writeSo only the first thread in any channel reliably renamed. This is the same per-channel-vs-per-thread bug class fixed for session keying in the prior change — but one layer down, in the rename lane.
Live evidence (staging, 2026-08-02): both threads created (bot-owned, created-name store entries present), A renamed, B's
discord auto-thread rename: thread=<B>line never fired.Fix
The connector now stamps
prospective_thread_idon the inbound (the anchor message id = the id of the thread it will auto-create), shipped for per-thread session keying. Reuse it in the rename lane: it's deterministic and per-message, so it names the exact thread even when several auto-threads share one channel._relay_auto_thread_infonow returns(prospective_thread_id, "")directly and never consults the collision-prone per-chat cache. The empty initial-name marker defers no-clobber enforcement to the connector's own created-name guard (prefer_connector_created=True), which is keyed per-thread and already in place. The send-result cache path remains as a fallback for older connectors that don't stamp the field — no behavior change there.Scope
Single gateway-side change in
gateway/run.py::_relay_auto_thread_info. No schema, no connector change, no cache-breaking. Backward compatible.Tests
Two new cases in
tests/gateway/relay/test_relay_threads.py:test_relay_auto_thread_info_prefers_prospective_thread_id— prospective id wins over a poisoned cache entry (proves the cache isn't read)test_sibling_threads_in_one_channel_each_rename_to_own_thread— two sibling threads in one channel each rename to their own thread idFull gateway session + relay suites green: 211 passed.
Live verification
Deployed to staging (single current-code instance) and confirmed the full chain earlier: connector stamps → gateway logs
has_prospective=True prospective=<thread_id>. This PR closes the rename-lane gap that remained after session keying landed.