2f09df5615
When a Discord channel message initiates a relay auto-thread, the thread does not exist at ingest (source.thread_id is None) — the connector creates it on its FIRST send and auto-threads any outbound carrying the reply anchor. The final reply carries that anchor, so it lands in the thread. But the tool-progress / status bubbles (the "Searching the web for..." updates and the streaming preamble) were sent with _progress_metadata=None and _progress_reply_to=None: _resolve_progress_thread_id returns None for Discord (only slack/mattermost get a synthetic thread), so the progress send had no anchor and the connector posted it FLAT in the parent channel. Result: the search-status updates leaked outside the thread while the answer threaded (staging repro 2026-08-02). The connector now stamps prospective_thread_id on the inbound (the anchor message id == the id of the thread it will create). Reuse it: when a relay-delivered Discord channel-initiate carries prospective_thread_id and has no real thread yet, carry the reply anchor (event_message_id) on both the progress metadata (reply_to_message_id) and the progress reply_to, so the connector routes the progress bubble into the SAME auto-thread as the final reply. Applied to both the tool-progress path (_progress_metadata / _progress_reply_to) and the status/interim callback path (_status_thread_metadata). Events already arriving in a real thread, DMs, and non-relay sources are untouched (guarded on delivered_via_upstream_relay + prospective_thread_id + not thread_id). Tests: two new cases in test_run_progress_topics.py — a relay Discord channel-initiate asserts every progress send carries the anchor (reply_to + metadata.reply_to_message_id + non_conversational), and an event already in a real thread asserts the synthetic-anchor path does NOT engage. Full gateway progress + relay + session suites green (228 passed).