diff --git a/tasks/orchestrator/reports/dev-2026-06-04-observability-fixes.md b/tasks/orchestrator/reports/dev-2026-06-04-observability-fixes.md new file mode 100644 index 0000000..cb21259 --- /dev/null +++ b/tasks/orchestrator/reports/dev-2026-06-04-observability-fixes.md @@ -0,0 +1,85 @@ +# Dev Report: orchestrator — 4 правки наблюдаемости + вторая дверь баг-8 +Дата: 2026-06-04 +Статус: DONE + +## Задача +Одна ветка `fix/observability-and-merge-gate`, один PR. 4 правки: +1. КРИТ: merge-вебхук при `current_stage=="deploy"` НЕ ставит done (gated by deployer verdict). +2. КРИТ: токены — показывать полный вход (input + cache_read + cache_creation), хранить cache_creation. +3. deploy→done success → Plane в терминальный Done. +4. Все агенты прикладывают ссылку на свой артефакт в финиш-коммент. + +## Результат — сдача +- **PR: #20** — https://git.mva154.duckdns.org/admin/orchestrator/pulls/20 (open, НЕ смержен) +- **Ветка:** `fix/observability-and-merge-gate` (от актуального main `2629dff`) +- **Commit:** `61e26a8` +- **pytest (вся сюита):** `244 passed, 9 failed` + - Baseline был 227 passed + 10 failed. Добавлено **17 новых тестов** (227→244). + - 9 failed = подмножество off-limits HMAC/401 группы. 10-й off-limits тест + (`test_plane_webhook_generates_sequential_ids`) падает только в изоляции, + а в полной сюите проходит из-за порядка/состояния DB — это pre-existing + флак внутри off-limits группы, не связан с правками. Ни один НЕ off-limits + тест не падает. + +## Что изменилось в каждой правке + +### Правка 1 — `src/webhooks/gitea.py` (merge-gate, баг-8 вторая дверь) +В ветке `action=="closed" and pr.get("merged")` добавлен ранний `return` при +`current_stage == "deploy"` (только лог, без update_task_stage / advance_stage — +чтобы не было гонки). Для всех остальных стадий поведение merge→done сохранено. +deployer мержит PR в начале работы → merge-вебхук больше не фейк-завершает задачу; +done на deploy решает только `check_deploy_status` через `advance_stage`. + +### Правка 2 — `src/usage.py` + `src/db.py` (полный вход токенов) +- `db.py`: idempotent миграция `_ensure_column(agent_runs, "cache_creation_tokens", "INTEGER")`. +- `parse_usage_from_text`: добавлен `cache_creation_tokens` из + `cache_creation_input_tokens`. +- `record_usage`: пишет новую колонку. +- Новый `fmt_in()`: `in_total = input + cache_read + cache_creation`, + `cached = cache_read + cache_creation`. Формат `8.5M in (8.4M cached)`; + при cached==0 — просто `45.2k in` (обратно совместимо). +- `usage_comment`: использует `fmt_in()`. +- `task_usage_summary`: `total_in = SUM(input+cache_read+cache_creation)`, + отдельно `total_cached`, per_agent теперь 5-кортежи; COALESCE(...,0) для NULL. +- `task_summary_comment`: показывает ` вход ( cached) / выход · `. +- `cost_usd` НЕ тронут. + +### Правка 3 — `src/stage_engine.py` + `src/plane_sync.py` (Plane→Done) +- `plane_sync.py`: новый `set_issue_done()` → `_set_issue_state_direct(PLANE_STATES["done"])` + (маппинг PLANE_STATES НЕ менялся). +- `stage_engine.advance_stage`: в Advance-блоке при `next_stage == "done"` и наличии + work_item_id вызывается `set_issue_done(work_item_id)` (в try/except). Теперь + успешный deploy→done гарантированно доводит Plane до терминального Done + (раньше застревал на In Progress, т.к. merge-вебхук обходил флоу). + +### Правка 4 — `src/usage.py` + `src/agents/launcher.py` (ссылки на артефакты) +- `usage.py`: `usage_comment` принимает опц. `repo/branch/work_item_id/pr_number`; + новая `artifact_links()` строит markdown-ссылки через `gitea_public_url` + (fallback gitea_url): reviewer→12-review.md, tester→13-test-report.md, + deployer→14-deploy-log.md, architect→06-adr/, developer→PR+branch. + Отсутствующий контекст/артефакт → ссылка пропускается, не падает. analyst не тронут. +- `launcher._post_usage_comments`: прокидывает repo/branch/work_item_id; для developer + резолвит номер открытого PR (`_open_pr_number`). + +## Изменённые файлы +- `src/webhooks/gitea.py` — merge-gate на deploy +- `src/db.py` — миграция cache_creation_tokens +- `src/usage.py` — парсинг/формат/summary/artifact_links +- `src/plane_sync.py` — set_issue_done +- `src/stage_engine.py` — терминальный Plane Done на deploy→done +- `src/agents/launcher.py` — контекст ссылок + _open_pr_number +- `tests/test_usage.py` — +12 тестов (cache_creation, формат cached/без, summary, ссылки) +- `tests/test_webhooks.py` — +2 теста (merge на deploy→не done; merge не-deploy→done) +- `tests/test_stage_engine.py` — +2 теста (deploy→done форсит Plane Done; не-терминал не форсит) + +## Проблемы и решения +- pytest нельзя запустить из репо напрямую (pycache принадлежит root, нет venv). + Решение: запуск внутри контейнера `orchestrator` из смонтированного `/repos/orchestrator` + с `PYTHONDONTWRITEBYTECODE=1 -p no:cacheprovider`. +- `.env.bak-*` файлы в репо untracked — НЕ добавлял в коммит (staged только 9 целевых файлов). +- Существующий тест `test_usage_comment_format` остался зелёным: при cache=0 + `in_total == input_tokens`, формат `45.2k in` без скобки — обратная совместимость сохранена. + +## Не сделано (по ТЗ) +- PR НЕ смержен — оставлен на ревью Стрим (PR #20, open). +- PLANE_STATES маппинг, HMAC, gitea_public_url конфиг, conftest.py, status-only verdict-модель — не тронуты.