# 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-модель — не тронуты.