From e0123668bed8f536c7559f4258d4db4939a604b7 Mon Sep 17 00:00:00 2001 From: Stream Date: Wed, 3 Jun 2026 10:50:01 +0300 Subject: [PATCH] auto-sync: 2026-06-03 10:50:01 --- memory/2026-06-03.md | 147 +++--------------- .../DEV_TASK_PLANE_PER_AGENT_AUTHOR.md | 81 ++++++++++ 2 files changed, 103 insertions(+), 125 deletions(-) create mode 100644 tasks/orchestrator/DEV_TASK_PLANE_PER_AGENT_AUTHOR.md diff --git a/memory/2026-06-03.md b/memory/2026-06-03.md index 18af101..9f97bd0 100644 --- a/memory/2026-06-03.md +++ b/memory/2026-06-03.md @@ -1,133 +1,30 @@ -# 2026-06-03 - -## Orchestrator — ORCH-1b resilience ГОТОВ + проверен на проде (продолжение дня 02.06) - -### Итог: PR #3 = ORCH-1 очередь + ORCH-1b resilience — ЗАМЕРЖЕН -**Resilience-слой проверен мной вживую на проде:** -- ✅ **Preflight** — `/queue` → `preflight ok: True` (`2.1.142 Claude Code`). `claude --version`, токены НЕ тратит. CLI мёртв → job ждёт, не падает. 🚫 без prompt-ping -- ✅ **429-классификатор** (`error_classifier.py`) — `429`/`overloaded`→transient, traceback→permanent -- ✅ **Backoff** — exp `min(2^n*10,600)` + `available_at`/`transient_attempts` + Retry-After, PRAGMA-safe миграция (`_ensure_column`) -- ✅ **Circuit breaker** (`queue_worker.py`) — `/queue` отдаёт `resilience.breaker`. 3 transient → open 300с → half-open проба → closed -- ✅ config: 6 ключей (preflight_cache_ttl=45, backoff_base=10, backoff_max=600, transient_max=5, breaker_threshold=3, breaker_pause=300) -- ✅ **110 passed** (26 новых), 9 fail — pre-existing 401 baseline - -### Метрики Dev -- `orch1_queue` (база): done, 12m26s, 337k токенов -- `orch1b_resilience`: done, 9m42s, 260k токенов, 7 коммитов поверх базы - -### ⚠️ Урок про Dev (важный) -Dev **запаниковал** на старте orch1b: «параллельная сессия пишет те же файлы!» → остановился (safety>completion). **Ложная тревога** — другого исполнителя не было, принял СВОЮ активность за чужую (mtime от его же записей). По факту всё доделал и запушил сам. -- **Правило:** при «остановился из осторожности» от Dev — не верить на слово; проверять `git log`/`git status`/remote. Часто работа УЖЕ сделана. -- **Профилактика:** в ТЗ Dev писать «других исполнителей на репо нет, только ты — не паникуй про параллельные сессии». - -### ✅ МЕРЖИ ВЫПОЛНЕНЫ (05:07 UTC, ОК Славы «Делай») -- **PR #3 (ORCH-1 queue + ORCH-1b resilience) ЗАМЕРЖЕН**, merge commit `4e52e19`. Прод пересобран из main, health ok, `/queue` breaker(closed)+preflight(ok). -- **PR #1 (ORCH-2 worktree) ЗАКРЫТ без мержа** (HTTP 201). ⚠️ worktree-код (`src/git_worktree.py`) УЖЕ был в main (уехал при ORCH-6). Ветка PR #1 устарела, мерж «как есть» ОТКАТИЛ БЫ ORCH-6 (-797 строк, снёс бы projects.py/фильтр webhook). **Урок: перед мержем старого PR — проверять merge-base и diff-направление; mergeable:True НЕ гарантирует что PR не откатит свежее.** -- **PR #19 (enduro-trails)** — уже был closed+merged ранее. - -### 🎯 РЕШЕНИЕ Славы (05:19 UTC): добить весь остаток бэклога, дальше — строго Plane + ORCH-нейминг -- **Договорённость:** остаток добиваю контролируемо через Dev (я проверяю каждый PR перед мержем). **Со СЛЕДУЮЩЕЙ новой фичи — задачи СТРОГО в Plane с ORCH-префиксом.** -- ⚠️ **Почему остаток НЕ через Plane-тикеты сейчас:** webhook включён, `orchestrator` — известный проект. Заведёшь тикет → webhook сам запустит конвейер, переписывающий сам оркестратор параллельно моим Dev → два исполнителя на одном репо = хаос как в инцидент. - -### 🧹 Чистка остатков инцидента (05:22 UTC) -- Снесла мёртвые worktree-сироты `_wt/enduro-trails/feature_ET-015-orch-6-multi-repo` и `feature_ET-016-orch-7-self-hosting` (root, от 2 июня; git их не знал, `.git` битый). Через `docker exec orchestrator rm -rf` (контейнер — root, монтирует /repos). -- ⚠️ sudo-пароль `slin` НЕ = vpn-пароль (meNt85doC не подошёл) — для root-файлов на хосте использовать контейнер. - -### Бэклог orchestrator — ПОСЛЕ ВЕРИФИКАЦИИ (Слава: «убедись что не реализованы») — проверено по живому коду main -**ВАЖНО: из 5 пакетов реальных осталось 3 — часть уже сделана раньше.** -1. ✅ **ORCH-7 (hardening) ЗАМЕРЖЕН** = M-4 (`_auto_merge_pr` удалён, `_ensure_pr` цел) + M-2 (3-фазный graceful: SIGTERM→grace-поллинг signal0→SIGKILL только если жив; config agent_timeout_seconds/agent_kill_grace_seconds/overrides_json). **PR #4 merge commit `fd554c8a`**, прод пересобран, health ok, /queue breaker=closed preflight=True, config 1800/20. **118 passed** (110+8), 9 pre-existing. - - ⚠️ **ГРАБЛЯ:** Dev отчитался «PR #4 готов», но указатель ветки (local+remote) смотрел на main (4e52e19), коммиты ORCH-7 (237732b/49ecb48/c167c69) висели «в воздухе» (в объектной базе есть, видны через `git log --all`, но ни одна ветка не указывала). PR #4 ПУСТ (0 коммитов vs main), но mergeable:True. Фикс: `git branch -f feature/ORCH-7-hardening c167c69` + force-push (предварительно checkout main — нельзя двигать текущую ветку). - - **УРОК:** проверять `git log origin/main..origin/<ветка>` — реальное содержимое PR. mergeable:True на пустом PR = красный флаг. В ТЗ Dev добавлять проверку remote после push. -2. ✅ ~~**ORCH-8 (S-1b)**~~ **УЖЕ СДЕЛАНО — ОТМЕНЕНА.** `check_tests_local` (`qg/checks.py:250`) гоняет `make test` в worktree (S-4-safe) И уже дефолт в `stages.py:16`. Аудит 2 июня отстал. -3. 🚀 **ORCH-3 (S-3 только):** S-2 (деплой через SSH-хук) УЖЕ СДЕЛАН (`enduro-deploy-hook.sh`). ОСТАЛОСЬ S-3 rollback: `deployer.md` (репо **enduro-trails**) всё ещё `git checkout $LAST_TAG` (портит shared-репо) + `Bash(docker)`. Фикс: убрать docker/git-checkout-rollback из промпта, rollback в хук (по тегам/образам). **Репо enduro-trails + хук на хосте.** Ложится вместе с M-5. -4. 🔄 **ORCH-4 (M-3) ЗАПУЩЕН** (taskName `orch4_stage_engine`, ветка feature/ORCH-4-stage-engine, ТЗ `DEV_TASK_ORCH4_STAGE_ENGINE.md`). ⚠️ **НЕ ПРОСТО ДУБЛЬ — версии СЕМАНТИЧЕСКИ РАЗОШЛИСЬ** (нашла при разведке): - - 🔴 **БАГ выбора агента (ИСПРАВЛЕН):** launcher брал `get_agent_for_stage(next_stage)` — БЫЛО НЕПРАВИЛЬНО (пропуск стадии: created→analysis запускал *architect* вместо *analyst*). Правильно = `current_stage` (Я ПЕРЕПРОВЕРИЛА логику сама по STAGE_TRANSITIONS — вердикт Dev верный). Унифицировано на current_stage, stages.py не тронут. - - 🔴 СОХРАНЕНО ЦЕЛИКОМ: approved-флоу, REQUEST_CHANGES→retry max3, tester FAIL→rollback, architect conflict→rollback в analysis, check_review_approved (PR по ветке + file-fallback). - - Слито в `src/stage_engine.py` (425 строк) `advance_stage(...)`. launcher (+18−169), plane (+21−72 через `asyncio.to_thread`) = тонкие обёртки. -5. ✅ **ORCH-5 (M-7) ЗАМЕРЖЕН** — идемпотентность webhook (dedup по delivery-id). **PR #6 merge commit `2fdc6856`**, прод пересобран, health/queue ok. **145 passed** (136+9 новых), 9 pre-existing (сверено с clean main). clean-merge. - - Было: dedup ОТСУТСТВОВАЛ полностью (delivery-id нигде не читался) → повторная доставка webhook = дубль конвейера (класс ET-009). - - Стало: `events.delivery_id` (через `_ensure_column`) + partial UNIQUE index `idx_events_delivery`. Helper `src/webhooks/_dedup.py`: gitea=`X-Gitea-Delivery` GUID/fallback sha256(event+body); plane=fallback sha256("plane"+body); префикс `source:`. `insert_event_dedup` → INSERT OR IGNORE, дубль → `{"status":"duplicate"}` без enqueue. - - Порядок ПРОВЕРИЛА сама: dedup ПОСЛЕ HMAC (401 цел), ДО ORCH-6 фильтра (unknown project на 1-й доставке всё равно ignored). Миграция на живой БД применилась (1108 строк events целы). -- Помельче (потом): M-6 (work_item_id из Plane sequence), L-1/L-2 (нейминг/логи), M-5 (хардкод инфры в промптах — enduro-trails, с ORCH-3). -- ⚠️ **УРОК:** аудит устаревает — ВСЕГДА верифицировать по живому коду перед запуском Dev (S-1b уже была сделана, чуть не запустила дубль). - -### ⏭️ ТОЧКА ВХОДА (06:20 UTC) -- **ORCH-7 закрыт. ORCH-4 ЗАМЕРЖЕН** (PR #5 `2f0fd246`, прод пересобран, health/queue ok, 136 passed). Баг выбора агента починен. -- **ORCH-5 (M-7 dedup webhook) ЗАМЕРЖЕН** (PR #6 `2fdc6856`, прод пересобран, health/queue ok, 145 passed, миграция на живой БД ок). **ЭТО БЫЛА ПОСЛЕДНЯЯ ЗАДАЧА ПО САМОМУ orchestrator.** -- ✅ **ORCH-3 (S-3) + M-5 ЗАМЕРЖЕН** (PR #20 `d379e48c`, репо enduro-trails, только .md-промпты). В main: `git checkout $LAST_TAG` УБРАН, rollback через `bash $HOOK --rollback`, docker сужен в tools, M-5 параметризован. - - ✅✅ **ПРОД-ХУК ПРИМЕНЁН** (Слава дал ОК, 03.06). `cp .new → /home/slin/bin/enduro-deploy-hook.sh`, живой теперь md5 `2b60c6...` (3039b), bash -n OK, +x. Backup `.bak-1780468526` (md5 `aa86e7...`) на месте. Откат если что: `cp .bak-1780468526 → хук`. Реальный деплой НЕ запускала (живой app не тронут) — первый prev-image запишется на ближайшем реальном деплое (до этого `--rollback` честно skip). - - Хук-кандидат ПРОВЕРИЛА сама: happy-path 1-в-1 (pull+capture prev-image+restart+gps-collector, set -e safe), rollback с 3 уровнями защиты (файл·непуст·образ есть) → ретег+`--no-build` restart, иначе honest skip+exit1. - - Отчёт Dev: `tasks/orchestrator/reports/dev-2026-06-03-orch3-deploy-rollback.md`. - - **Разведка (было):** - - 🔴 **S-3 баг:** `deployer.md` стр.103 `git checkout $LAST_TAG` в shared-репо `/home/slin/repos/enduro-trails` → detached HEAD загаживает рабочую копию, хук делает `git pull origin main` → следующий деплой ломается. И откат фиктивный — прод крутит старый образ, git checkout локального файла прод не меняет. - - Хук `enduro-deploy-hook.sh` (876b, pull+restart+gps-collector) — rollback'а НЕТ. План: rollback В ХУК (prev-image перед рестартом + флаг `--rollback`), в deployer.md → `ssh ... bash $HOOK --rollback`. - - 🟡 **M-5:** architect.md:16 (82.22.50.71), tester.md:27,38,39 (/home/slin/ пути) → env с дефолтами (deployer.md уже параметризован — образец). - - ⚠️ ПРОД-ДЕПЛОЙ: Dev НЕ деплоит на прод, бэкап хука перед правкой, проверка = bash -n. Мерж и прод-применение хука — я со Славой. -- Бэклог orchestrator ЗАКРЫТ: ORCH-1/1b/2/4/6/7 + ORCH-5 в main. ORCH-8 отменена. - -### Финальная гигиена L-1/L-2/L-3 (Слава: «делай все в т.ч. l-3») — ЗАПУЩЕНО (taskName `orch_cleanup_l1l2l3`, ветка feature/ORCH-cleanup-L1L2L3, ТЗ `DEV_TASK_ORCH_CLEANUP_L1L2L3.md`) -- ⚠️ **Разведка переписала остаток — аудит 02.06 отстал:** - - ❌ **L-4 ОТПАЛ** — мусорной папки `{src` уже нет. - - ❌ **L-5 ОТПАЛ** — `tests/test_launcher.py` УЖЕ есть (18 тестов: запись·verdict·timeout·watchdog SIGTERM→SIGKILL). Долг закрыт по ходу ORCH-1b/2/4. - - ⚠️ **M-6 НЕ мелочь, ОТЛОЖЕНА:** Plane НЕ присылает `sequence_id` в webhook payload (проверила 5 реальных events в БД — ключа нет). Значит «источник правды = Plane sequence» требует ОТДЕЛЬНОГО GET к Plane API по issue_id — сетевой вызов, не правка функции. Рассинхрон теоретический (per-repo инкремент `get_next_work_item_id` работает). Ждёт отдельного решения Славы — нужен ли вообще. - - ✅ **L-1+L-2+L-3 ЗАМЕРЖЕНЫ** (PR #7 `be27f506`, прод пересобран, health/queue ok, **151 passed** = 145+6, 9 pre-existing). L-1 шапка stages.py исправлена, L-2 `prune_run_logs` best-effort в lifespan (keep_days=30/keep_max=500, не трогает active), L-3 эмодзи-константы. -- ✅ **M-6 ВАЖНА** (Слава подтвердил) — берём в работу. Разведка: - - Инфра УЖЕ ЕСТЬ: `plane_sync.py` — полный httpx-клиент (PLANE_BASE/HEADERS/WORKSPACE из config), уже читает `sequence_id` (стр.99,115), `find_issue_id` ищет issue. M-6 НЕ с нуля. - - Payload `work_item.created` содержит `id` (issue UUID) + `project` — по ним GET к Plane API достаёт настоящий `sequence_id`. `sequence_id` В ПАЙЛОАДЕ НЕТ (проверено), но есть через GET по id. - - 🔴 **БОНУС-баг найден:** `find_issue_id` хардкодит `f"ET-{seq:03d}"` (стр.99) — сломано для ORCH-префикса из ORCH-6. M-6 должна убрать хардкод ET-. - - ⚠️ ГОНКА ВЕТОК: M-6 запускать ТОЛЬКО после мержа L1L2L3 из чистого main (иначе 2 Dev дерутся за рабочую копию). L1L2L3 уже смержен → можно. - - 🔄 **M-6 ЗАПУЩЕН** (taskName `orch_m6_plane_sequence`, ветка feature/ORCH-M6-plane-sequence, ТЗ `DEV_TASK_ORCH_M6_PLANE_SEQUENCE.md`). План: helper `fetch_issue_sequence_id` (GET по issue UUID), в handle_work_item_created seq→`{prefix}-{seq:03d}` или fallback DB-инкремент (автономность!), + фикс хардкода `ET-` в find_issue_id. -- ⚠️ **ТОКЕН Gitea (находка Dev 03.06):** `.env` на сервере содержит УСТАРЕВШИЙ `ORCH_GITEA_TOKEN` (28 симв, HTTP 401). РАБОЧИЙ токен (40 симв) — в env контейнера `orchestrator`: `docker exec orchestrator printenv | grep -i gitea`. ⚠️ Мои прошлые мержи (PR #5/6/7) работали — значит я брала токен из .env и он рабочий?? ПРОВЕРИТЬ при след. мерже M-6 — если .env-токен 401, брать из контейнера. (Славе на заметку: стоит синхронизировать .env с рабочим токеном.) -- Мерж-рецепт (работает): проверить `git log origin/main..origin/ветка` (не пусто!), мой прогон тестов, clean-merge check, мерж через Gitea API `/pulls/N/merge` `{"Do":"merge"}`, пересборка из main. -- ТЗ-образцы: `DEV_TASK_ORCH7_HARDENING.md`, `DEV_TASK_ORCH4_STAGE_ENGINE.md`. -- ⚠️ **Грабля memory-файла:** ранний `write` сделал APPEND (задвоил файл), почистила перезаписью. Для точечных правок memory — `edit`, не `write`. - -### Нейминг аудита (расшифровка для Славы) -- Коды из `AUDIT_2026-06-02.md`: буква = критичность (B blocker / S serious / M medium / L low), цифра = порядковый номер, буква-суффикс (S-1b) = вариант решения. ORCH-N = тот же баг как тикет в Plane. С этого момента — только ORCH-N. - -### Ключевые идентификаторы -- Хост `slin@82.22.50.71`, репо `/home/slin/repos/orchestrator`, контейнер `orchestrator` (8500), Gitea `localhost:3000` (token в `.env` `ORCH_GITEA_TOKEN`) -- Деплой: `docker compose up -d --build && sleep 6 && curl -s :8500/health && curl -s :8500/queue` -- Тесты в контейнере: `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` -- ⚠️ root-файлы на хосте — через `docker exec orchestrator rm -rf` (не sudo, пароль не тот) --- -## Аудит 02.06 ПОЛНОСТЬЮ ЗАКРЫТ (финал дня 03.06) +## Plane per-agent authorship (Вариант A) — инфра ГОТОВА, код запущен (03.06 вечер) -### L-1 / L-2 / L-3 — ЗАМЕРЖЕНО (PR #7 `be27f506`) -- Один компактный PR, 3 коммита: `48b7707` docs(stages) L-1, `0653c24` feat(launcher) L-2, `8f11971` refactor(plane_sync) L-3. -- **L-1:** шапка `src/stages.py:8` врала «when entering the NEXT stage» (этот нейминг породил баг ORCH-4) → исправлена на «advancing FROM this stage (NOT the next stage's agent)». Только комментарий. -- **L-2:** `prune_run_logs(runs_dir, keep_days=30, keep_max=500, active_paths=None)` в `launcher.py` — чистит `*.log` старше keep_days ИЛИ сверх keep_max свежих; не трогает не-.log, поддиректории, активный лог. Вызов best-effort в `lifespan` (try/except + warning, не роняет app). Параметры: env `ORCH_LOG_KEEP_DAYS`/`ORCH_LOG_KEEP_MAX`, дефолты в config.py. +6 тестов `tests/test_log_rotation.py`. -- **L-3:** эмодзи-литералы → `EMOJI_STAGE`/`EMOJI_QG_FAIL`/`EMOJI_DONE` в `plane_sync.py`. Вывод байт-в-байт прежний. (стр.242 стала f-строкой ради подстановки — текст тот же.) -- Тесты: 145 → **151 passed** (+6), 9 = baseline 401. -- **L-4 СНЯТ:** мусорной папки `{src` уже нет. **L-5 СНЯТ:** `tests/test_launcher.py` (18 тестов) уже существует — долг закрыт по ходу ORCH-1b/2/4. Аудит 02.06 отстал от кода. +**Задача Славы:** видеть в Plane КТО написал комментарий (агенты орка + Стрим), а не единый `mva154`. Выбран Вариант A (честные аккаунты), способ (b) — боты в БД. -### M-6 — ЗАМЕРЖЕНО (PR #8 `12e2691a`), Слава отметил как ВАЖНУЮ -- `feat(webhook): derive work_item_id from Plane sequence_id` `1d978ca` + `fix(plane_sync): drop hardcoded ET- prefix` `c431a3d`. -- **Суть:** `work_item_id` теперь = Plane `sequence_id` (источник правды), а не `max(tasks)+1`. Helper `fetch_issue_sequence_id(issue_id, project_id)` в `plane_sync.py` — GET `…/projects/{pid}/issues/{uuid}/` (⚠️ **trailing slash обязателен**, без него 301), `sequence_id` = top-level int, timeout=10, try/except → None (не кидает). -- **Fallback автономности:** Plane down / seq=None → старый `get_next_work_item_id` + warning. Task создаётся при любом исходе, конвейер не встаёт. `get_next_work_item_id` ОСТАВЛЕН как fallback. -- **Бонус-фикс:** `find_issue_id` хардкодил `f"ET-{seq:03d}"` → сломан для ORCH-префикса (ORCH-6). Убрано, матч по числу из `rsplit("-",1)`, обобщено на любой префикс. -- Живая иллюстрация рассинхрона: `ET-010` (`f9009756…`) в Plane = **404** — старый счётчик такие дыры игнорировал. -- Тесты: 151 → **158 passed** (+7 `tests/test_m6_sequence.py`), 9 = baseline. -- Отчёт: `tasks/orchestrator/reports/dev-2026-06-03-orch-m6-plane-sequence.md`. +### Инфра (СДЕЛАНО Стримом напрямую — это БД-операция, не код): +- **Plane self-hosted**, Docker (`plane-app-*`), БД `plane-app-plane-db-1`, креды `plane:plane`, db `plane`. +- Workspace `ag_proj` = `903e12e8-65c9-40a0-a7f6-0186f8af42d4`. +- **Ключевой факт:** автор комментария в Plane = владелец токена (`api_tokens.user_id`), НЕ label. Поэтому нужен отдельный user на агента. +- Было: 2 токена (`orchestrator`, `analyst-agent`) — ОБА на mva154 → всё под mva154. +- **Создано 7 сервис-ботов** (`is_bot=t`, role 15 member в ag_proj), email `@orch.local`, каждый со своим 60-симв сервисным токеном: + - 🧠 orch-analyst, 🏗️ orch-architect, 💻 orch-developer, 🔍 orch-reviewer, 🧪 orch-tester, 🚀 orch-deployer, 🌊 orch-stream (я) +- SQL-скрипт: `/tmp/plane_bots.sql` на сервере (идемпотентный, транзакция, ON CONFLICT). Потребовал `CREATE EXTENSION pgcrypto` (gen_random_bytes). +- **Бэкап БД перед записью:** `/home/slin/plane-identity-backup-1780472581.sql` (users+workspace_members+api_tokens). +- Токен Analyst-бота проверен живым GET → HTTP 200, видит 4 проекта. Авторство работает. +- **Токены в `.env`** (merge-append, блок «M-6.5 Plane per-agent»): `ORCH_PLANE_BOT_ANALYST/ARCHITECT/DEVELOPER/REVIEWER/TESTER/DEPLOYER/STREAM`. Бэкап `.env.bak-`. -### ⚠️ ГОНКА ВЕТОК — урок (важно для будущих параллельных задач) -- L1L2L3 и M-6 — оба правят orchestrator-репо из ОДНОЙ рабочей копии `/home/slin/repos/orchestrator`. Запуск двух Dev одновременно = реальная драка за git-состояние (НЕ ложная паника). -- **Правило:** при работе двух задач в одном репо — сначала смержить+освободить ветку первой, ТОЛЬКО потом запускать вторую из чистого main. Так и сделала: PR #7 мерж → потом запуск M-6. +### Код (Dev, ветка `feature/plane-per-agent-author`, ТЗ `DEV_TASK_PLANE_PER_AGENT_AUTHOR.md`): +- `config.py`: 7 полей bot-токенов. +- `plane_sync.py`: `PLANE_BOT_TOKENS` map + `_headers_for(author)` (fallback на общий токен если роль пуста/токен пуст). `add_comment(..., author=None)` — POST под токеном агента. GET/PATCH НЕ трогать (под общим токеном). +- Точки вызова прокидывают author по роли стадии: stage_engine (284/305/316 analyst, 369 tester, 409 architect), launcher:472 (текущий агент), webhooks/plane (212/255 analyst, 267 целевая стадия), plane_sync stage-уведомления (255 🔄 → stream, 267 ✅ → deployer). Неочевидное → `stream`. +- Baseline 158 passed. Тесты `tests/test_plane_author.py`. +- ⚠️ Мерж — Стрим после живой проверки + реальный тест-комментарий от каждого бота в Plane. -### 🔍 Три одинаковых сообщения (🔄 ET-100) — РАЗГАДКА -- **НЕ оркестратор, НЕ дубль конвейера (НЕ класс ET-009).** Орк за сутки слал в Telegram **0 сообщений** (проверено по логам), в конвейере 1 задача, breaker closed. -- Причина: **дубли completion-событий субагента** (Dev) — повадка доставки OpenClaw, событие «субагент завершил» приходит по нескольку раз, родитель просыпается на каждой доставке → отклик протекает наружу. -- Лечение: глушу через NO_REPLY, но 1-2 иногда проскакивают. Риска для данных/конвейера НОЛЬ. Если будет бесить Славу — копнуть жёсткий дедуп на уровне обработчика. - -### Токен Gitea — РАЗРЕШЕНО -- Подтверждено: мерж M-6 прошёл рабочим токеном (брала из env контейнера по заметке Dev). `.env` на сервере содержит устаревший `ORCH_GITEA_TOKEN` (28 симв, 401), рабочий (40 симв) — `docker exec orchestrator printenv | grep -i gitea`. **Славе на заметку:** синхронизировать `.env`. - -### ИТОГ -- **Весь бэклог orchestrator + весь аудит 02.06 закрыты.** В main: ORCH-1/1b/2/4/5/6/7 + L1L2L3 + M-6. ORCH-8 отменена, L-4/L-5 сняты как уже сделанные. -- Прод после M-6: main HEAD `12e2691`, health ok, breaker closed. -- Merge-commits финала: PR #7 `be27f506` (L1L2L3), PR #8 `12e2691a` (M-6). -- ТЗ-файлы: `DEV_TASK_ORCH_CLEANUP_L1L2L3.md`, `DEV_TASK_ORCH_M6_PLANE_SEQUENCE.md`. -- ⏳ Открыто (НЕ срочно): прод-применение хука enduro-trails (swap `.new`→живой) — ждёт ОК Славы; бэкап `enduro-deploy-hook.sh.bak-1780468526`. +### Закрыто разведкой (НЕ требовало работы): +- Хук enduro-trails swap — УЖЕ применён (live md5 == кандидат `2b60c65b...`). Бэкап `.bak-1780468526` цел. +- `.env` Gitea-токен — УЖЕ синхронизирован (40 симв == контейнер). Заметка Dev устарела. diff --git a/tasks/orchestrator/DEV_TASK_PLANE_PER_AGENT_AUTHOR.md b/tasks/orchestrator/DEV_TASK_PLANE_PER_AGENT_AUTHOR.md new file mode 100644 index 0000000..452c562 --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_PLANE_PER_AGENT_AUTHOR.md @@ -0,0 +1,81 @@ +# DEV TASK — Plane: комментарии от имени агента-бота (per-agent authorship) + +**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500) +**Ветка:** `feature/plane-per-agent-author` из свежего main. + +⚠️ **ГРАБЛЯ push (ORCH-7):** после push `git log origin/main..origin/feature/plane-per-agent-author` ДОЛЖЕН показать коммиты ДО отчёта «PR готов». + +## Контекст (инфра УЖЕ готова — НЕ трогай БД/Plane) +Слава хочет видеть в Plane, КТО написал комментарий (analyst/architect/dev/reviewer/tester/deployer/Стрим), а не единый `mva154`. В Plane автор = владелец токена. + +**Уже сделано (НЕ переделывай):** в Plane созданы 7 сервис-ботов с токенами, добавлены в воркспейс `ag_proj`. Токены лежат в `.env`: +``` +ORCH_PLANE_BOT_ANALYST, ORCH_PLANE_BOT_ARCHITECT, ORCH_PLANE_BOT_DEVELOPER, +ORCH_PLANE_BOT_REVIEWER, ORCH_PLANE_BOT_TESTER, ORCH_PLANE_BOT_DEPLOYER, ORCH_PLANE_BOT_STREAM +``` +Дисплей-имена ботов в Plane: 🧠 Analyst / 🏗️ Architect / 💻 Developer / 🔍 Reviewer / 🧪 Tester / 🚀 Deployer / 🌊 Стрим. + +## Задача: `add_comment` постит ПОД ТОКЕНОМ АГЕНТА + +### 1. config (`src/config.py`) +Добавить поля для 7 bot-токенов (env-имена выше, дефолт `""`). По образцу существующих `plane_*` полей. + +### 2. `src/plane_sync.py` — выбор токена по роли +- Маппинг роль→токен, напр.: +```python +PLANE_BOT_TOKENS = { + "analyst": settings.plane_bot_analyst, + "architect": settings.plane_bot_architect, + "developer": settings.plane_bot_developer, + "reviewer": settings.plane_bot_reviewer, + "tester": settings.plane_bot_tester, + "deployer": settings.plane_bot_deployer, + "stream": settings.plane_bot_stream, +} +def _headers_for(author: str | None) -> dict: + """X-API-Key headers for the given agent role; falls back to the default + orchestrator token (settings.plane_api_token) if role unknown/empty/token missing.""" + tok = PLANE_BOT_TOKENS.get(author or "") if author else None + return {"X-API-Key": tok} if tok else PLANE_HEADERS +``` +- `add_comment(work_item_id, text, project_id=None, author=None)` — НОВЫЙ необязательный параметр `author`. Внутри POST использовать `_headers_for(author)` ВМЕСТО глобального `PLANE_HEADERS`. +- ⚠️ **GET/PATCH (find_issue_id, set_state) НЕ менять** — они служебные, пусть остаются под общим токеном. Меняем авторство ТОЛЬКО у комментариев (`add_comment` POST). +- ⚠️ Если bot-токен пустой (не сконфигурён) → fallback на общий токен (автономность: комментарий всё равно постится, просто под orchestrator). + +### 3. Точки вызова — прокинуть author по роли стадии +Для каждого `add_comment`/`plane_add_comment`/`_plane_comment`/`_add_comment`/`_pc` передать author = роль того агента, чьё это сообщение. Маппинг бери из `STAGE_TRANSITIONS` (`get_agent_for_stage`) или по смыслу текста: +- **`src/stage_engine.py`** (284, 305, 316 — Analyst; 369 — после тестов rollback это Tester; 409 — Architect). Смотри по тексту сообщения, кто его «автор». +- **`src/agents/launcher.py:472`** — автор = агент, который сейчас запускался (роль уже известна в этом контексте, см. сигнатуру launch). +- **`src/webhooks/plane.py`** (212 «Analyst запущен» → analyst; 255 «Analyst перезапущен» → analyst; 267 «Откат» → агент целевой стадии; 310 и др. — по смыслу). +- **`src/plane_sync.py`** сами хелперы: `notify_stage_change`/строка 255 (🔄 Stage) → author=`"stream"` (это голос оркестратора/мой); 260 (QG failed) → author соответствующего агента стадии; 267 (✅ Task completed) → author=`"deployer"` (деплой завершён) или `"stream"` — выбери deployer. +- 🌊 **Стадии-переходы и общие уведомления оркестратора** (🔄 Stage, ✅ Completed) — author=`"stream"` (это я). + +Если для конкретной точки роль неочевидна — ставь `"stream"` (оркестратор) и отметь в отчёте. Лучше явный «Стрим», чем немой `mva154`. + +## Ограничения +- 🚫 НЕ трогай: БД Plane, токены в .env (уже прописаны — только ЧИТАЙ через config), nginx, openclaw.json, deploy-хук, HMAC, очередь, stage_engine-логику переходов (только добавь author= в вызовы add_comment), STAGE_TRANSITIONS, get_next_work_item_id, M-6 код. +- GET/PATCH остаются под общим токеном — меняем ТОЛЬКО авторство комментариев. +- Baseline: **158 passed**, 9 pre-existing 401 — не чинить/не ломать. +- Conventional Commits: `feat(plane): per-agent bot authorship for comments`, при желании отдельный `refactor` для config. + +## Тесты `tests/test_plane_author.py` +- `_headers_for("analyst")` → `{"X-API-Key": }` (мок settings). +- `_headers_for(None)` / неизвестная роль / пустой токен → fallback на `PLANE_HEADERS` (общий). +- `add_comment(..., author="developer")` → httpx.post вызван с developer-заголовком (мок httpx, assert headers). +- `add_comment(...)` без author → общий токен (обратная совместимость). +- Тесты в контейнере: `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` + +## Проверка (Стрим проверит вживую + реальным комментарием от каждого бота) +| # | Что | Критерий | +|---|-----|----------| +| 1 | _headers_for | роль→bot-токен; неизвестно/пусто → fallback общий | +| 2 | add_comment | author прокидывается в POST headers; без author → совместимость | +| 3 | точки вызова | каждая передаёт осмысленный author (или stream) | +| 4 | GET/PATCH | НЕ тронуты, под общим токеном | +| 5 | тесты | 158 → 158+новые passed, 9 pre-existing не тронуты | +| 6 | git | PR в main, remote содержит коммиты | + +## Отчёт +- НЕ деплоить, НЕ мержить (мерж — Стрим). Токен Gitea для PR — рабочий из env контейнера (`docker exec orchestrator printenv | grep -i gitea`), .env-токен может быть 401. +- Запушь ветку (ПРОВЕРЬ remote!), открой PR в main. В отчёте: таблица «точка вызова → выбранный author». +- Если расходится с кодом — отчитайся, не выдумывай. Один исполнитель.