7.0 KiB
Dev Report: orchestrator — фикс трекера (дубли сообщений + отставание)
Дата: 2026-06-04 Статус: DONE (PR на ревью, НЕ смержен)
Задача
Живой Telegram-трекер на ET-013 слал НОВОЕ сообщение вместо редактирования одного
(21 editMessageText / 7 sendMessage). Дубли + старый трекер осиротевал и отставал.
Корень: edit_telegram возвращал False на ЛЮБОМ 400 (включая Telegram-ответ
"message is not modified"), а update_task_tracker трактовал False как «edit упал»
→ слал новое сообщение + перезаписывал tracker_message_id.
Сделано
- Правка 1 —
edit_telegram: различимый результат + not_modified=успех - Правка 2 —
update_task_tracker: новое сообщение только приgone - Правка 3 —
render_task_tracker: «попытка N» у активной перезапущенной стадии - +13 тестов (мокают httpx)
- Baseline подтверждён, полный pytest зелёный кроме 9 off-limits
- Ветка
fix/tracker-edit-not-modified, commitec9aa74, PR #22 (НЕ смержен)
Сдача
- PR #22 — admin/orchestrator#22
- Ветка:
fix/tracker-edit-not-modified(от актуального main3e5c74c) - Commit:
ec9aa74 - pytest: 272 passed, 9 failed (те же off-limits HMAC/401 webhook-тесты; baseline был 259 passed + 9 failed → +13 новых тестов, 0 регрессий)
Изменённые файлы
src/notifications.py:edit_telegram(message_id, text) -> str(было-> bool). ВозвращаетEDIT_OK | EDIT_NOT_MODIFIED | EDIT_GONE | EDIT_FAILED. Разборdescriptionиз тела ответа: маркеры not-modified ("message is not modified","exactly the same") →not_modified(лог DEBUG); маркеры gone ("message to edit not found","message can't be edited","message_id_invalid") →gone; прочий 400 / 5xx / исключение →failed.update_task_tracker:ok/not_modified→ выход без send;failed→ лог + выход без send;gone→ send нового +set_tracker_message_id.render_task_tracker: активная стадия = текущая стадия задачи И есть in-flight (незавершённый) ран её агента (либо нет завершённого вообще). Если прогонов агента ≥2 →🔄 Review · попытка N … идёт, иначе старая строка🔄 Review … идёт. Завершённые ✅ строки не тронуты.
tests/test_telegram_tracker.py: обновлены 2 существующих теста под новый контракт edit_telegram (EDIT_OK/EDIT_GONEвместо True/False) + 13 новых.
Каждая из 3 правок
Правка 1 — edit_telegram: not_modified = успех, НЕ дубль
Раньше: return bool(data.get("ok")) → любой 400 = False.
Теперь: при ok:false парсим description и классифицируем:
not_modified (успех) / gone / failed. not_modified логируется на DEBUG.
Правка 2 — fallback на новое сообщение ТОЛЬКО при gone
Раньше: if edit_telegram(...) == False -> send_telegram (новое) + перезапись id.
Теперь:
ok/not_modified→return(ничего не делаем, дубля нет).failed(сеть/таймаут/5xx/неизвестный 400) → лог DEBUG +return(НЕ дубль; трекер дорисуется на следующем переходе).gone→send_telegram(...)+set_tracker_message_id. Стиль fire-and-forget сохранён, функция никогда не падает.
Правка 3 — повторные циклы стадии видимы
У активной перезапущенной стадии показывается 🔄 <Stage> · попытка N … идёт,
где N = число agent_runs этого агента по задаче. Это делает текст уникальным
между циклами review↔development (меньше not-modified) и честно показывает
переработку. Активная стадия определяется как «текущая стадия задачи + есть
in-flight ран её агента» — поэтому just-finished снимок (агент завершил, задача
ещё не сдвинулась) по-прежнему показывает ✅, а не 🔄. Завершённые строки не тронуты.
До/после по логике (когда шлётся новое сообщение)
- До: новое сообщение слалось при ЛЮБОМ не-True результате edit, включая not-modified и временные сбои → дубли + осиротевший отстающий трекер.
- После: новое сообщение слётся ТОЛЬКО при
gone(сообщение реально удалено/слишком старое/невалидный id). not_modified и временные ошибки оставляют существующий трекер на месте.
Тесты (добавлено 13)
- edit_telegram: ok / not_modified / "exactly the same" / not_found→gone / can't-be-edited→gone / unknown400→failed / timeout→failed / 5xx→failed.
- update_task_tracker: not_modified → НЕ зовёт send (id не меняется); gone → зовёт send + обновляет id; failed → НЕ зовёт send (id не меняется).
- render: «попытка 2» при 2 review-ранах; без «попытка N» при 1 ране; завершённые ✅ строки без «попытка N».
- Обновлены под новый контракт: test_second_call_edits_existing_message (EDIT_OK), test_fallback_to_new_message_when_edit_gone (EDIT_GONE).
Проблемы и решения
- На хосте нет
scp/sshpass scp→ файлы переносил черезbase64 | ssh 'base64 -d'. - Первая версия Правки 3 ломала
test_render_omits_model_when_unknown: активная стадия перекрывала ✅ для just-finished рана. Решение: активной считается только стадия с in-flight раном (или вообще без завершённого) — just-finished показывает ✅.
Следующий шаг
PR #22 на ревью у Стрима. Сам НЕ мержу.