diff --git a/memory/2026-06-04.md b/memory/2026-06-04.md index 07b8348..fffe7fa 100644 --- a/memory/2026-06-04.md +++ b/memory/2026-06-04.md @@ -205,3 +205,76 @@ Plane workspace=ag_proj, ET project_id=7a79f0a9-5278-49cd-9007-9a338f238f9c, API - ET-3 Done, ET-8 Backlog не тронуты. - Итог доски ET: 6 Done, 1 Cancelled, 1 Backlog — исторический хвост бага №1 подчищен. - **TODO Славы:** завести новую задачу на загрузку GPS-треков с разных платформ (Wikiloc и др.). + +### ✅ Фикс трекера PR #22 (b222d7af) — дубли + отставание — смержен/задеплоен +Боевой прогон ET-013 вскрыл: трекер слал НОВЫЕ сообщения (21 edit / 7 send). Корень: edit_telegram возвращал False на ЛЮБОМ 400 (включая "message is not modified" — Telegram говорит "текст тот же"), а update_task_tracker трактовал False как фейл → дубль + перезапись tracker_message_id (старый осиротевал). +- Dev opus-4-8, ветка fix/tracker-edit-not-modified, 2 файла +286/-23, +13 тестов. pytest 272 passed / 9 failed (off-limits). +- **Правка 1:** edit_telegram → 4 состояния (ok/not_modified/gone/failed) вместо bool, разбор description. +- **Правка 2:** send нового сообщения ТОЛЬКО при gone (message to edit not found / can't be edited / MESSAGE_ID_INVALID). not_modified/failed → НЕ дублировать. +- **Правка 3:** render показывает `🔄 Review · попытка N … идёт` при ≥2 прогонах стадии (циклы review↔dev видны). +- Задеплоен, health ok. + +### 🐛🔴 НОВЫЙ БАГ (НЕ исправлен): check_deploy_status ищет 14-deploy-log.md не там → ложный FAILED + откат успешного деплоя +**ET-013 (task 31) деплой РЕАЛЬНО прошёл успешно**, но гейт завернул задачу. Разбор run 86 (deployer, exit=0): +- Deployer отчёт SUCCESS: PR #26 merged (be7a052), tag v0.0.5, deploy-hook RC=0, healthcheck HTTP 200, код ET-013 живой на проде (app.js содержит ET-013 ×6). Написал 14-deploy-log.md (deploy_status: SUCCESS) и смержил артефакты через PR #27 (4e925cc). +- НО: `check_deploy_status` читает 14-deploy-log.md в **worktree фичи** (/repos/_wt/enduro-trails/feature_ET-013-z9-z11-z8/docs/work-items/ET-013/) — там есть 10/12/13, а 14-deploy-log.md НЕТ (ушёл в main через PR #27, worktree не подтянул). +- Итог: "Deploy log not found (14-deploy-log.md)" → verdict FAILED → откат deploy→development. Plane ET-013 откатился в development. +- 🟢 **ХОРОШО:** PR #20 merge-gate СРАБОТАЛ — лог "PR merged at deploy stage — done gated by deployer verdict, ignoring merge-driven done". Без него был бы фейк-done. +- 🔴 **ПЛОХО:** рассинхрон путей даёт ложные FAILED на успешных деплоях + лишний цикл development. +- **TODO:** оформить Dev фикс — check_deploy_status должен читать 14-deploy-log.md там, где деплоер его реально кладёт (учесть что артефакты деплоя мержатся в main отдельным PR, worktree фичи их не видит). Возможные варианты: читать из main после deploy-PR, или деплоер пишет лог в worktree фичи ДО мержа артефактов, или гейт fetch'ит main. +- **ET-013 фактически на проде (v0.0.5)** — застряла в development в Plane из-за ложного отката. Решить со Славой: довести руками до Done или ждать фикса гейта. + + +--- + +## 🐛 День багов трекера и деплой-гейта (вечер 04.06) + +Слава гонял живой трекер на боевой ET-013 и нашёл два реальных бага. Оба разобраны до кода. + +### Баг A — трекер плодит дубли сообщений (ИСПРАВЛЕН, PR #22, `b222d7af`, задеплоен) +- Симптом (Слава): «сообщение не одно, а при изменении приходит новое». +- Доказательство на ET-013: 21 editMessageText / 7 sendMessage → 7 дублей. +- Корень: `edit_telegram` (src/notifications.py ~76-97) возвращал `False` на ЛЮБОМ 400, + включая Telegram-ответ `"message is not modified"` (текст трекера не менялся между + переходами, напр. цикл review→development→review). `update_task_tracker` трактовал False + как «edit упал» → слал новое сообщение + перезаписывал tracker_message_id → дубли + + осиротевший отстающий трекер (застрял на снимке Review). +- Фикс (3 правки, PR #22, 272 passed / 9 off-limits, +13 тестов): + 1. `edit_telegram` теперь возвращает `ok|not_modified|gone|failed` (не bool). Разбирает + `description`: "message is not modified"/"exactly the same" → not_modified (успех, не дубль). + 2. `update_task_tracker` шлёт новое сообщение ТОЛЬКО при `gone` (message to edit not found / + can't be edited / MESSAGE_ID_INVALID). ok/not_modified → выход; failed (сеть/таймаут/5xx/ + неизвестный 400) → лог+выход, БЕЗ дубля. + 3. `render_task_tracker`: активная перезапущенная стадия → `🔄 Review · попытка N … идёт` + (N = число прогонов агента этой стадии). Завершённые ✅ строки не тронуты. +- Отчёт: tasks/orchestrator/reports/dev-2026-06-04-tracker-edit-fix.md + +### Баг B — ложный FAILED деплоя из-за рассинхрона путей (ПЕРЕДАН В РАЗРАБОТКУ) +- Контекст: ET-013 РЕАЛЬНО задеплоен (тег v0.0.5, deploy-hook RC=0, healthcheck 200, + фича живая в app.js). НО quality-gate ложно завернул в FAILED → откат deploy→development. +- ХОРОШО (не трогать): merge-gate из PR #20 сработал идеально — лог "PR merged at deploy + stage — done gated by deployer verdict, ignoring merge-driven done". Без него был бы фейк-done. +- Корень: deployer пишет `14-deploy-log.md` (deploy_status: SUCCESS) и мержит артефакты в + **main** через отдельный PR #27 (`4e925cc`). А QG `check_deploy_status` (src/qg/checks.py + ~284) читает `14-deploy-log.md` из **worktree ВЕТКИ ФИЧИ** (/repos/_wt/.../feature_ET-013/ + docs/work-items/ET-013/), где есть 10/12/13, но НЕТ 14 → "Deploy log not found" → FAILED → + лишний откат. **Каждый** успешный деплой так заворачивается. +- ТЗ для Dev: `tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md` + (записать после flush — во время flush write блокирован). Ветка fix/deploy-gate-log-path, + один PR, НЕ мержить сам. Рекоменд. вариант: при отсутствии файла в worktree — fetch + + читать `git show origin/main:docs/work-items//14-deploy-log.md`. Альтернатива: деплоер + пишет лог в worktree фичи до проверки гейтом. НЕ трогать: merge-gate gitea.py, PLANE_STATES, + set_issue_done, launcher:475, HMAC, queue. Baseline pytest 272+9. +- ET-013: Слава сказал «доведи до Done» (код реально на проде) → сделать вручную в Plane. + +### Открытые задачи на след. ход (после flush) +1. Записать ТЗ `tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md` (контент подготовлен выше). +2. Запустить Dev по этому ТЗ (sessions_spawn, agentId dev, opus-4-8). +3. Довести ET-013 до Done в Plane вручную (код на проде, v0.0.5). Plane sequence ET-7? — нет, + ET-013 это work_item; в Plane найти соответствующий issue по названию «z5 треки» = ET-7 + (sequence). Сверить по названию перед сменой статуса. Set → Done. + +### Plane доступ (закреплено в TOOLS.md ранее) +- Web: https://plane.mva154.duckdns.org/ag_proj/projects/7a79f0a9-5278-49cd-9007-9a338f238f9c/issues/ +- API: localhost:8091/api/v1, X-API-Key (ORCH_PLANE_API_TOKEN), workspace ag_proj. +- Plane sequence_id (ET-1..8) ≠ work_item_id орка (ET-0NN) — сопоставлять по названию. diff --git a/tasks/orchestrator/reports/dev-2026-06-04-tracker-edit-fix.md b/tasks/orchestrator/reports/dev-2026-06-04-tracker-edit-fix.md new file mode 100644 index 0000000..d765217 --- /dev/null +++ b/tasks/orchestrator/reports/dev-2026-06-04-tracker-edit-fix.md @@ -0,0 +1,92 @@ +# 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 — повторные циклы стадии видимы +У активной перезапущенной стадии показывается `🔄 · попытка 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 на ревью у Стрима. Сам НЕ мержу.