From a61a23d6ac43b145ff68abfe19243f7439a6abdb Mon Sep 17 00:00:00 2001 From: Stream Date: Wed, 3 Jun 2026 22:10:01 +0300 Subject: [PATCH] auto-sync: 2026-06-03 22:10:01 --- memory/2026-06-03.md | 11 +++ .../DEV_TASK_STATUS_ONLY_VERDICT.md | 73 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 tasks/orchestrator/DEV_TASK_STATUS_ONLY_VERDICT.md diff --git a/memory/2026-06-03.md b/memory/2026-06-03.md index 08049d0..3ad7cc9 100644 --- a/memory/2026-06-03.md +++ b/memory/2026-06-03.md @@ -146,3 +146,14 @@ approved=a519a341-dada-4a91-8910-7604f82b79c5 rejected=ba958f3c-5db5-461d- **Фикс (для 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. diff --git a/tasks/orchestrator/DEV_TASK_STATUS_ONLY_VERDICT.md b/tasks/orchestrator/DEV_TASK_STATUS_ONLY_VERDICT.md new file mode 100644 index 0000000..8fb933f --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_STATUS_ONLY_VERDICT.md @@ -0,0 +1,73 @@ +# DEV TASK — Вердикт ТОЛЬКО статусами (выпил комментного approve + фикс эхо-самоудара) + +**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500) +**Ветка:** `fix/status-only-verdict` из свежего main (`git checkout main && git pull && git checkout -b fix/status-only-verdict`). + +⚠️ **ГРАБЛЯ push (ORCH-7):** после push `git log origin/main..origin/fix/status-only-verdict` ДОЛЖЕН показать коммиты ДО отчёта «PR готов». +ℹ️ Токен Gitea для PR: `docker exec orchestrator printenv ORCH_GITEA_TOKEN`. + +## Контекст и ПРАВИЛЬНАЯ модель (источник правды — решение Славы, подтверждено 03.06) +Слава управляет конвейером **ТОЛЬКО СТАТУСАМИ**. **Комменты НИКОГДА не триггерят переходы.** Точная модель: +- **Одобрение стадии** → Слава ставит статус **Approved** → оркестратор двигает на следующую стадию (сам выставляет статус следующей стадии). +- **Отклонение** → Слава **сначала пишет коммент с причиной**, **потом** ставит статус **Rejected** → оркестратор откатывает + читает причину из последнего коммента. +- **Ответ на вопросы аналитика** (задача в **Needs Input**) → Слава пишет коммент(ы) с ответами, **потом сам возвращает статус в In Progress** → оркестратор перезапускает агента текущей стадии (analyst читает комменты из Plane). Триггер — СТАТУС In Progress, НЕ коммент. + +**КОММЕНТНЫЙ МЕХАНИЗМ УПРАВЛЕНИЯ ВЫПИЛИВАЕТСЯ ПОЛНОСТЬЮ.** `:approved:`/`:rejected:` в комментах + «answer-to-questions по комменту» — всё убрать. Они создавали баг 3 (эхо-самоудар): analyst постит свой коммент «жду approved» → handle_comment ловит свой же коммент → откатывает In Review → In Progress. + +## БАГ 3 (root) — самоудар + лишний PATCH +**Симптом:** analyst довёл задачу до **In Review** корректно, через 0.1с статус откатился в **In Progress**, уведомления нет. +**Две причины:** + +### Причина A — handle_comment управляет конвейером (выпилить весь механизм) +`src/webhooks/plane.py`, `handle_comment` (~397-490): **весь комментный механизм управления выпиливается.** +- Ветка `if ":approved:" in comment_body:` (~427-431) → **ВЫПИЛИТЬ.** +- Ветка `if ":rejected:" in comment_body:` (~420-424) → **ВЫПИЛИТЬ.** +- Ветка `if current_stage == "analysis":` (answer-to-questions по комменту, ~433-490) → **ВЫПИЛИТЬ.** Ответ на вопросы теперь идёт через возврат СТАТУСА в In Progress (см. ниже handle_status_start), не через коммент. +- Итого `handle_comment` либо удаляется целиком (и роутер `comment.created` → no-op/лог), либо сводится к чистому логированию без side-effect. Реши сам — главное: **ни один коммент не меняет статус и не запускает агентов.** Сохрани QG-лог если он информативен, но без действий. + +### Причина A2 — возврат в In Progress из Needs Input должен ПЕРЕЗАПУСКАТЬ агента +`handle_status_start` (~161-172) сейчас идемпотентен: `if existing task → not restarting`. Это правильно для защиты от дублей, НО ломает кейс «ответ на вопросы»: Слава вернул задачу из **Needs Input** в **In Progress**, ожидая что analyst перезапустится. **Надо:** если task существует И задача «ждала» (предыдущий статус был needs_input / стадия analysis в ожидании) → **перезапустить агента текущей стадии** (enqueue), чтобы он прочитал новые комменты Славы. +- Как отличить «обычный повторный In Progress» (защита от дублей) от «ответ на вопросы»: проверь предыдущий Plane-статус issue (был needs_input) ИЛИ флаг ожидания в БД. Определи самый надёжный способ на проде (есть ли в БД поле статуса/ожидания; или GET issue даёт прошлый статус через activity). Если нет running job по task И стадия активная (analysis) → перезапуск analyst с пометкой «прочитай свежие комменты Славы и доработай артефакты». Защита от дублей (нет двойного запуска при двойном webhook) — сохрани через проверку running job в очереди. +- ⚠️ Если надёжно определить «был needs_input» сложно — отчитайся с вариантом реализации ДО мержа (это ключевая развилка). Минимально: рестарт если стадия analysis И нет активной/running job для task. + +### Причина B — handle_verdict(Approved) сам сбивает статус в In Progress +`handle_verdict` (~170-200), ветка `if approved:` (~185): +```python +set_issue_in_progress(work_item_id) # ← УБРАТЬ +await _try_advance_stage(...) +``` +`set_issue_in_progress` здесь лишний — он откатывает статус назад перед advance. `_try_advance_stage` сам выставит статус следующей стадии (architecture/development/...). **Убрать `set_issue_in_progress` из ветки approved.** Проверь, что `_try_advance_stage` действительно сам ставит статус стадии (если НЕ ставит — тогда вместо in_progress выставить статус нужной стадии, НЕ in_progress). + +## REJECT: причина из коммента в handle_verdict +Сейчас `handle_verdict(approved=False)` передаёт фиксированную заглушку причины. **Надо:** при Rejected-статусе дотянуть **последний коммент** issue из Plane API (GET comments, взять самый свежий от человека) как `reason` и передать в `_rollback_stage`. Слава пишет причину отдельным комментом ПЕРЕД/вместе со сменой статуса. +- GET comments endpoint: `{PLANE_BASE}/workspaces/{WORKSPACE}/projects/{pid}/issues/{issue_id}/comments/` (тот же PLANE_HEADERS). Взять последний по created_at, стрипнуть HTML. +- Если коммента нет → заглушка «Rejected via status, no reason comment». + +## Ограничения +- 🚫 НЕ трогай: nginx/openclaw.json/.env-секреты/deploy-хук/HMAC/verify_plane_signature/очередь(queue_worker/jobs кроме чтения)/webhook URL в БД Plane/per-agent authorship/PLANE_STATES UUID/M-6 derive/uniqueness-guard(из PR #11)/fetch_issue_description(PR #11)/conftest.py. +- Conventional Commits, отдельные коммиты: `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`. +- ⚠️ Старые тесты `test_webhooks.py::test_plane_approved_advances_stage`, `test_plane_rejected_rolls_back`, `test_verdict_status.py` проверяют КОММЕНТНЫЙ `:approved:`/`:rejected:`. Раз комментный механизм выпиливается — **перепиши их под статусную модель** (approve/reject через handle_verdict по СТАТУСУ). Тесты, проверяющие что коммент `:approved:` двигает стадию — перепиши на обратное утверждение: «коммент НЕ триггерит переход». + +## Тесты (контейнер) +`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` +Новые/обновлённые: +- `test_inreview_comment_does_not_revert`: задача в In Review, прилетает ЛЮБОЙ коммент → статус НЕ меняется, агенты НЕ запускаются. **Главный тест бага 3.** +- `test_any_comment_no_pipeline_action`: коммент с `:approved:`/`:rejected:`/произвольным текстом → никаких side-effect (статус/очередь не трогаются). +- `test_approved_status_advances_without_inprogress_reset`: Approved-статус → advance, БЕЗ промежуточного PATCH в in_progress. +- `test_rejected_status_pulls_reason_from_comment`: Rejected-статус + последний коммент с причиной (мок GET comments) → reason передан в rollback. +- `test_inprogress_from_needs_input_relaunches_analyst`: задача в analysis/needs_input → возврат статуса в In Progress → analyst перезапущен (enqueue), читает комменты. + защита: двойной In Progress webhook не плодит двойной запуск. +- Перепиши `test_plane_approved_advances_stage`/`test_plane_rejected_rolls_back`/`test_verdict_status` под статусную модель. + +## Проверка (ассистент проверит вживую боевым прогоном) +| # | Что | Критерий | +|---|-----|----------| +| 1 | In Review держится | analyst → In Review, статус НЕ откатывается, уведомление с просьбой одобрить приходит | +| 2 | Approved-статус | Слава ставит Approved → задача уходит в Architecture, БЕЗ мелькания In Progress | +| 3 | Rejected-статус+коммент | Слава ставит Rejected + коммент причины → откат, analyst получает причину | +| 4 | коммент не рулит | `:approved:` в комменте больше НЕ двигает задачу | +| 5 | тесты | baseline не сломан + новые passed | +| 6 | git | PR в main, remote содержит коммиты | + +## Отчёт +- НЕ деплоить, НЕ мержить (мерж — ассистент после живой проверки + боевого прогона #6). +- В отчёте: что именно выпилил (файл:строки), **как реализовал перезапуск агента при возврате In Progress из Needs Input** (как отличаешь от защиты-дублей — ключевая развилка, опиши подробно), как тянешь reject-reason, какие старые тесты переписал и как, вывод релевантных тестов. Если что-то в коде разошлось с этим ТЗ (напр. _try_advance_stage сам НЕ ставит статус стадии, или нет способа узнать прошлый статус) — **отчитайся ДО мержа, не выдумывай**. Один исполнитель.