auto-sync: 2026-06-04 11:10:01
This commit is contained in:
152
tasks/orchestrator/DEV_TASK_OBSERVABILITY_FIXES.md
Normal file
152
tasks/orchestrator/DEV_TASK_OBSERVABILITY_FIXES.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# DEV TASK: orchestrator — 4 правки (merge-gate, токены, Plane→Done, артефакты)
|
||||||
|
|
||||||
|
Все правки в репо **orchestrator** на проде: `slin@82.22.50.71:/home/slin/repos/orchestrator`.
|
||||||
|
Прямой push в main запрещён (pre-receive hook) → **только через PR в Gitea**.
|
||||||
|
Gitea токен: `docker exec orchestrator printenv ORCH_GITEA_TOKEN`.
|
||||||
|
Одна ветка `fix/observability-and-merge-gate` от актуального main, **один PR** со всеми 4 правками.
|
||||||
|
Baseline pytest: **227 passed + 10 failed** (10 failed = off-limits: 9 HMAC/401 + 1 webhook-POST — НЕ чинить).
|
||||||
|
После всех правок: passed должно вырасти (новые тесты), те же 10 failed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ПРАВКА 1 (критичная): вторая дверь баг-8 — gitea merge обходит deploy-QG
|
||||||
|
|
||||||
|
**Файл:** `src/webhooks/gitea.py`
|
||||||
|
|
||||||
|
**Проблема:** ветка `action=="closed" and pr.get("merged", False)` слепо ставит `done`
|
||||||
|
для ЛЮБОЙ стадии. deployer мержит PR в начале работы → webhook merged прилетает за ~30 сек →
|
||||||
|
задача `done` ПОКА deployer ещё работает → его вердикт `deploy_status: FAILED` игнорируется,
|
||||||
|
`check_deploy_status` не вызывается. Фейк-done.
|
||||||
|
|
||||||
|
**Текущий код (≈стр 336-339):**
|
||||||
|
```python
|
||||||
|
elif action == "closed" and pr.get("merged", False):
|
||||||
|
update_task_stage(task_id, "done")
|
||||||
|
notify_stage_change(task_id, current_stage, "done")
|
||||||
|
logger.info(f"Task {task_id}: PR merged, stage → done")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Нужно:** при `current_stage == "deploy"` merge-вебхук НЕ должен ставить done напрямую —
|
||||||
|
done на deploy решает ТОЛЬКО `check_deploy_status` (через `advance_stage` при завершении
|
||||||
|
deployer-job). Для остальных стадий — поведение merge сохранить.
|
||||||
|
```python
|
||||||
|
elif action == "closed" and pr.get("merged", False):
|
||||||
|
# БАГ 8 (вторая дверь): на стадии deploy done гейтится вердиктом deployer'а
|
||||||
|
# (check_deploy_status), а НЕ фактом merge PR — deployer мержит PR в начале
|
||||||
|
# работы, merge != успешный деплой. advance_stage отработает при завершении job.
|
||||||
|
if current_stage == "deploy":
|
||||||
|
logger.info(
|
||||||
|
f"Task {task_id}: PR merged at deploy stage — done gated by deployer "
|
||||||
|
f"verdict (check_deploy_status), ignoring merge-driven done."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
update_task_stage(task_id, "done")
|
||||||
|
notify_stage_change(task_id, current_stage, "done")
|
||||||
|
logger.info(f"Task {task_id}: PR merged, stage → done")
|
||||||
|
```
|
||||||
|
**НЕ дублировать advance_stage из вебхука — только молчаливый return на deploy** (иначе гонка).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ПРАВКА 2 (критичная): учёт токенов — показывать ПОЛНЫЙ вход (cache_read теряется)
|
||||||
|
|
||||||
|
**Файлы:** `src/usage.py` (+ миграция БД для cache_creation, если решишь хранить).
|
||||||
|
|
||||||
|
**Проблема:** Claude CLI `--output-format json` отдаёт `usage.input_tokens` = только
|
||||||
|
НЕкэшированный свежий вход (~20-80 токенов). 99.99% реального входа в
|
||||||
|
`cache_read_input_tokens` (1-8 МЛН) + `cache_creation_input_tokens`. Сейчас:
|
||||||
|
- `parse_usage_from_text` сохраняет `input_tokens` и `cache_read_tokens` отдельно, но
|
||||||
|
**`cache_creation_input_tokens` НЕ парсится/НЕ хранится** → теряется.
|
||||||
|
- `usage_comment` показывает ТОЛЬКО `fmt_tokens(input_tokens)` → "21 in" при реальных 1.1М.
|
||||||
|
- `task_usage_summary` агрегирует `SUM(input_tokens)` → "221 токенов вход" на всю задачу. Абсурд.
|
||||||
|
|
||||||
|
**Что сделать:**
|
||||||
|
1. **Парсинг** (`parse_usage_from_text`, возвращаемый dict): добавить
|
||||||
|
`cache_creation_tokens = _int(usage.get("cache_creation_input_tokens"))`.
|
||||||
|
2. **БД**: добавить колонку `cache_creation_tokens INTEGER` в `agent_runs`
|
||||||
|
(idempotent миграция: `ALTER TABLE ... ADD COLUMN` в try/except, как делаются
|
||||||
|
существующие миграции в проекте — найди где инициализируется схема, например
|
||||||
|
`db.py`/`migrations`; НЕ ломай существующую схему). `record_usage` пишет это поле.
|
||||||
|
3. **Отображение** `usage_comment` — формат:
|
||||||
|
```
|
||||||
|
💻 Developer готов · 8.5M in (8.4M cached) / 45.8k out · $7.29
|
||||||
|
```
|
||||||
|
где `in_total = input_tokens + cache_read_tokens + cache_creation_tokens`,
|
||||||
|
а `cached = cache_read_tokens + cache_creation_tokens`. Если cached==0 — показывать
|
||||||
|
просто `8.5M in / 45.8k out` (без скобки).
|
||||||
|
4. **`task_usage_summary`**: агрегировать `total_in` как
|
||||||
|
`SUM(input_tokens + cache_read_tokens + cache_creation_tokens)`, плюс отдельно
|
||||||
|
`total_cached = SUM(cache_read_tokens + cache_creation_tokens)`.
|
||||||
|
`task_summary_comment` показывает: `📊 Итого по задаче: <total_in> вход (<cached> cached) / <out> выход · <cost>`.
|
||||||
|
5. **Существующие строки** в БД (с NULL в cache_creation) — COALESCE(...,0), не падать.
|
||||||
|
|
||||||
|
**НЕ трогать** расчёт `cost_usd` — он берётся из `total_cost_usd` CLI и корректен.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ПРАВКА 3: статус Plane должен доезжать до `Done`
|
||||||
|
|
||||||
|
**Файлы:** найти где после успешного deploy задача завершается (`stage_engine.advance_stage`
|
||||||
|
deploy→done success-ветка) и где синхронизируется Plane-state (`plane_sync` / `plane_notify_stage`).
|
||||||
|
|
||||||
|
**Проблема:** ET-012 в Plane застрял на `In Progress` (последний переход `Testing → In Progress`),
|
||||||
|
финального Plane-state `Done` нет. Done проставился только в очереди оркестратора (из-за
|
||||||
|
правки 1 раньше merge-вебхук вообще обходил флоу). После правки 1 deploy→done пойдёт через
|
||||||
|
`check_deploy_status` SUCCESS-ветку — на этом переходе нужно синхронизировать Plane-state в `Done`.
|
||||||
|
|
||||||
|
**Что сделать:** в success-ветке deploy→done (когда `check_deploy_status` вернул True)
|
||||||
|
вызвать обновление Plane-state в финальный `Done`/`Completed` (использовать существующий
|
||||||
|
маппинг PLANE_STATES — НЕ менять сам маппинг, только убедиться что финальный переход
|
||||||
|
вызывается). Проверь по `PLANE_STATES`, как называется терминальный стейт, и что
|
||||||
|
`plane_notify_stage(work_item_id, "deploy", "done")` доводит до него.
|
||||||
|
**Ограничение:** НЕ менять PLANE_STATES маппинг и status-only verdict-модель.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ПРАВКА 4: все агенты прикладывают ссылки на артефакты (не только analyst)
|
||||||
|
|
||||||
|
**Файлы:** где формируются per-agent finish-комменты (`usage_comment` в usage.py и/или
|
||||||
|
место вызова в launcher/stage_engine, где постится коммент в Plane после агента).
|
||||||
|
|
||||||
|
**Проблема:** только analyst прикладывает ссылки на доки. Architect/developer/reviewer/
|
||||||
|
tester/deployer постят только токены, без ссылок на свои артефакты.
|
||||||
|
|
||||||
|
**Что сделать:** к финиш-комменту каждого агента добавить ссылку(и) на его артефакт(ы) в Gitea
|
||||||
|
(использовать `gitea_public_url` — он уже есть для кликабельных ссылок, см. PR #14).
|
||||||
|
Маппинг агент → артефакт (путь в репо `docs/work-items/{work_item_id}/`):
|
||||||
|
- **architect** → ADR-файлы `docs/work-items/{wid}/06-adr/*.md` (или design-док стадии architecture)
|
||||||
|
- **developer** → ссылка на PR (номер PR уже известен в флоу) + branch
|
||||||
|
- **reviewer** → `docs/work-items/{wid}/12-review.md`
|
||||||
|
- **tester** → `docs/work-items/{wid}/13-test-report.md`
|
||||||
|
- **deployer** → `docs/work-items/{wid}/14-deploy-log.md`
|
||||||
|
- **analyst** → как сейчас (не ломать).
|
||||||
|
|
||||||
|
Формат ссылки — как у analyst сейчас (markdown-ссылка на файл в Gitea через gitea_public_url:
|
||||||
|
`http://git.../admin/{repo}/src/branch/{branch}/docs/work-items/{wid}/12-review.md`).
|
||||||
|
Если конкретный артефакт-файл для агента отсутствует — ссылку для него пропустить (не падать).
|
||||||
|
**Ограничение:** НЕ менять формат коммента analyst, НЕ трогать gitea_public_url конфиг.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ОГРАНИЧЕНИЯ (общие, НЕ трогать)
|
||||||
|
- HMAC, project-filter (`get_project_by_repo`), nginx, openclaw.json, .env, queue (кроме чтения),
|
||||||
|
PLANE_STATES маппинг, conftest.py, status-only verdict-модель, gitea_public_url конфиг.
|
||||||
|
- Сохранить блок launcher.py:475.
|
||||||
|
- НЕ менять ветки `action=="reviewed"` (APPROVED/REQUEST_CHANGES) в gitea.py.
|
||||||
|
- Стадии кроме deploy в gitea.py merge-обработчике — поведение сохранить.
|
||||||
|
|
||||||
|
## ТЕСТЫ (обязательно)
|
||||||
|
- **Правка 1:** 2 теста (merge на deploy → НЕ done; merge на не-deploy → done).
|
||||||
|
- **Правка 2:** тесты парсинга cache_creation; usage_comment с cached/без; task_usage_summary
|
||||||
|
суммирует все три компонента входа.
|
||||||
|
- **Правка 3:** тест/проверка что success deploy→done доводит Plane до терминала (мок plane_sync).
|
||||||
|
- **Правка 4:** тест что usage_comment/finish-коммент содержит ссылку на артефакт для
|
||||||
|
reviewer/tester/deployer (мок).
|
||||||
|
- Полный `pytest` зелёный (кроме тех же 10 off-limits).
|
||||||
|
|
||||||
|
## СДАЧА
|
||||||
|
- Ветка `fix/observability-and-merge-gate` от актуального main, один PR в Gitea.
|
||||||
|
- НЕ мержить PR сам — оставить на ревью Стрим.
|
||||||
|
- Отчёт: `tasks/orchestrator/reports/dev-2026-06-04-observability-fixes.md` —
|
||||||
|
commit-хеш, номер PR, вывод pytest (passed/failed), краткий diff по каждой из 4 правок.
|
||||||
|
- Сообщить: номер PR + результат pytest + что именно изменилось в каждой правке.
|
||||||
Reference in New Issue
Block a user