Files
wiki/memory/2026-06-03.md
2026-06-03 21:30:01 +03:00

135 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
## Конвейер 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 фичи):
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. Мерж — Стрим после живой проверки.
### 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: `a4668c0` feat(plane) stage visibility+verdict UUIDs, `09b1c5e` feat(webhook) старт на In Progress, `38a741d` feat(webhook) вердикт Approved/Rejected (вариант B), `9a702a0` feat(metrics) токены/cost per-agent, `7fd6529` test(conftest) глушилка Telegram.
- **Прогон тестов сама: 190 passed, 9 failed.** Девять — pre-existing baseline (сравнила полную сюиту на main ДО ветки = те же 9). Regression = 0. Из девяти: 8 это 401-invalid-signature, 1 это `test_plane_approved` TypeError — ВСЕ падали и на main. ⚠️ Уточнение к прошлым заметкам: реальный baseline = 9 (не только 401-набор).
- **Telegram-утечка закрыта:** `tests/conftest.py` autouse-фикстура глушит `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):
1. **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.
2. **Коллизия 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`), baseUrl `https://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-е число) или писать саппорту на повышение лимита.