From 4b9a0f7c6fc55e0acf2ca4cb850326b83e65dcd7 Mon Sep 17 00:00:00 2001 From: Stream Date: Thu, 4 Jun 2026 13:40:01 +0300 Subject: [PATCH] auto-sync: 2026-06-04 13:40:01 --- tasks/orchestrator/BACKLOG.md | 17 ++- .../DEV_TASK_DEPLOY_GATE_PATH_FIX.md | 96 ++++++++++++++ tasks/orchestrator/STATUS.md | 34 ++++- .../dev-2026-06-04-deploy-gate-path-fix.md | 125 ++++++++++++++++++ 4 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md create mode 100644 tasks/orchestrator/reports/dev-2026-06-04-deploy-gate-path-fix.md diff --git a/tasks/orchestrator/BACKLOG.md b/tasks/orchestrator/BACKLOG.md index 75c10b1..556942d 100644 --- a/tasks/orchestrator/BACKLOG.md +++ b/tasks/orchestrator/BACKLOG.md @@ -2,7 +2,22 @@ --- type: backlog -updated_at: 2026-06-03 +updated_at: 2026-06-04 +--- + +## 🐛 Баги (2026-06-04, живой прогон ET-013) + +### ✅ BUG-DEPLOY-LOG-PATH · ложный FAILED деплоя (рассинхрон путей 14-deploy-log.md) · СТАТУС: ✅ ИСПРАВЛЕН (PR #23, `34894f46`, на проде) +- **Приоритет:** высокий (бьёт по каждому успешному деплою — ложный откат). +- **Симптом (ET-013):** деплой РЕАЛЬНО успешен (тег v0.0.5, deploy-hook RC=0, healthcheck HTTP 200, фича живая в app.js), но QG завернул в FAILED → откат deploy→development. +- **Корень:** deployer пишет `14-deploy-log.md` (deploy_status: SUCCESS) и мержит артефакты в **main** через отдельный PR (#27, `4e925cc`). QG `check_deploy_status` (src/qg/checks.py ~284) читает файл через `_repo_path(repo, branch)` из **worktree ВЕТКИ ФИЧИ** (`/repos/_wt/.../feature_ET-013/docs/work-items/ET-013/`), где есть 10/12/13, но нет 14 → «Deploy log not found» → FAILED. +- **НЕ трогать:** merge-gate из PR #20 (`gitea.py`, `current_stage==deploy`) — он СРАБОТАЛ ПРАВИЛЬНО (без него фейк-done). +- **Решение (PR #23):** новые `_deploy_log_from_main` + `_parse_deploy_status` в qg/checks.py. Порядок: worktree → `git fetch origin main` + `git show origin/main:docs/work-items//14-deploy-log.md` на shared-клоне → not found. Любая git-ошибка → деградация (None), таймауты 30s/15s. 277 passed / 9 off-limits. merge-gate gitea.py НЕ тронут. +- **Источники:** `memory/2026-06-04.md`, `STATUS.md` (раздел багов), `tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md`. + +### ✅ BUG-TRACKER-DUP · трекер плодит дубли · СТАТУС: ✅ ИСПРАВЛЕН (PR #22, `b222d7af`) +- См. STATUS.md «Баг A» и `memory/2026-06-04.md`. `edit_telegram` возвращал False на `"message is not modified"` → дубли. Фикс: 4 состояния (ok/not_modified/gone/failed), send только при gone. + --- ## EPIC: E2E онбординг нового проекта (03.06.2026) diff --git a/tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md b/tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md new file mode 100644 index 0000000..cf15cde --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_DEPLOY_GATE_PATH_FIX.md @@ -0,0 +1,96 @@ +# DEV TASK: orchestrator — ложный FAILED деплоя (рассинхрон путей 14-deploy-log.md) + +Репо: `slin@82.22.50.71:/home/slin/repos/orchestrator` (пароль `motoZ@yaz2010`). +Push в main запрещён (pre-receive hook) → **только PR в Gitea**. +Gitea токен: `docker exec orchestrator printenv ORCH_GITEA_TOKEN`. +Одна ветка `fix/deploy-gate-log-path` от актуального main, **один PR**. НЕ мержить сам. +Baseline pytest на этом проде: **272 passed + 9 failed** (9 = off-limits HMAC/401, НЕ чинить). + +--- + +## КОНТЕКСТ / БАГ (подтверждён на живой задаче ET-013, 2026-06-04) + +ET-013 **реально и успешно задеплоен на прод** (тег `v0.0.5`, deploy-hook RC=0, +healthcheck HTTP 200, фича живая в `app.js`). НО quality-gate ложно завернул задачу +в `FAILED` и откатил `deploy → development`. + +### Что произошло по шагам +1. Deployer отработал, `exit_code=0`, смержил PR #26 (`be7a052`), запушил тег `v0.0.5`, + deploy-hook RC=0, healthcheck 200. +2. Deployer написал `14-deploy-log.md` с `deploy_status: SUCCESS` и смержил артефакты + через **отдельный PR #27** (`4e925cc`) — то есть файл уехал в **main**. +3. QG `check_deploy_status` (`src/qg/checks.py` ~284) читает `14-deploy-log.md` из + **worktree ВЕТКИ ФИЧИ** через `_repo_path(repo, branch)` → + `/repos/_wt/<...>/feature_ET-013/docs/work-items/ET-013/`. + В этом worktree лежат `10-tech-risks`, `12-review`, `13-test-report`, но **НЕТ + `14-deploy-log.md`** — он в main, а не в worktree фичи. +4. Файла нет → «Deploy log not found» → вердикт **FAILED** → откат `deploy → development`. + +### Корень +Артефакты деплоя пишутся/мержатся в **main** (PR #27), а гейт проверки деплоя читает их +из **worktree ветки фичи** (`_repo_path(repo, branch)`). Локации не совпадают → ложный +not-found → ложный FAILED → лишний откат успешного деплоя. **Каждый** успешный деплой +будет так заворачиваться. + +### Хорошая новость (НЕ трогать, это работает правильно) +merge-gate из PR #20 (`gitea.py`, `current_stage=="deploy"` → игнор merge-driven done) +сработал идеально: лог `"PR merged at deploy stage — done gated by deployer verdict, +ignoring merge-driven done"`. Без него merge PR'а проставил бы фейк-done. Теперь done +гейтится вердиктом деплоера — это правильно. Чиним только рассинхрон путей под этим гейтом. + +--- + +## ШАГ 0 — ПОДТВЕРДИТЬ ПО КОДУ (перед правкой) +1. Открой `src/qg/checks.py`, функцию `check_deploy_status` (~стр. 284) и `_repo_path` + (~стр. 13). Зафиксируй ТОЧНО: как формируется путь к `14-deploy-log.md` + (`_repo_path(repo, branch)` → worktree фичи), имя/паттерн файла, трактовку отсутствия + файла (текст «Deploy log not found» → FAILED). +2. Найди инструкцию/промпт деплоера (где он пишет `14-deploy-log.md` и через какой PR + мержит артефакты в main; искать в `src/agents/launcher.py` и любых промпт-строках). + Подтверди: пишет в main через отдельный PR, а не оставляет в worktree фичи. +3. Зафиксируй ОБА факта в отчёте (verbatim строки путей до/после). + +## ЧТО СДЕЛАТЬ (выбрать robust-вариант, обосновать в отчёте) + +Цель: `check_deploy_status` должен находить `14-deploy-log.md`, который реально написан +деплоером, и НЕ заворачивать успешный деплой. + +**Рекомендуемый вариант (robust, минимум побочек):** +- В `check_deploy_status`: если `14-deploy-log.md` НЕ найден в worktree-локации — + сделать `git -C fetch origin main` и прочитать файл из **`origin/main`** + (`git show origin/main:docs/work-items//14-deploy-log.md`), распарсить `deploy_status` + оттуда. SUCCESS в main → PASS. Только если нет НИ в worktree, НИ в main → FAILED. +- Это переживает то, что деплоер мержит лог в main отдельным PR (как сейчас и делает). +- Аккуратно с repo clone path: использовать shared clone `settings.repos_dir/` + для git-команд (не worktree фичи). Тайм-аут на fetch, не падать — при ошибке fetch + деградировать к «нет в main» (а не к исключению). + +**Альтернатива (если по коду чище):** заставить деплоера писать `14-deploy-log.md` в ту же +worktree-локацию ветки фичи (где лежат `10/12/13`) ДО проверки гейтом, а не только в main. +Тогда гейт находит его там же. Выбрать, если это меньше меняет и не ломает merge артефактов. + +Выбрать ОДИН вариант, обосновать. Требование: успешный деплой с реально написанным +`SUCCESS`-логом больше НЕ заворачивается в FAILED; реальный FAILED по-прежнему ловится. + +### НЕ ТРОГАТЬ +- merge-gate `gitea.py` (`current_stage=="deploy"`), PLANE_STATES, set_issue_done, + launcher deployer-guard (launcher.py:475), HMAC, queue, usage_comment/Plane-комменты, + cost_usd, формат трекера/Итого, миграции. Только логика поиска лога деплоя + (опц.) + локация записи лога деплоером. + +## ТЕСТЫ (обязательно, мокать git/файлы) +- `check_deploy_status`: лог `SUCCESS` есть в worktree → PASS (регресс не сломан). +- `check_deploy_status`: лога нет в worktree, но `SUCCESS` есть в `origin/main` → PASS + (это и есть фикс — кейс ET-013). +- `check_deploy_status`: лог `FAILED` в main → FAILED. +- `check_deploy_status`: лога нет нигде (ни worktree, ни main) → FAILED («not found»). +- `check_deploy_status`: fetch падает (сеть) → не исключение, деградирует к «нет в main». +- Полный pytest зелёный кроме тех же 9 off-limits. + +## СДАЧА +- Ветка `fix/deploy-gate-log-path`, один PR в Gitea. НЕ мержить — на ревью Стрим. +- Отчёт: `tasks/orchestrator/reports/dev-2026-06-04-deploy-gate-path-fix.md` — + commit-хеш, PR-номер, вывод pytest, verbatim путь до/после (где гейт читал и где деплоер + писал), какой вариант выбран и почему, как теперь находится лог. +- Перенос файлов на прод: на хосте нет scp → через `base64 | ssh 'base64 -d'` либо docker cp. +- Сообщить: PR-номер, результат pytest, краткое описание фикса. diff --git a/tasks/orchestrator/STATUS.md b/tasks/orchestrator/STATUS.md index 9f22ddd..778e1ad 100644 --- a/tasks/orchestrator/STATUS.md +++ b/tasks/orchestrator/STATUS.md @@ -1,11 +1,41 @@ # Статус проекта: Мультиагентная разработка ПО -**Дата обновления:** 2026-06-03 20:50 UTC +**Дата обновления:** 2026-06-04 10:35 UTC **Ревьюер:** Стрим --- -## 📌 Свежее (2026-06-03) — сессия багов входа/выхода analyst +## 📌 Свежее (2026-06-04) — observability, живой ТГ-трекер, баги на боевом ET-013 + +| Работа | Статус | +|--------|--------| +| **PR #20** observability: токены (input+cache_read+cache_creation, `15.1M in`), merge-gate `current_stage==deploy`, Plane→Done (`set_issue_done`), артефакты от всех агентов | 🟢 смержен (`2801983d`), на проде | +| **PR #21** живой Telegram-трекер B+ (одно editMessageText-сообщение, умные пинги только на алерты) | 🟢 смержен (`3e5c74ce`), на проде | +| **PR #22** фикс трекера: дубли (`not modified`=успех, send только при `gone`) + `🔄 Review · попытка N` | 🟢 смержен (`b222d7af`), на проде | +| **PR #23** фикс деплой-гейта (ложный FAILED, рассинхрон путей `14-deploy-log.md`; fallback на `origin/main`) | 🟢 смержен (`34894f46`), на проде | +| **Доска ET синхронизирована** (6 Done + ET-8 Done вручную + ET-4 Cancelled) | 🟢 сделано | + +**Детали:** `🐛 Известные баги` ниже + `memory/2026-06-04.md` + reports `dev-2026-06-04-*`. + +--- + +## 🐛 Известные баги (живой трекер + деплой, 2026-06-04) + +### ✅ Баг A — трекер плодит дубли сообщений (ИСПРАВЛЕН, PR #22) +- **Симптом:** вместо одного редактируемого сообщения — новые на каждом цикле. На ET-013: 21 editMessageText / 7 sendMessage = 7 дублей. +- **Корень:** `edit_telegram` (src/notifications.py) возвращал `False` на ЛЮБОМ 400, включая Telegram-ответ `"message is not modified"` (текст трекера не менялся). `update_task_tracker` трактовал False как «edit упал» → новое сообщение + перезапись tracker_message_id (старый трекер осиротевал, отставал). +- **Фикс (PR #22, `b222d7af`):** `edit_telegram` → `ok|not_modified|gone|failed`. Новое сообщение ТОЛЬКО при `gone`. `render_task_tracker` показывает `🔄 Review · попытка N` на повторных циклах. 272 passed / 9 off-limits. + +### ✅ Баг B — ложный FAILED деплоя (РАССИНХРОН ПУТЕЙ, ИСПРАВЛЕН PR #23) +- **Симптом:** ET-013 РЕАЛЬНО задеплоен (тег v0.0.5, deploy-hook RC=0, healthcheck 200, фича живая), но QG завернул в FAILED → откат deploy→development. +- **Корень:** deployer пишет `14-deploy-log.md` (deploy_status: SUCCESS) и мержит артефакты в **main** через отдельный PR (#27). А QG `check_deploy_status` (src/qg/checks.py ~284) читает этот файл через `_repo_path(repo, branch)` из **worktree ветки фичи**, где его нет → «not found» → ложный FAILED. Бьёт по КАЖДОМУ успешному деплою. +- **Важно:** merge-gate из PR #20 (`current_stage==deploy`) СРАБОТАЛ ПРАВИЛЬНО (без него был бы фейк-done) — НЕ трогать. +- **Фикс (PR #23, `34894f46`, на проде):** новые `_deploy_log_from_main` + `_parse_deploy_status` в qg/checks.py. Порядок поиска: worktree → `git fetch origin main` + `git show origin/main:.../14-deploy-log.md` на shared-клоне → not found. Любая git-ошибка → деградация (None, не исключение), таймауты 30s/15s. 277 passed / 9 off-limits. merge-gate gitea.py НЕ тронут. +- **ET-013 (=Plane ET-8)** доведена до Done вручную (код реально на проде) + коммент с объяснением. + +--- + +## 📌 Предыдущее (2026-06-03) — сессия багов входа/выхода analyst | Работа | Статус | |--------|--------| diff --git a/tasks/orchestrator/reports/dev-2026-06-04-deploy-gate-path-fix.md b/tasks/orchestrator/reports/dev-2026-06-04-deploy-gate-path-fix.md new file mode 100644 index 0000000..774c883 --- /dev/null +++ b/tasks/orchestrator/reports/dev-2026-06-04-deploy-gate-path-fix.md @@ -0,0 +1,125 @@ +# Dev Report: orchestrator — ложный FAILED деплоя (рассинхрон путей 14-deploy-log.md) +Дата: 2026-06-04 +Статус: DONE + +## Задача +QG `check_deploy_status` ложно заворачивал успешные деплои в FAILED и откатывал +`deploy → development`. Причина: deployer пишет `14-deploy-log.md` (deploy_status: SUCCESS) +и мержит артефакты в **main** отдельным PR, а гейт читает лог из **worktree ветки фичи** +через `_repo_path(repo, branch)` — там лога нет → «Deploy log not found» → FAILED. +Починить ТОЛЬКО поиск лога деплоя. НЕ трогать merge-gate (`gitea.py`, `current_stage=="deploy"`). + +## ШАГ 0 — подтверждение по коду (verbatim) + +### Где гейт читал лог (ДО фикса) — `src/qg/checks.py` +`_repo_path` (стр. 13–24): +```python +def _repo_path(repo: str, branch: str | None = None) -> str: + if branch: + wt = get_worktree_path(repo, branch) + if os.path.isdir(wt): + return wt + return os.path.join(settings.repos_dir, repo) +``` +`check_deploy_status` (стр. ~284, ДО): +```python +repo_path = _repo_path(repo, branch) +log_path = os.path.join(repo_path, f"docs/work-items/{work_item_id}/14-deploy-log.md") +if not os.path.isfile(log_path): + return False, "Deploy log not found (14-deploy-log.md)" +``` +`get_worktree_path` (`src/git_worktree.py:33`): +```python +def get_worktree_path(repo: str, branch: str) -> str: + return os.path.join(settings.worktrees_dir, repo, _safe(branch)) +``` +→ при наличии ветки путь = `/repos/_wt///docs/work-items//14-deploy-log.md` +(worktree ФИЧИ). Отсутствие файла → текст «Deploy log not found (14-deploy-log.md)» → FAILED. + +### Где деплоер пишет лог (verbatim подтверждение) +- `src/usage.py:328`: `"deployer": ("Deploy log", "14-deploy-log.md")` — артефакт деплоера. +- Промпт деплоера лежит в целевом репо (`.openclaw/agents/deployer.md` в enduro-trails), + не в orchestrator. Лог пишется и **мержится в main отдельным PR** (для ET-013 — PR #27, + commit `4e925cc`). +- **Живое подтверждение в проде:** в shared-клоне `/home/slin/repos/enduro-trails`: + ``` + $ git show origin/main:docs/work-items/ET-013/14-deploy-log.md + --- + deploy_status: SUCCESS + version: v0.0.5 + work_item: ET-013 + pr: 26 + merge_commit: be7a052 + ``` + При этом в worktree ветки фичи файла нет → отсюда ложный FAILED. + +**Рассинхрон подтверждён:** пишется/мержится в `origin/main`, читается из worktree фичи. + +## Выбранный вариант: РЕКОМЕНДУЕМЫЙ (robust, origin/main fallback) + +Выбран вариант с чтением из `origin/main`, а не «деплоер пишет в worktree», потому что: +1. Меняет только логику чтения в `check_deploy_status` — ноль изменений в пайплайне + деплоера/мержа артефактов (меньше риска, ничего нового не ломает). +2. Переживает текущее поведение «деплоер мержит лог в main отдельным PR» как есть. +3. Не зависит от состояния worktree (может быть зачищен/переаллоцирован к моменту гейта). +4. Альтернатива (писать в worktree ДО гейта) потребовала бы менять промпт деплоера в + целевом репо + порядок merge артефактов — больше поверхности изменений. + +### Логика (ПОСЛЕ фикса) — порядок поиска: worktree → origin/main → not found +```python +repo_path = _repo_path(repo, branch) +log_path = os.path.join(repo_path, f"docs/work-items/{work_item_id}/14-deploy-log.md") +if os.path.isfile(log_path): + ...read & _parse_deploy_status(content) # регресс сохранён +main_content = _deploy_log_from_main(repo, work_item_id) # git fetch origin main + git show +if main_content is not None: + return _parse_deploy_status(main_content) # ET-013 fix: SUCCESS в main → PASS +return False, "Deploy log not found (14-deploy-log.md)" +``` +`_deploy_log_from_main`: использует shared-клон `settings.repos_dir/` (НЕ worktree), +`git -C fetch origin main` (timeout 30s) + `git show origin/main:docs/work-items//14-deploy-log.md` +(timeout 15s). Любая ошибка git (нет клона / нет `.git` / сеть / нет файла в main) → +возвращает `None` (деградация к «not found»), **никогда не бросает исключение**. +Парсинг frontmatter вынесен в `_parse_deploy_status` (читает только `deploy_status:`). + +## Изменённые файлы +- `src/qg/checks.py` — `import subprocess`; новые `_parse_deploy_status`, + `_deploy_log_from_main`; `check_deploy_status` с origin/main-fallback. +- `tests/test_qg.py` — 5 новых тестов в `TestCheckDeployStatus`. + +## Тесты +- worktree `SUCCESS` → PASS (регресс не сломан) — `test_success_verdict_passes` (+short-circuit тест). +- нет в worktree, `SUCCESS` в origin/main → PASS (**ET-013 fix**) — `test_origin_main_success_passes_when_absent_in_worktree`. +- `FAILED` в main → FAILED — `test_origin_main_failed_fails`. +- нет нигде → not found — `test_absent_everywhere_fails`. +- fetch падает (TimeoutExpired) → деградация без исключения — `test_fetch_failure_degrades_no_exception`. +- worktree-лог короткозамыкает origin/main lookup — `test_worktree_log_short_circuits_main_lookup`. + +### Вывод pytest (на проде, в контейнере orchestrator против `/repos/orchestrator`) +- `tests/test_qg.py`: **33 passed** (было 28 + 5 новых). +- Полный suite (детерминированно, `-p no:randomly`): **277 passed, 9 failed**. + - 9 failed = те же off-limits HMAC/401 в `test_webhooks.py` (НЕ чинить). + - Baseline на чистом дереве: **272 passed, 9 failed**. Дельта = +5 (мои тесты), 0 новых падений. + - Списки FAILED у baseline и у фикса **идентичны** (сравнил отсортированно). + - Прим.: один прогон без `no:randomly` дал «10 failed/276 passed» — это flaky-ordering + в 401-webhook-тестах, не связано с фиксом (детерминированный прогон стабильно 9+277). + +## Результат +- Ветка `fix/deploy-gate-log-path` от актуального origin/main. +- Commit: `4e4cc6c`. +- **PR #23** в Gitea: https://git.mva154.duckdns.org/admin/orchestrator/pulls/23 (НЕ смержен, на ревью). +- Push в main НЕ делал. merge-gate `gitea.py` НЕ тронут. Off-limits (PLANE_STATES, + set_issue_done, launcher:475, HMAC, queue, usage_comment, cost_usd, трекер, миграции) — не тронуты. +- Живая валидация ET-013: `_deploy_log_from_main("enduro-trails","ET-013")` фетчит и + читает `deploy_status: SUCCESS` из origin/main → теперь PASS (раньше был ложный FAILED). + +## Путь до/после (verbatim) +- **ДО (читал):** `_repo_path(repo, branch)` → `/repos/_wt///docs/work-items//14-deploy-log.md` (worktree фичи; файла нет). +- **Деплоер писал:** `docs/work-items//14-deploy-log.md` → мержил в `origin/main` отдельным PR. +- **ПОСЛЕ (читает):** worktree (как раньше) → если нет, fallback `git show origin/main:docs/work-items//14-deploy-log.md` на shared-клоне `settings.repos_dir/` → если нет нигде, «not found». + +## Проблемы и решения +- Тесты не в образе контейнера (в `/app` запечён только `src/`). Прогонял в контейнере + против смонтированного host-репо: `docker exec orchestrator sh -c 'cd /repos/orchestrator && python -m pytest ...'`. +- Кажущийся +1 fail в одном прогоне — flaky-ordering 401-webhook тестов; подтверждено + детерминированным `-p no:randomly` (стабильно 9 failed) и идентичными списками FAILED.