auto-sync: 2026-06-03 18:20:01
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
# Dev Report: Конвейер UX (4 фичи)
|
||||
Дата: 2026-06-03
|
||||
Статус: IN PROGRESS
|
||||
Статус: DONE (PR открыт, НЕ мержён, НЕ задеплоен)
|
||||
|
||||
## Задача
|
||||
Реализовать 4 UX-фичи оркестратора в ветке feature/pipeline-ux:
|
||||
4 UX-фичи оркестратора в ветке `feature/pipeline-ux`:
|
||||
1. Старт по статусу (In Progress), не по created
|
||||
2. Вердикт-статусы Approved/Rejected (вариант B), комменты оставить рабочими
|
||||
2. Вердикт-статусы Approved/Rejected (вариант B), комменты остаются рабочими
|
||||
3. Видимость стадий на доске (6 новых UUID статусов)
|
||||
4. Расход токенов по агентам (claude --output-format json, agent_runs +cols, комменты)
|
||||
4. Расход токенов по агентам
|
||||
|
||||
## Реальный формат Plane webhook на смену статуса
|
||||
Из таблицы `events` (event id=160), РЕАЛЬНЫЙ payload:
|
||||
Поймал в таблице `events` (event id=160, 28 issue-событий, есть action=updated):
|
||||
```json
|
||||
{
|
||||
"event": "issue",
|
||||
@@ -18,9 +18,8 @@
|
||||
"data": {
|
||||
"id": "<issue-uuid>",
|
||||
"state": {"id": "<state-uuid>", "name": "In Progress", "group": "started"},
|
||||
"project": "7a79f0a9-...",
|
||||
"sequence_id": 2,
|
||||
...
|
||||
"project": "7a79f0a9-5278-49cd-9007-9a338f238f9c",
|
||||
"sequence_id": 2
|
||||
},
|
||||
"activity": {
|
||||
"field": "state",
|
||||
@@ -30,20 +29,68 @@
|
||||
}
|
||||
}
|
||||
```
|
||||
**Ключ:** `event="issue"`, `action="updated"`, `activity.field == "state"`, новый статус в `data.state.id` (== `activity.new_value`).
|
||||
**Ключ:** `event="issue"`, `action="updated"`, новый статус в **`data.state.id`** (== `activity.new_value`).
|
||||
Также поддержан вариант `data.state` как голая строка-UUID и алиас `work_item.updated`.
|
||||
|
||||
## Сделано
|
||||
- [x] Изучил кодовую базу, baseline 166 passed / 9 pre-existing 401
|
||||
- [x] Поймал реальный payload смены статуса (28 issue-событий, есть action=updated)
|
||||
- [ ] Фича 1
|
||||
- [ ] Фича 2
|
||||
- [ ] Фича 3
|
||||
- [ ] Фича 4
|
||||
- [ ] Тесты
|
||||
- [ ] Push + PR
|
||||
## Таблица: фича → файлы/функции
|
||||
| Фича | Файлы | Ключевые функции |
|
||||
|------|-------|------------------|
|
||||
| 1. Старт по статусу | `src/webhooks/plane.py` | `plane_webhook` (dispatch `issue/updated`), `handle_issue_updated`, `handle_status_start`, `start_pipeline` (вынесено из `handle_work_item_created`), `_qg0_errors`; `handle_work_item_created` → только soft-лог |
|
||||
| 2. Вердикт-статусы | `src/webhooks/plane.py`, `src/plane_sync.py` | `handle_verdict` (Approved→`_try_advance_stage`, Rejected→`_rollback_stage`), `_rollback_stage` (общий с `:rejected:`-коммент-веткой); UUID `approved`/`rejected` в `PLANE_STATES` |
|
||||
| 3. Видимость стадий | `src/plane_sync.py` | `PLANE_STATES` +6 UUID, `STAGE_VISIBILITY_STATE`, `STAGE_TO_STATE` (arch/dev/review/testing → свои статусы), `set_issue_stage_state` |
|
||||
| 4. Токены | `src/agents/launcher.py`, `src/db.py`, `src/usage.py` (новый) | `--output-format json` в claude-cmd; ALTER agent_runs (+input/output/cache_read/cost_usd, идемпотентно); `_monitor_agent` парсит usage; `_post_usage_comments`; `usage.py`: `parse_usage_from_text/log`, `record_usage`, `usage_comment`, `task_summary_comment` |
|
||||
|
||||
## Изменённые файлы
|
||||
(заполняется по ходу)
|
||||
## Идемпотентность / защита handle_comment (Фича 1)
|
||||
`handle_status_start` проверяет `get_task_by_plane_id(plane_id)` — если задача уже есть, лог + return (НЕ дублит, НЕ перезапускает analyst). Поэтому `set_issue_in_progress` из `handle_comment` (approve/ответы) не триггерит повторный старт — задача к тому моменту уже в БД.
|
||||
|
||||
## Следующий шаг
|
||||
Реализация фич.
|
||||
## Token usage — формат коммента (вариант A)
|
||||
Под именем агента (через `add_comment(author=role)`):
|
||||
```
|
||||
💻 Developer готов · 45.2k in / 12.1k out · $0.21
|
||||
```
|
||||
Сводка Deployer на done (вариант B, SUM по agent_runs GROUP BY agent):
|
||||
```
|
||||
📊 Итого по задаче: 1.5k токенов вход / 300 выход · $0.15
|
||||
• Developer: 1.0k in / 200 out · $0.10
|
||||
• Tester: 500 in / 100 out · $0.05
|
||||
```
|
||||
|
||||
## Боевой usage-парс (CLI 2.1.142, реальный вывод)
|
||||
Запустил `claude --print --output-format json "Say hi"` в контейнере, прогнал `parse_usage_from_log`:
|
||||
```
|
||||
PARSED: {'input_tokens': 6, 'output_tokens': 15, 'cache_read_tokens': 18500, 'cost_usd': 0.0560175}
|
||||
AGENT COMMENT: 💻 Developer готов · 6 in / 15 out · $0.06
|
||||
```
|
||||
Реальные поля JSON: `total_cost_usd`, `usage.input_tokens`, `usage.output_tokens`, `usage.cache_read_input_tokens` — подтверждены вживую.
|
||||
|
||||
## Тесты
|
||||
Baseline: 166 passed, 9 pre-existing 401 (test_webhooks.py).
|
||||
**Итог: 190 passed, 9 failed (те же 9 baseline, не тронуты).**
|
||||
Новые (+24):
|
||||
- `tests/test_status_trigger.py` — created не стартует; In Progress стартует analyst; повтор идемпотентен
|
||||
- `tests/test_verdict_status.py` — Approved→advance, Rejected→rollback; комменты `:approved:`/`:rejected:` живы
|
||||
- `tests/test_stage_visibility.py` — UUID в PLANE_STATES; set_issue_stage_state ставит верный UUID; analysis/deploy no-op; Needs Input/In Review/Blocked приоритетны
|
||||
- `tests/test_usage.py` — парс реального CLI-JSON (в т.ч. с текстом перед JSON); NULL при битом JSON; запись; формат токенов k/M; сводка по задаче
|
||||
|
||||
Изменены существующие тесты под новый триггер (created→issue/updated In Progress):
|
||||
`tests/test_m6_sequence.py`, `tests/test_plane_webhook.py`, `tests/test_webhook_dedup.py`.
|
||||
|
||||
## Git
|
||||
- Ветка `feature/pipeline-ux` из свежего main (e9fd305).
|
||||
- 4 коммита (Conventional Commits):
|
||||
- `a4668c0` feat(plane): stage visibility on board + verdict status UUIDs
|
||||
- `09b1c5e` feat(webhook): start pipeline on In Progress status (not on create)
|
||||
- `38a741d` feat(webhook): verdict via Approved/Rejected statuses (variant B)
|
||||
- `9a702a0` feat(metrics): per-agent token/cost accounting
|
||||
- **Remote проверен** (ORCH-7 грабля): `git log origin/main..origin/feature/pipeline-ux` показывает все 4 коммита.
|
||||
- **PR #10** → main: https://git.mva154.duckdns.org/admin/orchestrator/pulls/10
|
||||
|
||||
## Ограничения соблюдены
|
||||
- БД Plane / nginx / openclaw.json / deploy-хук / HMAC / очередь / get_next_work_item_id / M-6 / per-agent authorship — НЕ тронуты (используются).
|
||||
- ORCH-6 резолв проектов НЕ переделан; оставлен TODO(ORCH-10) в PLANE_STATES (статусы per-project).
|
||||
- НЕ деплоил, НЕ мержил — мерж за Стримом после живой проверки.
|
||||
- `.env.bak-*` (стрэй-бэкап) НЕ закоммичен.
|
||||
|
||||
## Замечание для Стрима (живая проверка)
|
||||
- Rejected-вердикт (и `:rejected:` коммент) теперь **перезапускает агента предыдущей стадии** (через `STAGE_AUTHORS[prev_stage]`) — это унифицировано для обоих механизмов (раньше коммент-ветка для non-analysis только откатывала без перезапуска). Так выполнено требование ТЗ «откат + перезапуск агента».
|
||||
- Стадийные статусы видны на доске только в проекте enduro (UUID per-project). При онбординге новых проектов — ORCH-10.
|
||||
|
||||
Reference in New Issue
Block a user