Files
wiki/tasks/orchestrator/DEV_TASK_PIPELINE_UX.md
2026-06-03 18:10:01 +03:00

14 KiB
Raw Blame History

DEV TASK — Конвейер UX: триггер-по-статусу, вердикт-статусы, видимость стадий, расход токенов

Проект: orchestrator | Сервер: slin@82.22.50.71 | Репо: /home/slin/repos/orchestrator | Контейнер: orchestrator (8500) Ветка: feature/pipeline-ux из свежего main (git checkout main && git pull && git checkout -b feature/pipeline-ux).

⚠️ ГРАБЛЯ push (ORCH-7): после push git log origin/main..origin/feature/pipeline-ux ДОЛЖЕН показать коммиты ДО отчёта «PR готов». Токен Gitea для PR — рабочий из env контейнера: docker exec orchestrator printenv ORCH_GITEA_TOKEN (.env-токен может быть 401).

Контекст

Слава ведёт бэклог в Plane и хочет: (1) задача лежит в бэклоге пока он не запустит; (2) одобрение/отклонение — СТАТУСАМИ, не только комментами; (3) видеть на доске какая стадия идёт; (4) видеть расход токенов каждого агента.

ИНФРА УЖЕ ГОТОВА Стримом (НЕ трогай БД Plane): в проекте enduro (7a79f0a9-5278-49cd-9007-9a338f238f9c) созданы статусы с UUID:

architecture = 3020bbb7-6122-4663-930c-0315ba8dfa3d
development   = 9920609b-f140-4e46-ab95-89acda8412c8
review        = ba0d802c-5218-41d4-ab43-978b0ea123ed
testing       = 7855d807-b1bf-42ef-8dae-6cde0df92d02
approved      = a519a341-dada-4a91-8910-7604f82b79c5
rejected      = ba958f3c-5db5-461d-8f82-89425e413b97

Существующие (PLANE_STATES уже в src/plane_sync.py): backlog/todo/in_progress/needs_input/in_review/blocked/done/cancelled.

⚠️ ВАЖНО про реестр проектов (ORCH-6): UUID статусов выше — ТОЛЬКО для проекта enduro. Сейчас PLANE_STATES — глобальный словарь. Для текущей задачи достаточно расширить глобальный PLANE_STATES этими UUID (один проект в проде). Но в коде ОСТАВЬ TODO-коммент: «при онбординге новых проектов статусы должны резолвиться по проекту (см. ORCH-10 в BACKLOG.md)». НЕ переделывай резолв на per-project сейчас — это отдельный эпик.


ФИЧА 1 — Старт конвейера по статусу (не по созданию)

Сейчас (src/webhooks/plane.py:94): work_item.created → сразу handle_work_item_created → enqueue analyst. Бэклог невозможен.

Надо:

  • work_item.created БОЛЬШЕ НЕ запускает конвейер. На создание — только лог (опц. валидация QG-0 можно оставить как «мягкую» проверку, но НЕ создавать ветку/не запускать analyst).
  • Запуск конвейера = переход статуса в In Progress (state changed → in_progress) из бэклог-статуса (Backlog/Todo/Triage).
  • Plane шлёт webhook на изменение issue (event="issue", action="updated" либо work_item.updated) с новым state. Определи фактический формат payload (проверь delivery/payload Plane на проде — см. ниже «как смотреть webhook»). Лови переход на in_progress и вызывай существующую логику создания задачи + запуска analyst (вынеси из handle_work_item_created переиспользуемую функцию start_pipeline(data, project_id) если нужно).
  • Идемпотентность: если задача для этого plane_id УЖЕ есть в БД (tasks) — повторный переход в in_progress НЕ создаёт дубль и НЕ перезапускает analyst (просто лог). insert_event_dedup уже есть — используй.

⚠️ Грабля: не сломай существующий handle_comment (он тоже дёргает set_issue_in_progress при approve/ответах — это НЕ должно триггерить повторный старт; защита через «задача уже существует»).

ФИЧА 2 — Вердикт статусами (вариант B): Approved / Rejected

Сейчас: approve/reject только через комменты :approved:/:rejected: в handle_comment.

Надо ДОБАВИТЬ (комменты ОСТАВИТЬ рабочими — оба механизма):

  • Перевод issue в статус Approved = то же, что коммент :approved: → QG текущей стадии → advance. Переиспользуй _try_advance_stage.
  • Перевод в статус Rejected = то же, что :rejected: → откат на предыдущую стадию + перезапуск агента. Переиспользуй существующую rollback-ветку.
  • ⚠️ Для Rejected причина не приходит со статусом → агенту передавай «Reason: (rejected via status, see latest comment)». Слава причину пишет отдельным комментом (это его процесс).
  • После обработки вердикта верни issue в рабочий статус стадии (Approved→ статус следующей стадии; Rejected→ статус предыдущей) — см. Фича 3 (set_issue по стадии).

ФИЧА 3 — Видимость стадий на доске

Сейчас: статус меняется только на in_review/needs_input/in_progress/blocked/done. Стадии architecture/development/review/testing невидимы (всё In Progress).

Надо:

  • Расширить PLANE_STATES 6 новыми UUID (выше).
  • Маппинг стадия→статус, напр. STAGE_TO_STATE = {"architecture":"architecture","development":"development","review":"review","testing":"testing"} (analysis остаётся как есть — in_progress/in_review/needs_input по текущей логике).
  • При входе в стадию (в advance_stage / при enqueue агента стадии) дёргать set_issue_state(work_item_id, STAGE_TO_STATE[stage]). Добавь хелпер set_issue_stage_state(work_item_id, stage, project_id=None) в plane_sync по образцу set_issue_in_review.
  • На доске должно быть видно: задача физически переезжает Architecture→Development→Review→Testing→Done.
  • ⚠️ НЕ ломай Needs Input / In Review / Blocked — они приоритетнее (если агент задал вопрос — статус Needs Input, а не Development).

ФИЧА 4 — Расход токенов по агентам

CLI подтверждён вживую: Claude Code 2.1.142, --output-format json отдаёт single-result JSON с полями: total_cost_usd, usage.input_tokens, usage.output_tokens, usage.cache_read_input_tokens, usage.cache_creation_input_tokens, modelUsage, num_turns, duration_ms.

Надо:

  1. launcher.py: в claude-команду (строка ~212) добавить --output-format json. ⚠️ Тогда stdout = ОДИН JSON в конце (не текст). Проверь что мониторинг/парсинг лога не ломается (сейчас лог парсится на ошибки в _monitor_agent). JSON писать в лог как сейчас, плюс распарсить usage по завершении.
  2. db.py: ALTER/добавить в agent_runs колонки: input_tokens INTEGER, output_tokens INTEGER, cache_read_tokens INTEGER, cost_usd REAL. Сделать через идемпотентную миграцию (try ALTER TABLE ADD COLUMN, ignore if exists — как принято в проекте; глянь как делались прошлые миграции схемы).
  3. По завершении run'а (_monitor_agent / где exit_code пишется): прочитать output_path, распарсить последний JSON-объект, извлечь usage+cost, записать в agent_runs.
    • ⚠️ Если JSON не распарсился (агент упал/таймаут) — записать NULL/0, НЕ падать. Логировать warning.
  4. Коммент в Plane (вариант A): при завершении агента постить в задачу под именем этого агента: например 💻 Developer готов · 45.2k in / 12.1k out · $0.21 (форматируй токены k/M, cost 2 знака). Используй существующий add_comment(..., author=<role>).
  5. Итог по задаче (вариант B): при переходе в deploy/done — Deployer постит сводку: суммарно по всем agent_runs задачи: 📊 Итого по задаче: <N> токенов вход / <M> выход · $<total> + разбивка по агентам (по строкам). SQL: SELECT agent, SUM(...) FROM agent_runs WHERE task_id=? GROUP BY agent.
  6. (опц., если просто) эндпоинт GET /tasks/{work_item_id}/usage — JSON со сводкой. Не обязательно, но плюс.

Ограничения

  • 🚫 НЕ трогай: БД Plane (статусы/боты уже созданы — только ЧИТАЙ UUID), nginx, openclaw.json, deploy-хук, HMAC/verify_plane_signature, очередь (queue_worker/jobs — кроме чтения), ORCH-6 резолв проектов (оставь TODO, не переделывай), get_next_work_item_id, M-6, per-agent authorship код (используй add_comment(author=), не ломай).
  • Комменты :approved:/:rejected: ОСТАЮТСЯ рабочими (оба механизма параллельно).
  • Baseline: 166 passed, 9 pre-existing 401 — не чинить/не ломать.
  • Conventional Commits, отдельные коммиты по фичам (feat(webhook) старт-по-статусу, feat(webhook) вердикт-статусы, feat(plane) видимость стадий, feat(metrics) токены).

Как смотреть формат Plane webhook (для Фич 1,2)

  • Прод принимает на /webhook/plane. Реальные payload'ы есть в таблице дедупликации событий (insert_event_dedup пишет body). Посмотри последние: docker exec orchestrator python3 -c "import sqlite3;[print(r) for r in sqlite3.connect('/app/data/orchestrator.db').execute('SELECT source,event_type,substr(payload,1,400) FROM webhook_events ORDER BY id DESC LIMIT 5')]" (уточни имя таблицы/колонок в db.py).
  • Если событий обновления статуса нет — попроси Стрим перевести тестовую задачу между статусами в Plane, чтобы поймать payload. Не выдумывай структуру.

Тесты (контейнер)

IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q

  • tests/test_status_trigger.py: created НЕ запускает; переход→in_progress запускает; повторный переход не дублирует.
  • tests/test_verdict_status.py: Approved→advance; Rejected→rollback; комменты по-прежнему работают.
  • tests/test_stage_visibility.py: вход в стадию → set правильного state UUID; Needs Input приоритетнее.
  • tests/test_usage.py: парсинг usage из примера CLI-JSON; запись в agent_runs; коммент с форматом токенов; сводка по задаче. Мокать httpx/subprocess.

Проверка (Стрим проверит вживую)

# Что Критерий
1 старт по статусу created не стартует; Backlog→In Progress стартует analyst; нет дублей
2 вердикт-статусы Approved=advance, Rejected=rollback; комменты живы
3 стадии на доске задача переезжает по колонкам Architecture→…→Testing; Needs Input/Blocked приоритетны
4 токены agent_runs пишет токены+cost; коммент агента с расходом; сводка Deployer
5 тесты 166 → 166+новые passed, 9 baseline не тронуты
6 git PR в main, remote содержит коммиты

Отчёт

  • НЕ деплоить, НЕ мержить (мерж — Стрим после живой проверки). Запушь ветку (ПРОВЕРЬ remote!), PR в main.
  • В отчёте: какой РЕАЛЬНЫЙ формат Plane-webhook на обновление статуса ты нашёл (event/action/где state); таблица «фича → файлы/функции»; пример строки usage-коммента; вывод одного boevoго usage-парса. Если что-то разошлось с кодом — отчитайся, не выдумывай. Один исполнитель.