auto-sync: 2026-06-04 13:40:01

This commit is contained in:
Stream
2026-06-04 13:40:01 +03:00
parent 19c82df399
commit 4b9a0f7c6f
4 changed files with 269 additions and 3 deletions

View File

@@ -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/<WI>/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)

View File

@@ -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 <repo_clone> fetch origin main` и прочитать файл из **`origin/main`**
(`git show origin/main:docs/work-items/<WI>/14-deploy-log.md`), распарсить `deploy_status`
оттуда. SUCCESS в main → PASS. Только если нет НИ в worktree, НИ в main → FAILED.
- Это переживает то, что деплоер мержит лог в main отдельным PR (как сейчас и делает).
- Аккуратно с repo clone path: использовать shared clone `settings.repos_dir/<repo>`
для 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, краткое описание фикса.

View File

@@ -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
| Работа | Статус |
|--------|--------|

View File

@@ -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` (стр. 1324):
```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/<repo>/<safe-branch>/docs/work-items/<WI>/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/<repo>` (НЕ worktree),
`git -C <clone> fetch origin main` (timeout 30s) + `git show origin/main:docs/work-items/<WI>/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/<repo>/<safe-branch>/docs/work-items/<WI>/14-deploy-log.md` (worktree фичи; файла нет).
- **Деплоер писал:** `docs/work-items/<WI>/14-deploy-log.md` → мержил в `origin/main` отдельным PR.
- **ПОСЛЕ (читает):** worktree (как раньше) → если нет, fallback `git show origin/main:docs/work-items/<WI>/14-deploy-log.md` на shared-клоне `settings.repos_dir/<repo>` → если нет нигде, «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.