Files
wiki/tasks/orchestrator/DEV_TASK_STATUS_ONLY_VERDICT.md
2026-06-03 22:10:01 +03:00

12 KiB
Raw Blame History

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):

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 сам НЕ ставит статус стадии, или нет способа узнать прошлый статус) — отчитайся ДО мержа, не выдумывай. Один исполнитель.