auto-sync: 2026-06-03 18:10:01

This commit is contained in:
Stream
2026-06-03 18:10:01 +03:00
parent 9a2484d376
commit 69b0e323b8
3 changed files with 183 additions and 26 deletions

View File

@@ -2,35 +2,38 @@
---
## Plane per-agent authorship — ЗАВЕРШЕНО И ПОДТВЕРЖДЕНО (03.06)
## Конвейер UX — статусная модель + токены (03.06 день, в работе)
### PR #9 `e9fd3052` ЗАМЕРЖЕН + прод пересобран
- Код: `config.py` (7 bot-полей), `plane_sync.py` (`PLANE_BOT_TOKENS`, `STAGE_AUTHORS`, `_headers_for(author)` fallback, `add_comment(author=)` POST под токеном бота), точки вызова прокидывают author. GET/PATCH под общим токеном (не тронуты). 166 passed (158+8), 9 baseline.
- ⚠️ Config-патч ОБЯЗАТЕЛЕН: образ уже содержит `ORCH_PLANE_BOT_*` env → без полей в config pydantic падает `extra_forbidden`. Прод поднялся → патч сработал.
### Решения Славы по статусной модели (ПОДТВЕРЖДЕНЫ):
- **Бэклог в Plane.** Задача лежит в Backlog/Todo/Triage пока Слава не переведёт в **In Progress** → только это запускает конвейер (analyst). Сейчас триггер = work_item.created (любой тикет сразу стартует) — НАДО починить.
- **Вердикт статусами (вариант B):** статус **Approved** = advance, **Rejected** = rollback. Комменты `:approved:`/`:rejected:` ОСТАВИТЬ параллельно. Причину reject Слава пишет комментом.
- **Стадии видимы на доске:** architecture/development/review/testing — отдельными статусами, не скрыты в In Progress.
- **Расход токенов по агентам:** показывать (A) в комменте каждого агента + (B) итог-сводка от Deployer.
### 🔑 ГЛАВНЫЙ УРОК: 403 → project_members
- Боты были в **workspace_members**, но комментить можно только **project_members**. POST коммента давал **403 Forbidden** (аутентификация ок, прав нет).
- Фикс: добавила 7 ботов в `project_members` проекта `7a79f0a9-...` (role 15, member). Бэкап `plane-projmembers-backup-*`.
- ⚠️ NOT NULL `member_id` — без него INSERT падал (транзакция откатывалась молча). Правильный INSERT: id, role, **member_id**, project_id, workspace_id, view_props/default_props/preferences='{}', sort_order=65535, is_active, created_at, updated_at.
### Механика конвейера (по факту кода, для справки):
- Все 6 агентов работают над ОДНОЙ задачей = 1 тикет Plane = 1 feature-ветка, комментят в неё (теперь под своими именами).
- Статус сейчас меняется только: in_review (analyst ждёт approve), needs_input (вопросы), blocked, in_progress, done. Стадии architecture/dev/review/testing невидимы.
- 2 ручных гейта: analyst→ждёт :approved: (In Review); reviewer→:approved:. Остальное едет автоматически.
- Needs Input: analyst пишет `01-questions.md`, статус Needs Input, ждёт. Слава отвечает обычным комментом → analyst перезапуск. Лимит 3 раунда → Blocked + Telegram-алерт.
- Агенты = **Claude CLI** (`/opt/claude-code/bin/claude.exe --print`), вывод в лог. Версия 2.1.142.
### ✅ БОЕВАЯ ПРОВЕРКА ПРОЙДЕНА (proof из БД)
- Каждый из 7 ботов запостил коммент на живую ET-009 (`64e98247-...`) → **HTTP 201**.
- `issue_comments JOIN users ON actor_id` показал авторов: 🧠 Analyst / 🏗️ Architect / 💻 Developer / 🔍 Reviewer / 🧪 Tester / 🚀 Deployer / 🌊 Стрим — НЕ mva154.
- Тестовые комменты убраны (soft-delete `deleted_at`, UPDATE 7 → 0 живых).
### ИНФРА сделана Стримом (БД Plane, бэкап states `plane-states-backup-*`):
6 новых статусов в проекте enduro (`7a79f0a9-...`), группа started, в правильном порядке доски:
```
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
```
### ⚠️ ГРАБЛЯ shell/psql (потеряла время — на будущее)
- heredoc через `ssh '...'` с вложенными кавычками/скобками в python/SQL — постоянно ломается (глотает вывод, `(` unexpected, кавычки съедаются).
- psql `-v var=...` без кавычек = идентификатор (column "font" does not exist).
- `scp` в контейнере НЕТ.
- **РАБОЧИЙ способ передать скрипт на сервер:** `write` локально → `base64 -w0``echo '<b64>' | base64 -d > /tmp/x` на сервере → `docker cp``psql -f`. Надёжно, без экранирования.
### CLI usage подтверждён вживую (для фичи токенов):
`claude --output-format json` отдаёт: `total_cost_usd`, `usage.{input_tokens,output_tokens,cache_read_input_tokens,cache_creation_input_tokens}`, `modelUsage`, `num_turns`, `duration_ms`. Сейчас запуск plain-text (`--print` без формата) → usage НЕ собирается. agent_runs (id,task_id,agent,started/finished,exit_code,output_path) — НЕТ полей токенов, надо ALTER.
### Plane identity — справочник
- БД Plane: контейнер `plane-app-plane-db-1`, user `plane`, пароль = `docker exec plane-app-api-1 printenv POSTGRES_PASSWORD`.
- Workspace `ag_proj` = `903e12e8-65c9-40a0-a7f6-0186f8af42d4`. Главный проект enduro = `7a79f0a9-5278-49cd-9007-9a338f238f9c`.
- 7 ботов: email `<role>@orch.local`, `is_bot=t`, в workspace_members(role 15) И project_members(role 15). Токены 60-симв в `.env` `ORCH_PLANE_BOT_<ROLE>`.
- Автор коммента = `issue_comments.actor_id` = владелец токена. Роли: ANALYST/ARCHITECT/DEVELOPER/REVIEWER/TESTER/DEPLOYER/STREAM.
- ⚠️ При создании НОВЫХ проектов в Plane — ботов надо добавлять в project_members нового проекта, иначе 403. (Кандидат на автоматизацию: при ORCH-6 resolve проекта — авто-добавление ботов.)
### Код — Dev, ветка `feature/pipeline-ux`, ТЗ `DEV_TASK_PIPELINE_UX.md` (4 фичи):
1. Старт по статусу (in_progress), не по created. Идемпотентно (задача уже есть → не дублить).
2. Вердикт-статусы Approved/Rejected (+ комменты живы).
3. Видимость стадий: PLANE_STATES += 6 UUID, set_issue по стадии (Needs Input/Blocked приоритетны).
4. Токены: `--output-format json`, ALTER agent_runs (+input/output/cache/cost), коммент агента + сводка Deployer.
- ⚠️ Статусы per-project — ОСТАВИТЬ TODO (ORCH-10 эпик), не переделывать резолв сейчас.
- Baseline 166 passed. Мерж — Стрим после живой проверки.
### ИТОГ дня 03.06
Закрыто: ORCH-1..7, L1L2L3, M-6, Plane per-agent authorship (Вариант A, боты). Всё в проде, проверено вживую.
Открыто (НЕ срочно): авто-добавление ботов в project_members при новом проекте.
### Recraft (попутно, 03.06): модель OpenRouter = `recraft/recraft-v4.1` (НЕ в каталоге /models, но работает). Дёргать chat/completions с `modalities:["image"]` (только image, без text — иначе 404). Сгенерила пляжные картинки Славе.

View File

@@ -0,0 +1,105 @@
# 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-парса. Если что-то разошлось с кодом — отчитайся, не выдумывай. Один исполнитель.

View File

@@ -0,0 +1,49 @@
# Dev Report: Конвейер UX (4 фичи)
Дата: 2026-06-03
Статус: IN PROGRESS
## Задача
Реализовать 4 UX-фичи оркестратора в ветке feature/pipeline-ux:
1. Старт по статусу (In Progress), не по created
2. Вердикт-статусы Approved/Rejected (вариант B), комменты оставить рабочими
3. Видимость стадий на доске (6 новых UUID статусов)
4. Расход токенов по агентам (claude --output-format json, agent_runs +cols, комменты)
## Реальный формат Plane webhook на смену статуса
Из таблицы `events` (event id=160), РЕАЛЬНЫЙ payload:
```json
{
"event": "issue",
"action": "updated",
"data": {
"id": "<issue-uuid>",
"state": {"id": "<state-uuid>", "name": "In Progress", "group": "started"},
"project": "7a79f0a9-...",
"sequence_id": 2,
...
},
"activity": {
"field": "state",
"new_value": "<new-state-uuid>",
"old_value": "<old-state-uuid>",
"actor": {...}
}
}
```
**Ключ:** `event="issue"`, `action="updated"`, `activity.field == "state"`, новый статус в `data.state.id` (== `activity.new_value`).
## Сделано
- [x] Изучил кодовую базу, baseline 166 passed / 9 pre-existing 401
- [x] Поймал реальный payload смены статуса (28 issue-событий, есть action=updated)
- [ ] Фича 1
- [ ] Фича 2
- [ ] Фича 3
- [ ] Фича 4
- [ ] Тесты
- [ ] Push + PR
## Изменённые файлы
(заполняется по ходу)
## Следующий шаг
Реализация фич.