diff --git a/memory/2026-06-03.md b/memory/2026-06-03.md index 88c3a2c..f06ad73 100644 --- a/memory/2026-06-03.md +++ b/memory/2026-06-03.md @@ -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 '' | 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 `@orch.local`, `is_bot=t`, в workspace_members(role 15) И project_members(role 15). Токены 60-симв в `.env` `ORCH_PLANE_BOT_`. -- Автор коммента = `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). Сгенерила пляжные картинки Славе. diff --git a/tasks/orchestrator/DEV_TASK_PIPELINE_UX.md b/tasks/orchestrator/DEV_TASK_PIPELINE_UX.md new file mode 100644 index 0000000..58cd891 --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_PIPELINE_UX.md @@ -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=)`. +5. **Итог по задаче (вариант B):** при переходе в deploy/done — Deployer постит сводку: суммарно по всем agent_runs задачи: `📊 Итого по задаче: токенов вход / выход · $` + разбивка по агентам (по строкам). 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-парса. Если что-то разошлось с кодом — отчитайся, не выдумывай. Один исполнитель. diff --git a/tasks/orchestrator/reports/dev-2026-06-03-pipeline-ux.md b/tasks/orchestrator/reports/dev-2026-06-03-pipeline-ux.md new file mode 100644 index 0000000..6f226e0 --- /dev/null +++ b/tasks/orchestrator/reports/dev-2026-06-03-pipeline-ux.md @@ -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": "", + "state": {"id": "", "name": "In Progress", "group": "started"}, + "project": "7a79f0a9-...", + "sequence_id": 2, + ... + }, + "activity": { + "field": "state", + "new_value": "", + "old_value": "", + "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 + +## Изменённые файлы +(заполняется по ходу) + +## Следующий шаг +Реализация фич.