93 lines
7.0 KiB
Markdown
93 lines
7.0 KiB
Markdown
# 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`.
|
||
|
||
## Сделано
|
||
- [x] Правка 1 — `edit_telegram`: различимый результат + not_modified=успех
|
||
- [x] Правка 2 — `update_task_tracker`: новое сообщение только при `gone`
|
||
- [x] Правка 3 — `render_task_tracker`: «попытка N» у активной перезапущенной стадии
|
||
- [x] +13 тестов (мокают httpx)
|
||
- [x] Baseline подтверждён, полный pytest зелёный кроме 9 off-limits
|
||
- [x] Ветка `fix/tracker-edit-not-modified`, commit `ec9aa74`, PR #22 (НЕ смержен)
|
||
|
||
## Сдача
|
||
- **PR #22** — https://git.mva154.duckdns.org/admin/orchestrator/pulls/22
|
||
- Ветка: `fix/tracker-edit-not-modified` (от актуального main `3e5c74c`)
|
||
- 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 на ревью у Стрима. Сам НЕ мержу.
|