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

461 lines
62 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-е число) или писать саппорту на повышение лимита.
---
## БАГ 3 — самоудар по эхо-комменту: In Review → In Progress (03.06 ~18:30)
**Симптом:** analyst довёл #6 (ET-011) до In Review корректно (баг2 фикс работает!), но через 0.1с статус откатился обратно в In Progress. На скрине Славы State=In Progress, уведомления с :approved: нет.
**Цепочка (лог 18:17:05):**
1. analyst PATCH → In Review (38fb1f64) ✅
2. analyst постит коммент «BRD/ТЗ готовы. Жду :approved:» (author=analyst)
3. Этот коммент эхом прилетает в `handle_comment` (src/webhooks/plane.py ~433)
4. Коммент без :approved:/:rejected:, а `current_stage=="analysis"` → код трактует как «ответ человека на вопросы аналитика» → `set_issue_in_progress(work_item_id)`ОТКАТ In Review→In Progress 🐛
**Корень:** handle_comment НЕ отличает собственный коммент бота/агента от ответа человека. Любой не-вердиктный коммент на стадии analysis → возврат в In Progress. analyst сам себя сбивает.
**Фикс (для Dev):** в handle_comment игнорировать комменты, автором которых является бот/агент (analyst/architect/... или служебный actor). Проверять author/actor вебхука: если это наш сервисный аккаунт или один из агентов — return (no action). Только комменты РЕАЛЬНОГО человека (Слава) должны триггерить approved/rejected/answer-to-questions.
**NB:** PLANE_STATES в Enduro Trails корректен — один In Progress (b873d9eb). «5 In Progress» ранее — статусы ДРУГИХ проектов, ложная тревога.
**Состояние:** #6 сейчас In Progress (откачен), task 26 stage=analysis, артефакты в feature/ET-011-untitled готовы. После фикса бага3 — повторить и убедиться что In Review держится.
---
## РЕШЕНО: чистая статусная модель управления конвейером (03.06, подтверждено Славой)
Слава управляет конвейером ТОЛЬКО статусами. Комменты НИКОГДА не триггерят переходы. Финальная модель:
- **Одобрение** → Слава ставит статус **Approved** → оркестратор двигает на след. стадию (сам ставит статус стадии).
- **Отклонение** → Слава СНАЧАЛА пишет коммент с причиной, ПОТОМ ставит **Rejected** → оркестратор откатывает + читает причину из последнего коммента.
- **Ответ на вопросы analyst** (задача в Needs Input) → Слава пишет коммент(ы), ПОТОМ САМ возвращает статус в **In Progress** → оркестратор перезапускает агента текущей стадии (читает комменты). Триггер = СТАТУС In Progress, не коммент.
- Комментный механизм (:approved:/:rejected:/answer-by-comment) — ВЫПИЛИВАЕТСЯ полностью.
ТЗ: tasks/orchestrator/DEV_TASK_STATUS_ONLY_VERDICT.md
Ключевая правка vs прошлого PR: handle_status_start теперь должен ПЕРЕЗАПУСКАТЬ агента при возврате In Progress из Needs Input (не просто idempotent-skip), отличая от защиты-дублей через running-job/prev-status.
---
## PR #12 — статус-онли модель + баг 3 закрыт, боевой прогон удачен (03.06 ~19:25)
**Merge main = `2d392b6`.** Ветка `fix/status-only-verdict` (из main с PR #11). Задеплоен (rebuild+recreate).
### Что выпилено/изменено (src/webhooks/plane.py):
- **handle_comment → чистый логгер.** Весь комментный механизм управления убран: ветки `:approved:` (~427), `:rejected:` (~420), answer-to-questions `if current_stage=="analysis"` (~433-490). Ни один коммент не меняет статус и не запускает агентов. Router routes comment.created → только лог. Корень бага 3 (эхо-самоудар) устранён.
- **handle_status_start: перезапуск агента при In Progress из Needs Input.** Развилка решена через running-job (а НЕ prev-status — его нет: tasks-таблица без status-колонки, payload несёт только новый статус). Логика: нет task → start_pipeline; task есть + НЕТ active job → агент простаивает = «Слава ответил на вопросы» → relaunch агента текущей стадии (STAGE_AUTHORS[stage]); task есть + active job → busy/дубль → skip. Двухслойный дедуп: insert_event_dedup (идентичные тела) + новый db-хелпер `has_active_job_for_task(task_id)` (`SELECT 1 FROM jobs WHERE task_id=? AND status IN ('queued','running')`) — для РАЗНЫХ webhook при живой джобе.
- **handle_verdict(approved): убран `set_issue_in_progress`** — он откатывал статус перед advance (мелькание In Progress). Подтверждено: `_try_advance_stage → advance_stage → notify_stage_change → update_issue_state` (plane_sync.py:255) сам PATCH-ит статус след. стадии. Безопасно.
- **handle_verdict(rejected): причина из последнего коммента.** Новый `_latest_comment_reason(issue_id, repo, project_id)`: GET .../issues/<id>/comments/, новейший по created_at, стрип HTML, trim 300. Fallback «Rejected via status, no reason comment». Передаётся в _rollback_stage.
### Тесты (мой прогон сам): **204 passed, 9 failed** (те же 9 baseline pre-existing signature/401, regression 0, +12 новых).
Новые/переписанные: test_status_only_verdict.py (test_inreview_comment_does_not_revert = главный для бага3, test_any_comment_no_pipeline_action, test_approved_status_advances_without_inprogress_reset, test_rejected_status_pulls_reason_from_comment); test_status_trigger.py (test_repeat_in_progress_while_job_active_does_not_relaunch, test_inprogress_from_needs_input_relaunches_analyst); test_verdict_status.py (approved/rejected comment → noop); test_webhooks.py (approved/rejected переписаны на статус-триггеры).
Коммиты: `fix(webhook): remove comment-based approve, keep status-only verdict`, `fix(webhook): drop redundant in_progress reset on Approved`, `feat(webhook): pull reject reason from latest comment`.
### ✅ БОЕВОЙ ПРОГОН — баг 3 побеждён (главное):
Почистил прошлый task 26, вернул #6 в Backlog → перевёл в In Progress → новый прогон **task 27 = ET-011** (guard снова ушёл от занятого ET-006). analyst run 61: 6105 out, $0.40.
- ✅ Лог: `comment.created ... logged only, no pipeline action (status-only verdict)` — коммент analyst НЕ триггерит ничего.
- ✅ Лог: `issue updated to state 38fb1f64... no pipeline action` — перевод в In Review больше НЕ вызывает откат.
-**#6 = In Review ДЕРЖИТСЯ** (раньше за 0.1с был откат в In Progress). Баг 3 закрыт в бою.
### Артефакты analyst ET-011 (ветка feature/ET-011-untitled, Gitea admin/enduro-trails):
docs/work-items/ET-011/: 00-business-request.md, 01-brd.md, 02-trz.md, 03-acceptance-criteria.md, 04-test-plan.yaml, 04b-ui-test-cases.md.
Внешний URL Gitea = `https://git.mva154.duckdns.org/admin/enduro-trails/src/branch/feature/ET-011-untitled/docs/work-items/ET-011/<файл>`.
### Открытый хвост (следующая сессия):
- **#6 сейчас в In Review** в Plane, ждёт ручной проверки Славы: перевести в **Approved** → должна уехать в Architecture БЕЗ мелькания In Progress. Или Reject: коммент причины → Rejected → analyst перезапуск с причиной. Слава решит — сам в UI или мне симулировать.
- **Косметика (предложен 2-й тикет Dev, Слава ещё не подтвердил):** (1) коммент analyst всё ещё пишет «Жду :approved:» — устаревшая формулировка, поправить на «переведите в Approved»; (2) добавить ссылки на артефакты прямо в коммент Plane (чтобы не лезть в Gitea руками).
- Ветка-slug всё ещё `untitled` (в тестовом webhook нет name; реальный Plane шлёт name — не критично).
- Tokenator: квота исчерпана (116k), ждать сброса (~1 число) или саппорт. OpenClaw не блокирует (фолбэк), Claude CLI агентов не касается.
---
## PR #13 — вход/выход analyst: 3 бага закрыты, боевой прогон чистый (03.06 ~20:30)
**Merge main = `dce9ac8`.** Ветка `fix/taskmd-description` из main `2d392b6` (после PR #12). Задеплоен (rebuild+recreate). 3 коммита на remote (ORCH-7 проверено): `b91be74`, `96c5e6b`, `a9cdb17`.
### Баг A (главный) — description дотягивался, но НЕ доходил до analyst
`src/webhooks/plane.py`, `start_pipeline` task_desc (~стр.512→522): собирался БЕЗ description (`...Stage: analysis\nTitle: {name}`), хотя description дотянут на ~401 (445 chars в логе). `.task.md` = 101 байт → analyst писал «business request пуст» (его заключение было ПРАВИЛЬНЫМ — ему дали пустышку). **Фикс:** добавлен `\n\nDescription:\n{description}` (финальная переменная после дотяжки, не payload). После фикса `.task.md` = 973 байта с полным ТЗ.
### Баг B — name=untitled
`start_pipeline` ~376: `name = data.get("name","untitled")` → на issue.updated Plane не шлёт name → untitled (ветка feature/ET-011-untitled, Title untitled). Тот же класс что баг1(description), но для name фикс не делали. **Фикс:** новая `fetch_issue_fields(issue_id, project_id) -> (name, description)` в plane_sync.py рядом с fetch_issue_description (его НЕ тронули, переиспользовали endpoint/токен). **Один GET** вместо двух. В start_pipeline name тянется ВЫШЕ построения slug/branch (~469). Пустой name → fallback untitled, не падает. После фикса ветка = `feature/ET-011-popup-enduro-trails`.
### Баг C — коммент analyst устарел + нет ссылок
`src/stage_engine.py`, ветка artifacts-ready→In Review (`_handle_analysis_approved_flow`). Новый `_build_analyst_ready_comment`: текст просит статус **Approved** (НЕ `:approved:`, НЕ In Progress — подтверждено Славой: одобрение=Approved). `<a href>`-ссылки в comment_html на реально существующие доки (os.path.isfile по worktree). База `settings.gitea_url` + `settings.gitea_owner`.
### Тесты (мой прогон сам): **207 passed + 9 failed** (те же 9 baseline pre-existing signature/401, regression 0, +3 новых).
test_taskdesc_includes_description, test_name_fetched_when_payload_empty, test_analyst_comment_asks_approved_with_links. Обновлены 3 мока в test_pipeline_start_bugs.py (fetch_issue_description→fetch_issue_fields). Коммиты: `fix(pipeline): pass issue description to analyst task file`, `fix(pipeline): fetch issue name from Plane API on status-trigger start`, `feat(plane): analyst comment asks for Approved status + links docs`.
### ✅ БОЕВОЙ ПРОГОН — всё чисто:
Почистил прошлый прогон, #6 в Backlog с реальным name → In Progress → analyst run 62 (exit 0, 26302 out, $1.60). Логи: `pulled name from Plane API ('Скачивание трека из popup на карте')`, `.task.md` 973 байта, BRD осмысленный («Пользователь видит публичные GPS-треки... возможности скачать нет...»), `comment.created no pipeline action`, `issue updated 38fb1f64 no pipeline action`, `analyst finished, requested Approved status`. **#6 = In Review ДЕРЖИТСЯ.**
### ⚠️ Хвост (для следующей сессии):
- ~~Ссылки в комменте localhost~~ → ИСПРАВЛЕНО в PR #14 (см. ниже).
---
## PR #14 — ссылки в комменте на внешний Gitea (03.06 ~20:00)
**Merge main = `b01643f`.** Ветка `fix/gitea-public-url`, 1 коммит `ca63bc2`. Задеплоен.
- **Проблема:** ссылки строились от `settings.gitea_url` = localhost:3000 (внутренний, для git-операций clone/push) → из браузера не кликаются. Менять gitea_url НЕЛЬЗЯ (сломает клоны).
- **Решение:** новое поле `gitea_public_url` в config.py (env `ORCH_GITEA_PUBLIC_URL`), fallback на gitea_url при пустом. В `_build_analyst_ready_comment` (stage_engine.py ~299): `base = (getattr(settings,'gitea_public_url','') or settings.gitea_url).rstrip('/')`. gitea_url НЕ тронут.
- **Env на проде** (ассистент дописал в `.env`, бэкап `.env.bak-*`): `ORCH_GITEA_PUBLIC_URL=https://git.mva154.duckdns.org`. Домен живой (HTTP 200).
- Тесты сам: 208 passed + 9 baseline (regression 0).
-**Боевой прогон (task 29, run 63, exit 0, $1.37):** ссылки в комменте = `https://git.mva154.duckdns.org/admin/enduro-trails/.../ET-011/*.md` — внешние, кликабельные. Коммент про Approved корректен. #6 = In Review держится.
### Открытый хвост:
- HTML коммента `<p>...<ul>...</ul></p>` — формально невалидная вложенность (Plane/TipTap переваривает, ссылки рабочие). Косметика.
- **#6 висит в In Review, ждёт Славу.** Approved → должна уехать в Architecture БЕЗ мелькания In Progress (PR #12, ещё не проверено живьём). Слава решит: сам в UI или мне симулировать.
- Боевая ветка теперь `feature/ET-011-popup-enduro-trails` (slug исправлен).
---
## PR #14 — ссылки в комменте analyst: localhost → внешний публичный Gitea (03.06 ~20:50)
**Merge main = `b01643f`.** Ветка `fix/gitea-public-url` из main `dce9ac8` (после PR #13). Один коммит `ca63bc2` `feat(config): external gitea_public_url for clickable doc links`. Задеплоен.
### Проблема
Ссылки на доки в комменте analyst строились от `settings.gitea_url` = `http://localhost:3000` (env `ORCH_GITEA_URL`) — это ВНУТРЕННИЙ URL для git-операций (clone/push из docker), из браузера Славы не кликается.
### Решение (НЕ менять gitea_url — он для git!)
- `src/config.py`: новое поле `gitea_public_url: str = ""` (env `ORCH_GITEA_PUBLIC_URL`, префикс ORCH_, fallback на gitea_url).
- `src/stage_engine.py` `_build_analyst_ready_comment` (~стр.299): `base = (getattr(settings,"gitea_public_url","") or settings.gitea_url).rstrip("/")`. Только эта строка.
- `gitea_url` НЕ тронут — git-операции по-прежнему localhost.
### Env на проде (ставила Я, не Dev): `ORCH_GITEA_PUBLIC_URL=https://git.mva154.duckdns.org` дописан в `.env` (аккуратно, не затирая), проверен в контейнере.
### Тесты (мой прогон): **208 passed + 9 failed** (те же baseline pre-existing). test_analyst_comment_asks_approved_with_links расширен (public задан→ссылки от него) + новый test_analyst_comment_falls_back_to_gitea_url.
### ✅ БОЕВОЙ ПРОГОН (task 29, run 63, exit 0, 20530 out, $1.37): ссылки в комменте на `https://git.mva154.duckdns.org`, кликабельны, все 6 доков. Коммент правильный (Approved/Rejected). **#6 = In Review ДЕРЖИТСЯ.**
### ⚠️ Состояние на конец сессии:
- **#6 висит в In Review, ждёт вердикта Славы** в Plane (ссылки рабочие — глянуть доки → Approved или Rejected+причина).
- **НЕ проверено живьём:** переход Approved → Architecture без мелькания In Progress (PR #12). Слава решит сам в UI или симулировать.
- Был сбой PGPASSWORD на UPDATE статуса при ручном переводе #6 — webhook всё равно прошёл (analyst стартанул). Если повторять ручной перевод статуса через psql — подставлять реальный `$PW`, не литерал.
- HTML коммента `<p>...<ul>...</ul></p>` — формально невалидная вложенность (Plane переваривает, косметика).
### Хронология багов входа/выхода analyst (закрыто за сессию):
PR #12 (status-only verdict, баг3 эхо-самоудар) → PR #13 (баги A description, B name, C коммент+ссылки) → PR #14 (ссылки на внешний домен). Все боевые прогоны чистые, #6 In Review стабилен.
---
## PR #15 — БАГ 4: Approved из analysis не двигал конвейер (03.06 ~20:32)
**Merge main = `b6aa107`.** Ветка `fix/approved-advances-stage`, коммит `0b8013c` `fix(stage): approved verdict advances analysis->architecture instead of re-running gate`. Задеплоен.
- **Симптом (боевой, Слава нажал Approved на #6):** лог `advance from analysis` → 200 OK, но task 29 застрял stage=analysis, architect НЕ запущен, в Architecture не уехало. Блокер.
- **Корень:** stage_engine.py advance_stage ветка `check_analysis_approved` ВСЕГДА `_handle_analysis_approved_flow(...) + return result` → блок Advance недостижим. А _handle_analysis_approved_flow при agent=None (webhook Approved) делал `return` (т.к. `if not (agent=='analyst' ...)`). Два пути через один тупик.
- **Фикс:** развести по `finished_agent`: agent=='analyst' (launcher) → approved-flow+return (In Review+коммент, как было); agent is None (Approved-вердикт) → НЕ заходить в flow, провалиться в общий Advance (update_task_stage(architecture)+enqueue architect).
- Сама проверила ДО фикса: `get_agent_for_stage('analysis')`=architect, `get_next_stage('analysis')`=architecture → фикс не зациклит analyst.
- Тесты сама: 210 passed + 9 baseline. Новый test_approved_verdict_advances_analysis_to_architecture зелёный.
-**БОЕВОЙ:** догнала Approved (task 29 застрял) → послала Approved-вердикт → `analysis -> architecture (auto-advance after None)`, enqueued architect job 11, run 64 pid 47. Plane #6 = Architecture (3020bbb7). **Конвейер впервые прошёл стадию end-to-end через ручной Approved.**
- architect запущен, проектирует. Следить: создаст docs/work-items/ET-011/ архитектуру → Development.
### Хронология (вся сессия): PR#12 (status-only, баг3 эхо) → PR#13 (баги A desc/B name/C коммент+ссылки) → PR#14 (ссылки внешний домен) → PR#15 (баг4 Approved-advance). Все боевые чистые.
---
## PR #15 — БАГ 4: ручной Approved из analysis НЕ двигал конвейер (03.06 ~20:30) — БЛОКЕР, закрыт
**Merge main = `b6aa107`.** Ветка `fix/approved-advances-stage` из main `b01643f` (PR #14). Коммит `0b8013c` `fix(stage): approved verdict advances analysis->architecture instead of re-running gate`. Задеплоен.
### Симптом (боевой)
Слава перевёл #6 (task 29) в **Approved** из analysis. Лог: `Task 29: Approved status -> advance from analysis``200 OK`, НО task остался `stage=analysis`, architect НЕ запущен, в Architecture не уехало. Конвейер встал на первой стадии.
### Корень (`src/stage_engine.py` `advance_stage`, ~стр.193)
Ветка `if qg_name == "check_analysis_approved": _handle_analysis_approved_flow(...); return result`**ВСЕГДА** ранний return → блок Advance (~стр.228: update_task_stage + enqueue_job) недостижим. А `_handle_analysis_approved_flow` (~стр.312) в начале `if not (agent=="analyst" and work_item_id): return` → на webhook-пути Approved (`finished_agent=None`) выходил ничего не сделав. Доп.нюанс: `check_analysis_approved` ищет `:approved:` **коммент**, а Слава ставил **статус** Approved → QG вернул бы False и снова заблокировал advance даже при простом fall-through.
### Фикс — развести ветку по `finished_agent`
- `agent == "analyst"` (launcher) → `_handle_analysis_approved_flow` + return (In Review + коммент, НЕ ломать).
- `agent is None` (webhook Approved-вердикт) → `result.qg_passed=True`, `qg_reason="approved-via-status"`, НЕ заходить во flow, НЕ перезапускать QG → провалиться в блок Advance (analysis→architecture + enqueue architect).
- Прочие QG обёрнуты в `else: passed,reason=_run_qg(...)`.
### Подтверждено: `get_agent_for_stage("analysis")=architect` (не analyst!), `get_next_stage("analysis")=architecture` (`src/stages.py:14`). Зацикливания analyst нет.
### Тесты: **210 passed + 9 pre-existing failed** (test_webhooks HMAC/401). +2 новых: `test_approved_verdict_advances_analysis_to_architecture` (finished_agent=None→advanced/architecture/architect, QG замокан на fail = доказывает что гейт не перезапускается), `test_launcher_path_does_not_advance_and_calls_flow` (finished_agent=analyst→не advance, flow вызван). test_stage_engine: 18→20.
### ✅ БОЕВОЙ ПРОГОН (task 29): отправила Approved-вердикт заново → `analysis→architecture` auto-advance, #6 уехал в **Architecture**, **architect запущен** (job 11, run 64, pid 47), без мелькания In Progress. Первый полный end-to-end проход стадии через ручной Approved.
### 🔥 Конвейер поехал дальше сам (пока писала доки):
- architect завершился (exit 0, 49758 tokens): создал `docs/work-items/ET-011/06-adr/ADR-014-gpx-download-endpoint.md`, `ADR-015-source-redistribution-policy.md`, `07-infra-requirements.md`.
- **architecture → development** auto-advance, **developer запущен** (run 65) — пишет код.
- ⚠️ `QG check_ci_green — failed: CI state: failure` — CI красный (ожидаемо, кода ещё нет / early-fail). developer работает.
- **Итог:** три стадии подряд (analysis→architecture→development) с одного Approved.
## Документация обновлена (03.06) — по запросу Славы «обнови доки + запиши баги»
- **`tasks/orchestrator/ORCHESTRATOR_DOCS.md`**: новый раздел «🐛 Баги входа/выхода analyst (03.06.2026)» — таблица 4 багов; обновлены статус-шапка, переходы статусов (status-only model PR#12), ссылки (gitea_public_url PR#14), webhook events, Changelog (PR #12#15).
- **`tasks/orchestrator/STATUS.md`**: свежий блок 03.06 + таблица багов сессии.
- **wiki ingest** обеих доков (правило: при работе с проектами обновлять онтологию + wiki).
### Сводка 4 багов сессии (для быстрой ссылки):
- **Баг 3** PR #12 (`status-only verdict`): эхо-самоудар — собственный апдейт статуса перезапускал стадию.
- **Баг A** PR #13: `description` (Plane API, 445 симв) не попадал в `.task.md` (был 101-байт пустышка).
- **Баг B** PR #13: `name` не тянулся → ветка `untitled`.
- **Баг C** PR #13: устаревший коммент analyst «Жду :approved:» + нет ссылок на доки.
- **PR #14**: ссылки в комменте на `localhost:3000` → внешний `gitea_public_url=https://git.mva154.duckdns.org`.
- **Баг 4** PR #15 (БЛОКЕР): ручной Approved не продвигал analysis→architecture.
### Хвост на следующую сессию:
- Следить за developer (run 65) + CI: дойдёт ли до review/testing/deploy.
- Старые разделы ORCHESTRATOR_DOCS про `:approved:`/`:rejected:` комментом — переписаны на status-only, но перепроверить остаточные упоминания в нижней части файла (~стр.100+).
---
## Баги 5-6 закрыты (03.06 вечер) — конвейер дошёл до честного CI-гейта
### Баг 5 (PR #16, main 994f73a): QG check_tests_local зависел от `make` (нет в контейнере)
- `src/qg/checks.py` check_tests_local: `["make","test"]``["python","-m","pytest","../../tests/","-v"]`, cwd=`<repo>/src/api` (1:1 с Makefile цели test). Коммит `90c9ffe`.
- Тесты: 213 passed + 9 baseline. Боевой прогон: make-бага ушла, но вылез баг 6.
### Баг 6 (PR #17, main 7922f6b): локальный QG дублировал CI + гонял тесты enduro-trails в окружении оркестратора без зависимостей (lxml/shapely/defusedxml → ModuleNotFoundError)
- **Задумка check_tests_local изначально:** S-1 затычка эпохи «Gitea CI не настроен → always false». CI теперь настроен (.gitea/workflows/ci.yml) → затычка устарела, дублирует check_ci_green в кривом окружении.
- **Вариант 1 (выбран Славой):** довериться CI.
- `src/stages.py:16` development QG: `check_tests_local``check_ci_green`.
- `src/webhooks/gitea.py` ~219: снято подавление CI-failure, теперь `state==failure && stage==development` → notify_qg_failure.
- `check_tests_local` НЕ удалён, помечен DEPRECATED, остался в QG_CHECKS, не wired ни к одной стадии.
- Коммит `e15d339`. Тесты: 215 passed + 10 failed (9 baseline + 1 новый webhook-тест на том же 401 HMAC-барьере — среда, не логика).
- Деплой ✅, `get_qg_for_stage("development")=="check_ci_green"` подтверждён на проде.
### 🎯 Боевой прогон ET-011 (task 29): конвейер РАБОТАЕТ КАК ЗАДУМАНО
- Прямой вызов `handle_ci_status(success)``check_ci_green` (double-check API) → **False: CI state: failure** → справедливо НЕ пропустил development→review. Логика верна.
- **Реальный CI ET-011:** lint=failure (ruff стиль), test=**success** (тесты в правильном окружении ПРОХОДЯТ!), build=skipped.
- Т.е. конвейер уперся не в свой косяк, а в РЕАЛЬНОЕ качество кода — линтер честно краснит на стиле. Здоровое состояние.
### Хвост на след. сессию:
- task 29 стоит в development, CI красный ТОЛЬКО из-за ruff lint (тесты зелёные).
- Развилка: (A) developer-retry чтобы сам починил lint, или (B) глянуть на что ruff ругается. Слава склонялся глянуть ruff сначала.
- HMAC webhook через curl/shell даёт 401 (подпись от raw body не сходится при shell-сериализации) — для боевых прогонов дёргать обработчики напрямую в python (docker exec orchestrator python3), а не через HTTP.
### Итог сессии 03.06: закрыто 6 багов (PR #12-#17), конвейер прошёл analysis→architecture→development end-to-end, дошёл до честного CI-гейта на качестве кода.
---
## Баг 7 закрыт (03.06 ночь) — конвейер САМОВОССТАНАВЛИВАЕТСЯ на красном CI 🔥
### Баг 7 (PR #18, main a0621b9): красный CI на development не возвращал задачу developer'у
- **Дыра:** после бага 6 CI стал авторитетным гейтом, но handle_ci_status при failure делал ТОЛЬКО notify_qg_failure → конвейер вис, требовал ручного вмешательства. Слава прямо спросил «почему автономно не ушло на девелопера».
- **Корень:** retry developer'а был только на review request_changes (max 3x), на CI-failure ветки retry не было — побочка нашего же фикса бага 6 (достроили мост наполовину).
- **Фикс (по образцу review-retry):** src/webhooks/gitea.py CI-failure ветка ~стр.219 — после notify_qg_failure добавлен блок: retry_count = COUNT agent_runs developer; if < MAX_DEV_RETRIES(=3) → enqueue_job("developer", attempt N/3); else notify_error escalating. Отличия от review: задача УЖЕ в development (без update_task_stage), branch из переменной branch. Коммит `3a285de`.
- **Лимит:** общий MAX_DEV_RETRIES=3 по ВСЕМ agent_runs developer'а (review+CI вместе ≤3) — по требованию Славы «ограничить как на ревью».
- Тесты: 217 passed (215+2 pure-logic, вызов handle_ci_status напрямую с моками, не через TestClient → обход 401 HMAC baseline) + 10 baseline failed.
### 🎯🔥 БОЕВОЙ прогон — САМОВОССТАНОВЛЕНИЕ РАБОТАЕТ
- Дёрнула CI-failure на ET-011 (task 29) → оркестратор АВТОНОМНО: увидел красный CI → check_ci_green failed → поставил developer'а в очередь (job 13) → запустил run_id=66 pid=45 (attempt 2/3). developer runs 1→2.
- Лог: `🚀 ET-011: developer запущен (run_id=66)`. Без единого ручного пинка.
- **developer run 66 пошёл чинить 10× E402 в src/api/main.py** (импорты не в шапке: shapely/typing/fastapi после кода на стр.20). Если починит → зелёный CI → development→review автономно.
### Хвост на след. сессию:
- Следить за developer run 66: починит ли E402, перезапушит ли, станет ли CI зелёным, поедет ли конвейер в review.
- Если E402 — намеренный поздний импорт (после sys.path), developer может добавить noqa вместо перетасовки — оба варианта валидны.
- Лимит retry: после 3 попыток developer'а суммарно (review+CI) — эскалация Славе.
### ИТОГ сессии 03.06: закрыто 7 БАГОВ (PR #12-#18). Конвейер: analysis→architecture→development end-to-end + честный CI-гейт + АВТОНОМНОЕ самовосстановление на красном CI с лимитом попыток. Боевая обкатка на ET-011.
---
## Документация багов 5-7 + точная причина красного CI (03.06 ночь, конец сессии)
### Точная причина красного CI на ET-011 (диагностирована вживую)
- Прогнала `ruff check src/` (скопировав src в /tmp, без кэша — worktree принадлежит контейнеру, permission denied на кэш; ruff есть на ХОСТЕ ~/.local/bin/ruff 0.15.13, в КОНТЕЙНЕРЕ оркестратора ruff НЕТ — то же окружение что lxml).
- **Единственная придирка: E402 «module level import not at top of file», 10× в ОДНОМ файле `src/api/main.py`.** developer добавил импорт (shapely.geometry LineString для ET-011) не в шапку, а ниже кода (видимо после sys.path/константы ~стр.20-21).
- **Вердикт: косметика, не баг кода.** Тесты зелёные, краснит только стиль. Чинится: поднять импорты в шапку ИЛИ `# noqa: E402` (если поздний импорт намеренный после sys.path.insert).
### Документация записана в ORCHESTRATOR_DOCS.md (дополнено, НЕ затёрто)
1. **Pipeline + Quality Gates таблицы:** выход development гейтится `check_ci_green`елёный→review, красный→retry). `check_tests_local` помечен DEPRECATED (в реестре QG_CHECKS для совместимости, не подключён к стадиям). Принцип: оркестратор НЕ гоняет тесты чужих проектов в своём контейнере — это делает Gitea CI в правильном окружении.
2. **Раздел «Механизмы автономности» → новый блок Retry (CI fail):** красный CI → notify_qg_failure → возврат developer'у (attempt N/3), лимит ОБЩИЙ с review (review+CI вместе ≤3), потом эскалация. Помечено ✅ проверено боевым прогоном.
3. **Новый раздел «🐛 Баги QG-гейта development (PR #16#18)»** — таблица баг/PR/корень/фикс для багов 5/6/7.
4. **Changelog:** записи PR #16 (баг5 make→pytest), #17 (баг6 drop local QG→check_ci_green), #18 (баг7 CI-fail→developer retry). Шапка обновлена.
### Wiki + онтология
- `openclaw wiki ingest` прогнан после правок доков (правило: ingest после изменений в проектах).
- Онтология: предложила Славе занести баги 5-7 как записи проекта orchestrator — ждёт ответа (на момент flush не подтверждено).
### Хвост (актуально на след. сессию)
- developer run 66 чинит E402 в src/api/main.py. Следить: починит→перезапушит→зелёный CI→development→review автономно.
- Эпизод сбоев обработки turn (#16540 и повтор по PR #17/#18 completion event) — отвечала заново коротко, событие было дублем уже обработанного. NO_REPLY на повторные internal completion events корректен.
- HMAC webhook через shell-curl = 401 (подпись от raw body не сходится при shell-сериализации). Для боевых прогонов — дёргать обработчики напрямую в python (`docker exec orchestrator python3 -c "import asyncio; from src.webhooks.gitea import handle_ci_status; asyncio.run(...)"`), НЕ через HTTP. Записано как рабочий приём.
### Все хэши/PR сессии 03.06 (для истории)
- PR #12 status-only (баг3) | #13 баги A/B/C | #14 gitea_public_url | #15 баг4 Approved→advance (merge b6aa107)
- PR #16 баг5 make→pytest (merge main 994f73a, `fix(qg): run pytest directly instead of make`)
- PR #17 баг6 drop local QG (commit e15d339, merge main 7922f6b, `fix(qg): use check_ci_green instead of local tests on development stage`)
- PR #18 баг7 CI-fail retry (commit 3a285de, merge main a0621b9, `fix(ci): bounce task back to developer on red CI (capped retries)`)
---
## 🏆 ИСТОРИЧЕСКОЕ: ET-011 прошёл ВЕСЬ конвейер автономно (03.06 ночь, 02:12)
**task 29 → stage `done`.** Первый в истории полный сквозной автономный прогон оркестратора с одного Approved Славы.
### Цепочка runs (все exit 0):
```
analyst(63) → architect(64) → developer(65)
→ [красный CI E402] → developer retry(66) → reviewer(67)
→ [reviewer request_changes] → developer(68) → reviewer(69) ✅APPROVED
→ tester(70) ✅ → deployer(71) ✅ → DONE
```
### Что отработало автономно (БЕЗ ручного касания после Approved):
- **2 цикла самовосстановления:** (1) CI-retry баг 7 — красный CI E402 → developer сам поднял импорты + объявил deps в pyproject (commit 7d8407a3 `fix(ci): hoist imports to satisfy E402 + declare runtime/test deps`); (2) reviewer request_changes → ещё один developer-цикл (68→69).
- CI позеленел полностью: lint/test/build × push+PR = success.
- Конвейер сам: development→review→testing→deploy→done. Plane статусы синхронизировались.
### Итог: оркестратор сам пишет код, гоняет CI, чинит свои ошибки, ревьюит, тестирует, деплоит. С одного Approved.
### Вопрос Славы «почему 2 сообщения на deploy» — ОТВЕЧЕНО:
- Это 2 РАЗНЫХ события (не дубль): `notify_stage_change` (deploy→done, смена СТАДИИ) + `notify_agent_finished` (deployer завершил 4 мин, exit 0). Приходят парой на каждом шаге. notifications.py:53 vs :71.
- TODO-косметика на будущее: можно слить в одно сообщение на финальной стадии.
## Онтология обновлена (03.06, с ОК Славы)
- Создан **Project `proj_orchestrator`** (status active, folder tasks/orchestrator/, doc ORCHESTRATOR_DOCS.md) — раньше проекта в графе НЕ БЫЛО вообще.
- Баги 5/6/7 как **Task(status=done)** (типа Bug в схеме нет): task_orch_bug5/bug6/bug7, привязаны has_task к проекту. Детали PR/коммитов в description.
- Правило подтверждено: баг = Task done (фикс), не отдельный тип.
---
## Баг 8 + вариант А1 закрыты (03.06 глубокая ночь) — ET-011 РЕАЛЬНО заработала на проде
### Контекст: Слава показал что ET-011 done, НО файл не качается (главная цель таски)
- Конвейер пометил done, но GPX не скачивался. Спуск по слоям:
1. На проде старый образ (контейнер Up 37h) — `/download` эндпоинт отсутствовал (404). Корень: deploy-hook упал.
2. Разлочила прод: пересобрала enduro-trails контейнер из main (docker compose up -d --build) → /download появился.
3. Скачивание → 403 source_forbidden (политика ADR-015: enduro_russia download_allowed=false, ВСЕ 286 треков из этого источника).
### Вариант А1 (решение владельца, с предупреждением о юр.риске ToS EnduroRussia)
- config/gps_sources.yaml: enduro_russia download_allowed false→true (точечно, wikiloc/ttrails остались false). Файл root-owned → правила через docker-root (alpine volume), т.к. slin без sudo.
- Рестарт app (конфиг — volume :ro, без пересборки) → 🎯 GPX СКАЧИВАЕТСЯ: HTTP 200, 265KB, валидный GPX 1.1, content-disposition с кириллицей, xmllint OK.
- Закоммичено в git навсегда: enduro-trails PR #23 (commit 81c3394, merge b6b21aa). main защищён pre-receive (прямой push запрещён) → через ветку+PR.
### Баг 8 (PR #19 orchestrator, main 2629dff): deploy без QG-гейта
- Корень: deployer честно написал Status:FAILED, но задача в done. (1) stages.py deploy qg:None — гейта выхода НЕТ; (2) exit_code=код LLM-процесса (всегда 0) → launcher.py:475 не сработал.
- Фикс (Dev, commit e4a9c48): новый QG check_deploy_status по образцу check_reviewer_verdict — читает frontmatter deploy_status из 14-deploy-log.md. deploy.qg=check_deploy_status. SUCCESS→done, FAILED→откат development+set_issue_blocked+notify по ВЕРДИКТУ (stage_engine _handle_qg_failure_rollbacks). 227 passed (217+10) + 10 baseline.
- Правка 4 (deployer-промпт enduro-trails .openclaw/agents/deployer.md): обязал писать YAML-frontmatter deploy_status:SUCCESS/FAILED в 14-deploy-log.md. Коммит 7f6b39a в PR #23.
- ⚠️ fail-safe: без frontmatter check_deploy_status→False→откат (не done). Поэтому правка 4 обязательна вместе с PR #19.
### Инфра-блокер (TODO ops, НЕ чинила — нужен root):
- deploy-hook падает: /var/log/enduro-trails root-owned, slin без write и без NOPASSWD sudo. Фикс: sudo chown slin:slin /var/log/enduro-trails ИЛИ сменить LOG-путь в hook. До этого автодеплой enduro-trails будет падать (но теперь честно — баг 8 откатит, не пометит done).
### Онтология: task_orch_bug8 (done, urgent) добавлен, has_task→proj_orchestrator. Wiki переиндексирована.
### ИТОГ: закрыто 8 БАГОВ (PR #12-#19 orchestrator + enduro-trails #23). ET-011 РЕАЛЬНО работает на проде — GPX качается. Урок бага 8: зелёный CI ≠ работает на проде, нужен вердикт-гейт деплоя.