# План: tool-progress бабблы в тред (Mattermost) > Задача Alex 2026-09-29: «надо починить MM интеграцию: tool calls сыпятся > в основной канал а не в тред». Статус: **ПЛАН, ждёт подтверждения**. ## Симптом Alex пишет `@whale` в треде → **финальный ответ приходит в тред правильно**, а **бабблы tool-progress (🔍/⚙️/emoji + имя тула) падают в корень канала**. Доказательство из `~/.hermes/hermes-whale/logs/gateway.log` (2026-09-29): ``` 13:34:11 inbound message: platform=mattermost user=admin chat=a6nyh77eyjbn5c1owh1i8eebee msg='Почему' 13:35:09 response ready: platform=mattermost chat=a6nyh77eyjbn5c1owh1i8eebee ... response=805 chars ``` Session key: `agent:main:mattermost:channel:a6nyh77eyjbn5c1owh1i8eebee:pcoiddzrqty3ipqtozgcs9ttte` → **тред-часть в session_key есть** (`pcoiddzrqty3ipqtozgcs9ttte`), значит финал адресуется верно. Проблема только в progress-канале. ## Причина (найдена в коде) `gateway/run.py`, строки **17153-17170** — расчёт анкора для progress-бабблов: ```python if source.platform in (Platform.SLACK, Platform.MATTERMOST): _progress_thread_id = source.thread_id or event_message_id else: _progress_thread_id = source.thread_id _progress_metadata = (...) if _progress_thread_id else None _progress_reply_to = ( event_message_id if source.platform in (Platform.FEISHU, Platform.MATTERMOST) and source.thread_id and event_message_id else None ) ``` 🔴 **Ключевая строка — `_progress_reply_to`:** требуется `source.thread_id`. Но `source.thread_id` заполняется **только когда триггер-сообщение само внутри треда**. Когда `@whale` написан в корне канала (`root_id` пустой) — `source.thread_id` = `None` → `_progress_reply_to = None` → `adapter.send(..., reply_to=None)` → **пост уходит новым корневым постом канала**. При этом `_progress_metadata` считается по другой ветке (`thread_id or event_message_id`) и **тоже может получать якорь** — то есть два поля рассинхронизированы: metadata иногда есть, reply_to нет. `_send_progress_text()` (строка 17273) передаёт оба: ```python result = await adapter.send( chat_id=source.chat_id, content=text, reply_to=_progress_reply_to, # ← здесь None metadata=_progress_metadata, ) ``` MM-адаптер в `send()` (строка 290) уважает анкор только так: ```python if reply_to and self._reply_mode == "thread": resolved_root = await self._resolve_root_id(reply_to) payload["root_id"] = resolved_root ``` → при `reply_to=None` ветка не срабатывает вообще. **`MATTERMOST_REPLY_MODE=thread` уже стоит** (`.env`), т.е. адаптер готов — до него не доезжает анкор. ## Сравнение с остальными платформами | Платформа | Правило анкора progress | Ок? | |---|---|---| | Slack | `thread_id or event_message_id` для metadata | так же как MM | | Feishu | `reply_to` только при `thread_id` | тот же дефект | | **Mattermost** | metadata ← `thread_id or event_message_id`, **reply_to ← только `thread_id`** | 🔴 рассинхрон | ## Предлагаемый фикс (1 строка + симметрия) Выровнять `_progress_reply_to` под ту же логику, что metadata: опираться на уже вычисленный `_progress_thread_id`, а не на сырой `source.thread_id`. ```python _progress_reply_to = ( event_message_id if source.platform in (Platform.FEISHU, Platform.MATTERMOST) and _progress_thread_id and event_message_id else None ) ``` **Что меняется:** при посте в корне канала `_progress_thread_id = event_message_id` → `_progress_reply_to = event_message_id` → адаптер резолвит root и кладёт progress-баббл **в тред, созданный от этого поста**. При посте внутри треда поведение не меняется (`source.thread_id` уже непустой). ⚠️ **Не трогаю:** `reply_mode`, `REQUIRE_MENTION`, логику меншн-гейта (патч `9b46ba791a`), `edit_message`-путь (там metadata передаётся отдельно, строка 17243). ## Шаги 1. Бэкап `gateway/run.py` → `backups/-mm-thread-progress/run.py` (+ md5). 2. Правка строки 17166-17170 в `run.py` (скриптом в файле, не sed/inline). 3. `pytest tests/gateway/ -k mattermost` + `tests/gateway/test_stream_events.py` если есть. 4. Прогон шире: `pytest tests/gateway/ -q -o 'addopts='`. 5. Коммит `fix(mattermost): anchor tool-progress bubbles to thread`. 6. **Alex рестартует gateway** (`gateway run --replace` из моей сессии убьёт её же). ## Приёмка (Alex) Пишу `@whale` в корне канала `hermes-maintenance` → **и финальный ответ, и бабблы tool-progress видны в треде** под моим постом, в корне — ничего лишнего. ## Риски | Риск | Оценка | |---|---| | Прогресс уедет в тред там, где Alex ждал в корне | низкий — при `reply_mode=thread` это и есть желаемое | | Поломка Slack/Feishu | низкий — правка в общей ветке, для них `_progress_thread_id` считается иначе; проверить тестами | | Регресс в approval-промптах | нет — у них свой путь (`send_exec_approval`) |