18 KiB
Конвейер UX — статусная модель + токены (03.06 день, в работе)
Решения Славы по статусной модели (ПОДТВЕРЖДЕНЫ):
- Бэклог в 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.
Механика конвейера (по факту кода, для справки):
- Все 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.
ИНФРА сделана Стримом (БД 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
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.
Код — Dev, ветка feature/pipeline-ux, ТЗ DEV_TASK_PIPELINE_UX.md (4 фичи):
- Старт по статусу (in_progress), не по created. Идемпотентно (задача уже есть → не дублить).
- Вердикт-статусы Approved/Rejected (+ комменты живы).
- Видимость стадий: PLANE_STATES += 6 UUID, set_issue по стадии (Needs Input/Blocked приоритетны).
- Токены:
--output-format json, ALTER agent_runs (+input/output/cache/cost), коммент агента + сводка Deployer.
- ⚠️ Статусы per-project — ОСТАВИТЬ TODO (ORCH-10 эпик), не переделывать резолв сейчас.
- Baseline 166 passed. Мерж — Стрим после живой проверки.
Recraft (попутно, 03.06): модель OpenRouter = recraft/recraft-v4.1 (НЕ в каталоге /models, но работает). Дёргать chat/completions с modalities:["image"] (только image, без text — иначе 404). Сгенерила пляжные картинки Славе.
PR #10 СМЕРЖЕН в main (03.06, вечер) — конвейер UX
Merge commit main = 4773137. Проверено вживую перед мержем (не на слово Dev):
- 5 коммитов на remote:
a4668c0feat(plane) stage visibility+verdict UUIDs,09b1c5efeat(webhook) старт на In Progress,38a741dfeat(webhook) вердикт Approved/Rejected (вариант B),9a702a0feat(metrics) токены/cost per-agent,7fd6529test(conftest) глушилка Telegram. - Прогон тестов сама: 190 passed, 9 failed. Девять — pre-existing baseline (сравнила полную сюиту на main ДО ветки = те же 9). Regression = 0. Из девяти: 8 это 401-invalid-signature, 1 это
test_plane_approvedTypeError — ВСЕ падали и на main. ⚠️ Уточнение к прошлым заметкам: реальный baseline = 9 (не только 401-набор). - Telegram-утечка закрыта:
tests/conftest.pyautouse-фикстура глушитsend_telegramво всех тестах. Sentinel-проверка: 0 запросов в api.telegram.org за прогон. Покрыты модули: notifications (источник), stage_engine (module-level import — критичный), plane/launcher/queue_worker/main (local imports, raising=False). Источник шума «🔄 ET-100» = тестtest_webhook_dedupгонялся на проде без мока.
✅ ЗАДЕПЛОЕНО (03.06 ~16:10 UTC)
Деплой = rebuild образа + recreate (код COPY в образ из src/, НЕ bind-mount; data/ — bind-mount, переживает). Команда: git checkout main && git pull && docker compose up -d --build. Бэкап БД: data/orchestrator.db.bak-1780503005.
- ⚠️ Перед сборкой репо был на
feature/pipeline-ux— обязательноgit checkout main(иначе соберётся ветка). - Контейнер
Up, uvicorn:8500, queue worker стартовал, ошибок нет. Образ на4773137. - Миграция agent_runs прошла: добавились
input_tokens, output_tokens, cache_read_tokens, cost_usd. БД цела (11 задач).
Боевые тесты статусной модели на проде (вживую, HMAC-подписанные webhook):
- ✅ ТЕСТ 1 (created НЕ стартует):
work_item.created→ HTTP 200, задач создано 0, только soft QG-0 warning в лог. Бэклог-режим работает. - ✅ ТЕСТ 2b (idempotency): In Progress для существующей задачи (f9009756) → лог
task already exists (stage=analysis), not restarting, 11→11 задач. Защита handle_comment работает. - ⏳ ТЕСТ 2a (позитивный старт) не делала: запустит реальный analyst (токены + ветка в Gitea). Спросила Славу прежде чем плодить мусор.
- ⚠️ Грабля: UUID In Progress =
b873d9eb-993c-48cd-97ac-99a9b1623967. State UUID извлекается изdata.state.id(или bare string). - HMAC секрет:
ORCH_PLANE_WEBHOOK_SECRET(len 40). Подпись:printf %s BODY | openssl dgst -sha256 -hmac SECRET, заголовокX-Plane-Signature.
После деплоя — боевой тест (план):
Завести задачу в Backlog → перевести в In Progress (должен стартовать analyst, НЕ на created) → провести по доске (видеть переезд по колонкам Architecture→…→Testing) → показать Славе токены живьём (коммент агента 💻 Developer · Nk in / Mk out · $X + сводка Deployer). Боевой usage-тест на ветке уже дал 💻 Developer готов · 6 in / 15 out · $0.06.
Формат Plane-webhook на смену статуса (Dev нашёл по реальному payload):
event="issue", action="updated", новый статус в data.state.id. Старт ловит переход в In Progress из бэклог-статуса. Идемпотентность: задача уже в БД tasks → не дублить/не перезапускать.
Первый штатный тикет в Plane после фичи-1 (03.06 16:56)
- Тикет создан штатно в Backlog (не в обход!) — фича-1 наконец позволяет: создание НЕ запускает конвейер.
- ET-? seq=6, id
bfb4866c-4b69-4f97-916b-e7233d8259de, name «Скачивание трека из popup на карте (enduro-trails)», state=Backlog (113b24f6-...). - ✅ Проверено: 0 задач в оркестраторе, конвейер не стартанул. Бэклог работает в бою.
- Суть фичи (запрос Славы): тап по треку на карте → в popup с инфо кнопка «Скачать» → отдать файл трека (GPX обяз., KML опц.). Источник: пользовательские треки на карте (enduro-trails). Нужен бэкенд-эндпоинт отдачи трека по id + Content-Disposition.
- Создание issue через Plane REST: токен
ORCH_PLANE_API_TOKEN(len 60, общий orchestrator-токен),POST http://localhost:8091/api/v1/workspaces/ag_proj/projects/<proj>/issues/, заголовокX-API-Key, тело{name, state, description_html}. Backlog UUID =113b24f6-cce8-4be9-9a22-a359b9cf0122.
Боевой запуск #6 (ET-006) — баги вскрыты, чистка сделана (03.06 ~18:00)
Что РАБОТАЕТ (доказано в бою):
- ✅ Триггер по статусу In Progress (после фикса webhook URL)
- ✅ Токены считаются: run 59 analyst → 27 in / 36851 out / $2.13 записаны в agent_runs
- ✅ Per-agent authorship: комменты под именем analyst
- ✅ Webhook URL фикс:
http://172.21.0.1:8500/webhook/plane(Plane не резолвит DuckDNS — docker-сеть изолирована; старый внешний URL давал 500). UPDATE webhooks SET url в БД Plane сделан.
БАГИ (для Dev):
- issue.updated без description → QG-0 падает в Blocked. Plane на смену статуса шлёт только изменённые поля,
description/description_strippedпустые.start_pipelineчитает описание из webhook payload → QG-0 «Description < 20 символов» → задача в Blocked. ФИКС: тянуть описание через GET Plane API (issue detail) в start_pipeline, не из payload. - Коллизия work_item_id + рассинхрон веток. M-6 выдал
ET-006ПОВТОРНО: task 8 (plane 9884fb9c, 22 мая, gpx-upload, done) и task 25 (plane bfb4866c, сегодня, popup-enduro-trails). Worktree резолвится по work_item_id → analyst run 59 писал в ЧУЖОЙ worktree (gpx-upload task 8), а не в свой (popup task 25). Гейт check_analysis_complete искал артефакты в branch task 25 → не нашёл → не перевёл в In Review. ФИКС: get_next_work_item_id должен гарантировать уникальность (не переиспользовать seq); worktree/ветка должны привязываться к task_id, не к work_item_id.
Чистка ET-006 (сделана, бэкап БД orchestrator.db.bak-clean-*):
- Удалён дубль task 25 + его runs из БД.
- Удалён мусорный worktree feature_ET-006-gpx-upload.
- Откачен мусорный коммит 6edf97f (analyst run 59 в чужую ветку) → force-push origin/feature/ET-006-gpx-upload на d379e48.
- Тикет #6 (bfb4866c, seq 6) → Backlog, описание на месте. Готов к повторному чистому запуску ПОСЛЕ фикса бага 1.
PR #11 (баги конвейера) — смержен + задеплоен + ОБА бага побеждены в бою (03.06 ~18:15)
- Merge main
cd73c75. Деплой: rebuild+recreate, образ на cd73c75. Бэкап orchestrator.db.bak-deploy11-*. - Мой прогон: 195 passed (190+5 новых), 9 baseline (те же, regression 0).
- Баг1 фикс работает: лог
start_pipeline: pulled description from Plane API (445 chars)→ QG-0 прошёл, НЕ Blocked. Функция fetch_issue_description в plane_sync (переиспользует GET issue endpoint + PLANE_HEADERS). - Баг2 фикс работает: лог
work_item_id collision: derived ET-006 already in use; reassigned -> ET-011. Guard ensure_unique_work_item_id в db.py (поверх M-6 derive). Ветка уникальна per task. - Боевой перезапуск #6: task 26 = ET-011, analyst запущен (job_id=7).
- ⚠️ Мелочь: ветка
feature/ET-011-untitled(slug untitled — в тестовом webhook не было name; реальный Plane шлёт name, не критично). - ⚠️ Дедуп webhook по телу: повторный идентичный payload → {"status":"duplicate"}. Для теста уникализировать тело (activity_id).
PR #11 — финальный отчёт Dev (03.06 ~18:25, дополнение)
- Баг1 (description):
src/plane_sync.py→fetch_issue_description(issue_id, project_id)— переиспользует GET issue-detail + shared-токенPLANE_HEADERS(какfetch_issue_sequence_id). Берётdescription_stripped, при пустом стрипаетdescription_html(_strip_html). На ошибке →""(не бросает). Вызов вstart_pipelineПЕРЕД QG-0. Если и API пусто → честный QG-0 fail. - Баг2a (uniqueness):
src/db.py→ensure_unique_work_item_id(work_item_id, repo)— guard ПОВЕРХ M-6 derive (derive не тронут). ET-NNN занят в tasks для repo → шагает вперёд. Изоляция по repo. Warning при реассайне. - Баг2b (worktree, ~15 строк): worktree в
git_worktree.pyключуется по branch, таски реверс-резолвятся по(repo, branch). С 2a work_item_id уникален → префикс ветки уникален. Доп. страховка: занятая ветка →feature/{work_item_id}-{plane_id[:8]}+ warning. Сам git_worktree.py НЕ переписан (достаточно уникальности ветки). - Тесты:
tests/test_pipeline_start_bugs.py— 5 новых passed. Коммитыfa74610,ac9f5a0,c69e113.
Уроки tooling (03.06)
- Маски ломают bash/psql/python-heredoc: кавычки и спецсимволы в маскированных значениях рвут командную строку и
python3 -c. Решение: писать значения во временный bash/sql-скрипт черезwrite, исполнять файлом — масок нет внутри write. <pending>/<>в .env ломаютsource: строка видаHEYGEN_TALKING_PHOTO_ID=<pending>—<>интерпретируется как редирект. На ключи не влияет, ноsource .envпадает. Чистить такие placeholder-строки или грепать конкретный ключ.- Plane DNS-изоляция: Plane-контейнеры (docker-сеть 172.21.0.0/16) НЕ резолвят внешние домены даже через 8.8.8.8. Оркестратор в host-сети — резолвит. Поэтому webhook URL в БД Plane =
http://172.21.0.1:8500/webhook/plane(внутренний gateway), НЕ внешний DuckDNS-домен. Это уже починено, НЕ трогать. - Plane
issue.updatedшлёт только изменённые поля — на смене статуса description отсутствует. Любая логика, требующая полей тикета на updated-событии, должна дотягивать их из Plane API, а не из payload. - Webhook-дедуп по телу: идентичный payload →
{"status":"duplicate"}. Для повторного теста уникализировать тело (напр. activity_id/timestamp).
Tokenator — лимит исчерпан (03.06)
- Ключ
TOKENATOR_API_KEY(sk-fp-...5625), baseUrlhttps://api.tokenator.top/v1. - Месячная квота: 200,000,000 токенов. Потрачено 200,116,633 → remaining −116,633. Всё блокируется (
Token limit exceeded), включая/v1/models. - Нет эндпоинтов usage/account/limits (404). Расход виден в теле ошибки completions:
{"error":"Token limit exceeded","usage":{"remaining":...,"token_limit":...,"tokens_used":...}}. .dev-домен даёт SSL exit 35;.top(из openclaw.json) — рабочий.- Не блокирует OpenClaw — авто-фолбэк на другие провайдеры (OpenRouter DeepSeek/Grok). Claude CLI агентов (analyst и т.д.) Tokenator не касается.
Открытый хвост (для следующей сессии)
- Боевой #6 перезапущен как task 26 / ET-011, analyst (job_id=7) запущен ~18:15. Проверить: дошёл ли до In Review, появилось ли уведомление с кнопкой одобрения, в правильную ли ветку писал.
- Tokenator: ждать сброса квоты (вероятно 1-е число) или писать саппорту на повышение лимита.