From 432da2c4edce06e894a3d97a910cca75335645a6 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Wed, 17 Jun 2026 12:45:52 +0300 Subject: [PATCH] analyst(ET): auto-commit from analyst run_id=777 --- docs/work-items/ORCH-120/01-brd.md | 171 ++++++++++++++++++ docs/work-items/ORCH-120/02-trz.md | 111 ++++++++++++ .../ORCH-120/03-acceptance-criteria.md | 142 +++++++++++++++ docs/work-items/ORCH-120/04-test-plan.yaml | 88 +++++++++ 4 files changed, 512 insertions(+) create mode 100644 docs/work-items/ORCH-120/01-brd.md create mode 100644 docs/work-items/ORCH-120/02-trz.md create mode 100644 docs/work-items/ORCH-120/03-acceptance-criteria.md create mode 100644 docs/work-items/ORCH-120/04-test-plan.yaml diff --git a/docs/work-items/ORCH-120/01-brd.md b/docs/work-items/ORCH-120/01-brd.md new file mode 100644 index 0000000..10afaae --- /dev/null +++ b/docs/work-items/ORCH-120/01-brd.md @@ -0,0 +1,171 @@ +--- +work_item: ORCH-120 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +escalate: full-cycle +--- + +# 01 — BRD (бизнес-требования): ORCH-120 — Открытые вопросы аналитика должны переводить задачу в Needs Input + +Work Item: **ORCH-120** · Repo: **orchestrator** · Стадия: analysis + +> ⚠️ **Эскалация в полный цикл (`escalate: full-cycle`).** Это формально баг (метка `BUG:` в +> заголовке), но фикс требует архитектурных решений (правило приоритета веток в +> `_handle_analysis_approved_flow`, интеграция с осью «пауза» ORCH-124, семантика устаревания +> `01-questions.md`, стандартизация нового pipeline-артефакта) — нужен ADR. Поэтому выпущен +> **полный** analysis-пакет (01/02/03/04), а не облегчённый bug-shaped. Оператор снимает багфикс-трек +> командой `POST /bug-fast-track/escalate?work_item=ORCH-120`, после чего задача идёт через стадию +> `architecture` (ADR-001 D5 ORCH-019). Открытые проектные вопросы для архитектора — §6 (DQ-1…DQ-4). + +## 1. Бизнес-контекст и проблема + +При запуске конвейера аналитик (`analyst`) получает бизнес-запрос и **обязан** выпустить 4 файла +(`01-brd` / `02-trz` / `03-acceptance-criteria` / `04-test-plan.yaml`), иначе exit-гейт стадии +`analysis` не пройдёт. Если бизнес-запрос неоднозначен или неполон (классический пример — тело +запроса `Description: TBD`), у аналитика **нет рабочего канала** запросить уточнения у заказчика: он +вынужден **домысливать** требования и всё равно сдать 4 файла. Сфабрикованный пакет уходит в +`In Review` / `architecture` — то есть весь конвейер строит решение поверх выдуманных требований. + +**Парадокс:** механизм «вопросы → Needs Input» в движке **уже есть, но мёртв**. Код +`src/stage_engine.py::_handle_analysis_approved_flow` (стр. 769–786) читает файл +`docs/work-items//01-questions.md` и при его наличии вызывает `set_issue_needs_input(...)` + +комментарий в Plane + Telegram. Однако: + +1. **Контракт не доведён до аналитика.** Промпт `.openclaw/agents/analyst.md` **нигде** не упоминает + `01-questions.md`: ни в ``, ни в ``. Скелета `docs/_templates/01-questions.md` + нет, в манифесте `docs/_standards/PIPELINE_DOCS.md` артефакт не описан. Аналитик физически не + знает, что у него есть канал «задать блокирующий вопрос», поэтому домысливает. +2. **Ошибка приоритета веток.** В `_handle_analysis_approved_flow` ветка `files_ok` (все 4 файла на + месте — `check_analysis_complete`) проверяется **первой** и делает `return` (стр. 711–767). Ветка + `01-questions.md` (стр. 769) достижима, только если 4 файла НЕ полны. Значит, если аналитик сдал и + неполный/заглушечный пакет, и `01-questions.md` — движок уйдёт в `In Review`, проигнорировав + блокирующие вопросы. «Есть вопросы» должно иметь приоритет над «файлы на месте». +3. **Needs-Input блокирует serial-gate репо.** Задача в Needs Input остаётся в стадии `analysis` + (Plane-статус — слой B индикации, ORCH-066, **не** меняет `tasks.stage`) и при этом + `paused_at IS NULL`. По правилу serial-gate (ORCH-088) такая «активная» задача держит FIFO репо + закрытым: пока заказчик не ответит (часы/дни), ни одна следующая задача `orchestrator` не войдёт в + `analysis`. ORCH-124 ввёл ортогональную ось «пауза» (`tasks.paused_at` + `POST /serial-gate/pause| + resume`) ровно для случая «приостановлено, но не блокирует» — Needs-Input обязан её использовать. +4. **Нет гигиены устаревшего `01-questions.md`.** После ответа заказчика `handle_status_start` + перезапускает аналитика (`src/webhooks/plane.py:317–381`). Если перезапущенный аналитик теперь + выпускает полный валидный пакет, старый `01-questions.md` остаётся в ветке. Без правила + «устаревания» он либо игнорируется (если `files_ok` побеждает), либо вечно перезапускает Needs + Input (если вопросы получат приоритет). Нужно явное правило supersede. + +Корень — **разрыв контракта между промптом аналитика и движком** плюс **3 смежных дефекта потока** +(приоритет, блокировка очереди, устаревание). ORCH-120 закрывает их как единый «правильный поток +Needs Input». + +**Связь с предшественниками (контекст резюма из бэклога):** задача разморожена после корневых +фиксов **ORCH-124** (ось «пауза без блокировки» — необходимый фундамент для требования BR-3) и +**ORCH-126** (queued-job не застревает со stale `run_id`/`pid` — гарантирует, что перезапущенный +после ответа аналитик-job реально заберётся из очереди). Оба — предусловия, а не объём ORCH-120. + +## 2. Объём (scope) + +### В объёме +- **Контракт промпта аналитика:** `.openclaw/agents/analyst.md` явно документирует канал + «блокирующие открытые вопросы → пиши `01-questions.md`, НЕ фабрикуй 4 deliverables», с форматом и + правилом поведения на перезапуске (прочитать ответы, снять устаревшие вопросы). +- **Канон артефакта:** скелет `docs/_templates/01-questions.md` + строка в манифесте + `docs/_standards/PIPELINE_DOCS.md` (артефакт-сигнал Needs Input; **не** machine-verdict-док, гейтом + не парсится). +- **Приоритет веток в движке:** в `_handle_analysis_approved_flow` блокирующие открытые вопросы + получают корректный приоритет → задача с вопросами надёжно достигает Needs Input. +- **Неблокирование serial-gate:** переход в Needs Input не держит FIFO репо закрытым неопределённо + долго (интеграция с осью «пауза» ORCH-124). +- **Гигиена устаревания:** перезапущенный аналитик, выпустивший полный валидный пакет без свежих + вопросов, приводит к `In Review`, а не к повторному Needs Input. +- **Корректность resume-петли:** ответ заказчика → перезапуск аналитика → снятие паузы (unpark), job + забирается из очереди. +- **Обязательный регресс-тест** (красный до фикса, зелёный после) + анти-дрейф структурные тесты. + +### Вне объёма +- **Расширение владения Needs Input на других агентов.** ORCH-066 BR-10 фиксирует: Needs Input — + только у аналитика. Механизм не расширяется на architect/developer/reviewer/tester/deployer. +- **Новые QG-проверки и новые рёбра `STAGE_TRANSITIONS`.** Поток вопросов — pre-gate-ветка движка, + не Quality Gate. `check_analysis_complete`/`check_analysis_approved` — байт-в-байт. +- **Изменение семантики самого гейта `analysis`** (4 файла по-прежнему обязательны для прохождения + exit-гейта `analysis → architecture`). +- **Авто-ответ на вопросы / LLM-триаж ответов заказчика.** Ответы читает человек/аналитик, а не + отдельный автомат. +- **Машинерия багфикс-трека (ORCH-019)** и любые изменения вне аналитической стадии. + +## 3. Заинтересованные стороны +- **Заказчик / оператор (Слава)** — получает осмысленный запрос уточнений вместо выдуманных + требований; отвечает в Plane и возвращает задачу в работу. +- **Конвейер `orchestrator` (self-hosting)** — перестаёт строить решения поверх домыслов; serial-gate + репо не клинит на задаче, ждущей человека. +- **Аналитик-агент** — получает легитимный канал «не знаю — спрошу» вместо принуждения к фабрикации. +- **Другие проекты на общем инстансе (enduro-trails)** — не затронуты (нулевая регрессия при + отсутствии `01-questions.md` и вне self-hosting-области). + +## 4. Бизнес-требования (BR) +- **BR-1** — Аналитик, столкнувшийся с **блокирующей** неоднозначностью бизнес-запроса, ОБЯЗАН иметь + документированный канал запроса уточнений (`01-questions.md`) и НЕ должен фабриковать 4 deliverables + «лишь бы пройти гейт». Промпт `.openclaw/agents/analyst.md` описывает этот канал. +- **BR-2** — Наличие блокирующих открытых вопросов переводит задачу в Plane-статус **Needs Input** и + **останавливает** продвижение по конвейеру (не `In Review`, не `architecture`), даже если на диске + присутствуют частичные/заглушечные deliverables. Приоритет «вопросы» > «файлы на месте». +- **BR-3** — Задача в Needs Input **не блокирует** per-repo serial-gate FIFO неопределённо долго: + следующая задача `orchestrator` может войти в `analysis`, пока первая ждёт ответа человека. +- **BR-4** — После ответа заказчика (возврат issue в рабочий статус) аналитик перезапускается, читает + ответы и выпускает пакет. Если пакет полон и валиден и свежих блокирующих вопросов нет → задача + переходит в `In Review` (устаревший `01-questions.md` не должен повторно ронять её в Needs Input). +- **BR-5** — Поведение **обратимо и выборочно**: при отсутствии `01-questions.md` и выключенных + под-флагах поток Needs Input/паузы — байт-в-байт как до ORCH-120 (нулевая регрессия для enduro и + для штатной задачи без вопросов). +- **BR-6** — `01-questions.md` стандартизирован как pipeline-артефакт (скелет в `docs/_templates/` + + строка манифеста `PIPELINE_DOCS.md`): он сигнальный, **не** machine-verdict (гейтом не парсится). + +## 5. Нефункциональные требования (NFR) +- **NFR-1 (never-raise / fail-safe)** — Любая ошибка новой логики (чтение файла, park-вызов, + определение приоритета) НЕ роняет `advance_stage`/launcher и деградирует к безопасному прежнему + поведению (как существующие leaf'ы `serial_gate`/`labels`/`cancel`). +- **NFR-2 (обратная совместимость)** — Стадии, кроме `analysis`, и Needs-Input-владение (ORCH-066) — + не трогаются. `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict-ключи / семантика + exit-гейта `analysis` — байт-в-байт. +- **NFR-3 (инварианты serial-gate)** — Интеграция с паузой не регрессирует ORCH-088 (анти-stale-base: + отложенный срез ветки) и ORCH-124 (терминал `{done,cancelled}` и оси `task_deps`/`freeze` — + не читают `paused_at`; пауза их не обходит). +- **NFR-4 (self-hosting-безопасность)** — Поток только меняет Plane-статус/паузу/комментарий и читает + worktree: не деплоит, не рестартит прод-контейнер, не пушит в `main`, не трогает detached-процессы. +- **NFR-5 (наблюдаемость)** — Переход в Needs Input и park/unpark логируются структурно; состояние + паузы видно в блоке `serial_gate` `GET /queue` (ORCH-124 уже отдаёт `paused`). + +## 6. Допущения и ограничения +- **Допущение:** механизм чтения `01-questions.md` и `set_issue_needs_input` рабочие — задача в + основном **активирует и достраивает** существующий путь, а не строит его с нуля. +- **Допущение:** промпт `cat`-ается из worktree в момент запуска (ORCH-077 loading-model) → новый + контракт аналитика вступает в силу на следующем worktree от `main` без прод-рестарта. +- **Ограничение:** Plane-статус **Needs Input** должен существовать на доске проекта (ключ + `needs_input` уже в `plane_sync._DEFAULT_STATES`) — инфра-предусловие выполнено для ORCH. +- **Открытые проектные вопросы для архитектора (решить в `06-adr/`, НЕ в analysis):** + - **DQ-1** — Парковать задачу при Needs Input **автоматически** (`db.set_task_paused` в момент + перехода) или оставить park **операторским** (`POST /serial-gate/pause`)? Trade-off: + авто-park снимает риск стопора очереди (BR-3), но связывает индикацию (слой B) с осью планировщика. + - **DQ-2** — Механизм устаревания `01-questions.md` (BR-4): удалять файл при выпуске полного пакета / + приоритет по «вопросы свежее deliverables» (mtime/commit) / явный маркер «answered». Любой выбор + обязан быть детерминированным и не зависеть от сетевого Plane. + - **DQ-3** — Точное правило приоритета в `_handle_analysis_approved_flow`: проверять + `01-questions.md` ДО `files_ok`, либо ввести предикат «вопросы активны» с учётом DQ-2. + - **DQ-4** — Коллизия номера `01-questions.md` с `01-brd.md`. Движок читает именно `01-questions.md` + (`stage_engine.py:771`) — менять путь = код-изменение; стандарт документирует фактический путь. +- **Ограничение по флагам:** новое поведение (приоритет вопросов / авто-park) — под kill-switch с + безопасным дефолтом, чтобы откат был байт-в-байт (BR-5). + +## 7. Критерии успеха +Аналитик при блокирующей неоднозначности пишет `01-questions.md`, задача надёжно переходит в Needs +Input, **не** блокирует serial-gate репо, после ответа заказчика возобновляется и выпускает корректный +пакет; при отсутствии вопросов — поведение прежнее. Детальные PASS/FAIL — `03-acceptance-criteria.md`. + +## 8. Риски +- Связывание индикации (Plane-статус) с осью планировщика (пауза) при авто-park (DQ-1) — риск + непреднамеренного park; смягчение — kill-switch + явный лог. +- Устаревший `01-questions.md` зацикливает Needs Input (DQ-2) — смягчение детерминированным + supersede-правилом + регресс-тест BR-4. +- Регресс serial-gate (ORCH-088/124) при неаккуратной интеграции паузы — смягчение тестами NFR-3. +Детали и оценка — `10-tech-risks.md` (заполняет архитектор). diff --git a/docs/work-items/ORCH-120/02-trz.md b/docs/work-items/ORCH-120/02-trz.md new file mode 100644 index 0000000..5a52bb8 --- /dev/null +++ b/docs/work-items/ORCH-120/02-trz.md @@ -0,0 +1,111 @@ +--- +work_item: ORCH-120 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +--- + +# 02 — ТЗ (TRZ): ORCH-120 — Открытые вопросы аналитика → Needs Input + +Work Item: **ORCH-120** · Repo: **orchestrator** · Стадия: analysis + +> ТЗ описывает **конкретные изменения к реализации**, выведенные из BRD и фактического кода. +> Архитектурное обоснование (выбор механизма приоритета, авто-park vs operator-park, способ +> устаревания `01-questions.md`) — задача архитектора (`06-adr/`). Открытые проектные вопросы — +> BRD §6 (DQ-1…DQ-4). + +## 1. Сводка изменения + +Активировать и достроить уже существующий, но мёртвый путь «аналитик задаёт блокирующие вопросы → +задача в Needs Input». Четыре связанных изменения: (1) **контракт промпта** аналитика + +**канон артефакта** `01-questions.md`; (2) **приоритет** ветки вопросов над веткой «файлы готовы» в +`_handle_analysis_approved_flow`; (3) **неблокирование serial-gate** через ось «пауза» ORCH-124; +(4) **гигиена устаревания** `01-questions.md` на resume-петле. `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, +семантика и имена `check_*`, machine-verdict-ключи, схема существующих таблиц — **не меняются**. + +## 2. Задействованные модули / пути + +| Путь | Действие | +|------|----------| +| `.openclaw/agents/analyst.md` | **изменить** — добавить контракт «блокирующие вопросы → `01-questions.md`, не фабриковать deliverables» (в `` + `` + поведение на resume); сохранить канон 52d (5 секций, 6 полей frontmatter). | +| `docs/_templates/01-questions.md` | **создать** — скелет артефакта открытых вопросов (формат: контекст / список блокирующих вопросов с вариантами / что разблокирует анализ). | +| `docs/_standards/PIPELINE_DOCS.md` | **изменить** — строка манифеста §2 для `01-questions.md` (владелец `analyst`, категория `when-applicable`, стадия `analysis`, «механизм: ветка Needs Input в `_handle_analysis_approved_flow`», machine-key — «нет, сигнальный»). | +| `src/stage_engine.py` | **изменить** — `_handle_analysis_approved_flow`: правило приоритета (вопросы активны → Needs Input до/вместо `files_ok`, см. DQ-3); опц. вызов park (DQ-1); гигиена устаревания (DQ-2). Всё never-raise. | +| `src/webhooks/plane.py` | **изменить (точечно)** — `handle_status_start` (analysis-resume ветка, стр. 317–381): при перезапуске аналитика снять паузу (`clear_task_paused`/`POST` эквивалент), чтобы re-enqueued job был claimable. | +| `src/db.py` | **переиспользовать** — `set_task_paused` / `clear_task_paused` / `is_task_paused` (ORCH-124, уже есть; новых колонок НЕ вводить). | +| `src/serial_gate.py` | **не менять кодом** — ось «пауза» уже исключает `paused_at NOT NULL` (ORCH-124); ORCH-120 лишь корректно её триггерит. | +| `src/config.py` | **изменить** — добавить kill-switch(и) нового поведения (напр. `analyst_questions_gate_enabled`, опц. `analyst_needs_input_autopause_enabled`), env `ORCH_*`, безопасные дефолты. | +| `src/main.py` | **возможно** — наблюдаемость в блоке `GET /queue` (если потребуется доп. поле); pause/resume эндпоинты ORCH-124 переиспользуются как есть. | + +> Точный набор правок в `src/**` финализирует архитектор (DQ-1…DQ-3). TRZ фиксирует **наблюдаемый +> контракт**, а не конкретную реализацию ветвления. + +## 3. Функциональные требования + +### FR-1 — Контракт промпта аналитика (BR-1, BR-6) +`.openclaw/agents/analyst.md` явно описывает: при **блокирующей** неоднозначности бизнес-запроса +аналитик пишет `docs/work-items//01-questions.md` (через Write tool) со списком конкретных +блокирующих вопросов и **не** выпускает сфабрикованные 4 deliverables. Указывается поведение на +перезапуске: прочитать свежие комментарии-ответы в Plane, снять/не переписывать устаревшие вопросы, +выпустить полный пакет. Промпт остаётся в каноне 52d (5 секций, 6 полей schema, без `model:`). + +### FR-2 — Приоритет «вопросы активны» (BR-2) +В `_handle_analysis_approved_flow` наличие **активных** блокирующих вопросов (`01-questions.md`, +с учётом supersede-правила DQ-2) ведёт к `set_issue_needs_input(...)` + комментарий + Telegram + +`result.note = "analysis-needs-input"` **независимо** от того, присутствуют ли на диске 4 файла. +Сейчас ветка `files_ok` (стр. 711) делает `return` до проверки вопросов (стр. 769) — порядок/предикат +исправляется так, что вопросы имеют приоритет. Happy-path (нет вопросов, 4 файла) → `In Review` +(`analysis-in-review`) без изменений. + +### FR-3 — Неблокирование serial-gate (BR-3, NFR-3) +Переход в Needs Input приводит к тому, что задача **исключается** из «активного» предиката serial-gate +(ORCH-088), чтобы следующая задача `orchestrator` могла войти в `analysis`. Механизм — ось «пауза» +ORCH-124: `paused_at NOT NULL` уже исключается в `build_claim_clause`/`repo_has_active_task`/ +`_per_repo_snapshot`. Авто-park vs operator-park — DQ-1. Терминал `{done,cancelled}` и оси +`task_deps`/`freeze` не читают `paused_at` — пауза их не обходит (инвариант ORCH-124 цел). + +### FR-4 — Resume + unpark (BR-4) +`handle_status_start` (analysis-ветка) при перезапуске аналитика после ответа заказчика снимает паузу +(`clear_task_paused`), чтобы re-enqueued analyst-job был claimable (совместно с фиксом ORCH-126 о +stale `run_id`/`pid`). Существующий relaunch-guard ORCH-090 (relaunch только для `analysis`) — не +ослабляется. + +### FR-5 — Гигиена устаревания `01-questions.md` (BR-4) +Перезапущенный аналитик, выпустивший полный валидный пакет (4 файла) **без свежих** блокирующих +вопросов, приводит к `In Review`, а не к повторному Needs Input. Реализация supersede — DQ-2 +(детерминированно, offline, без сетевого Plane). + +### FR-6 — Обратимость / kill-switch (BR-5) +Новое поведение под kill-switch с безопасным дефолтом: при отсутствии `01-questions.md` и выключенном +под-флаге поток Needs Input/паузы — **байт-в-байт** как до ORCH-120. Скоуп — self-hosting +(`orchestrator`); enduro не затронут. + +## 4. Изменения API +**Нет новых эндпоинтов.** Переиспользуются существующие `POST /serial-gate/pause` и +`POST /serial-gate/resume` (ORCH-124, `src/main.py:396/442`) как операторский путь park/unpark (если +архитектор выберет operator-park, DQ-1). При авто-park вызывается `db.set_task_paused` напрямую из +движка. Блок `serial_gate` в `GET /queue` уже отдаёт `paused` — возможно дополнение полем-причиной. + +## 5. Изменения схемы БД +**Нет.** Колонка `tasks.paused_at` уже введена ORCH-124 (`src/db.py:160`, `_ensure_column`). Новых +таблиц/колонок/индексов ORCH-120 не вводит. + +## 6. Требования к новым/изменённым QG checks +**Нет.** `01-questions.md` — сигнальный артефакт, **не** machine-verdict-док; гейтом не парсится. +`check_analysis_complete` / `check_analysis_approved` / `_parse_*` — байт-в-байт. Поток вопросов +остаётся pre-gate-веткой движка (`_handle_analysis_approved_flow`), как и был. + +## 7. Совместимость / регресс +- **Обратная совместимость:** при отсутствии `01-questions.md` ветвление `_handle_analysis_approved_flow` + и serial-gate работают как прежде (NFR-2). Стадии ≠ `analysis` — не трогаются. +- **Kill-switch:** новое поведение (приоритет вопросов / авто-park) выключаемо → откат байт-в-байт + (FR-6/BR-5). Область — self-hosting `orchestrator`. +- **Инварианты:** ORCH-066 (Needs Input только у аналитика) — не расширяется; ORCH-088/124 (анти-stale-base, + терминал/freeze/deps оси) — не регрессируют (NFR-3); never-raise (NFR-1); self-hosting-безопасность + (NFR-4: без прод-рестарта/`main`-push). +- **Полный регресс** `pytest tests/` остаётся зелёным; обязательный новый регресс-тест (TC-01) красный + до фикса и зелёный после. +- **Трассировка маркеров (ORCH-078):** правки в `_handle_analysis_approved_flow`/`serial_gate`/ + `plane.py` сверяются с ADR ORCH-066 / ORCH-088 / ORCH-124 перед изменением (не сломать инварианты). diff --git a/docs/work-items/ORCH-120/03-acceptance-criteria.md b/docs/work-items/ORCH-120/03-acceptance-criteria.md new file mode 100644 index 0000000..86e52bd --- /dev/null +++ b/docs/work-items/ORCH-120/03-acceptance-criteria.md @@ -0,0 +1,142 @@ +--- +work_item: ORCH-120 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +--- + +# 03 — Критерии приёмки (Acceptance Criteria): ORCH-120 — Открытые вопросы аналитика → Needs Input + +Work Item: **ORCH-120** · Repo: **orchestrator** · Стадия: analysis + +Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** (что +считается провалом). Reviewer проверяет их буквально по файлам репозитория и тестам. + +--- + +## AC-1 — Приоритет вопросов над «файлы готовы» (регресс, обязательный) + +**Условие:** `_handle_analysis_approved_flow` вызывается после аналитика, в worktree присутствуют +ОДНОВРЕМЕННО все 4 файла (`01-brd`/`02-trz`/`03-acceptance-criteria`/`04-test-plan.yaml`) И +`01-questions.md` с активными блокирующими вопросами. +- **PASS:** вызывается `set_issue_needs_input(work_item_id)`, `result.note == "analysis-needs-input"`, + `set_issue_in_review` НЕ вызывается, задача НЕ продвигается на `architecture`. +- **FAIL:** задача уходит в `In Review` / `architecture` (текущее ошибочное поведение — ветка + `files_ok` побеждает). + +--- + +## AC-2 — Только вопросы, без deliverables (сохранение существующего поведения) + +**Условие:** `01-questions.md` присутствует, 4 файла отсутствуют. +- **PASS:** `set_issue_needs_input` вызван, `result.note == "analysis-needs-input"`, текст вопросов + передан в Plane-комментарий и Telegram. +- **FAIL:** задача не переходит в Needs Input или падает с исключением. + +--- + +## AC-3 — Happy-path без регресса + +**Условие:** `01-questions.md` отсутствует, все 4 файла на месте. +- **PASS:** `set_issue_in_review` вызван, `result.note == "analysis-in-review"`, запрошен статус + `Approved` (поведение байт-в-байт как до ORCH-120, включая autoApprove-ветку ORCH-089). +- **FAIL:** задача ошибочно уходит в Needs Input либо happy-path-комментарий/статус изменился. + +--- + +## AC-4 — Needs Input не блокирует serial-gate репо + +**Условие:** задача A `orchestrator` в `analysis` переведена в Needs Input по `01-questions.md`; +существует более поздняя задача B того же репо. +- **PASS:** A исключена из «активного» предиката serial-gate (через `paused_at NOT NULL`, ось + ORCH-124); B может войти в `analysis` (claim analyst-job B не блокируется задачей A). +- **FAIL:** A продолжает держать FIFO репо закрытым (`repo_has_active_task` возвращает True из-за A), + B не может стартовать, пока заказчик не ответит. + +--- + +## AC-5 — Resume снимает паузу и перезапускает аналитика + +**Условие:** заказчик ответил и вернул issue в рабочий статус (In Progress / To Analyse) на стадии +`analysis`; активного job нет. +- **PASS:** `handle_status_start` снимает паузу (`paused_at` → NULL) и enqueue'ит analyst-job; job + забирается из очереди (не застревает со stale `run_id`/`pid`, ср. ORCH-126); relaunch-guard ORCH-090 + (только `analysis`) соблюдён. +- **FAIL:** задача остаётся paused (job не claimable) ИЛИ перезапуск происходит на не-`analysis` стадии + (нарушение ORCH-090). + +--- + +## AC-6 — Гигиена устаревшего `01-questions.md` + +**Условие:** перезапущенный аналитик выпустил полный валидный пакет (4 файла) без свежих блокирующих +вопросов; устаревший `01-questions.md` от прошлого прогона мог остаться. +- **PASS:** задача переходит в `In Review` (`analysis-in-review`), а НЕ повторно в Needs Input; + supersede-правило (DQ-2) применено детерминированно и offline. +- **FAIL:** устаревший `01-questions.md` повторно роняет задачу в Needs Input (бесконечная петля). + +--- + +## AC-7 — Контракт промпта аналитика (анти-дрейф) + +**Условие:** содержимое `.openclaw/agents/analyst.md`. +- **PASS:** промпт документирует канал `01-questions.md` (блокирующие вопросы → Needs Input, не + фабриковать deliverables) И сохраняет канон 52d (5 XML-секций, 6 полей frontmatter-схемы, без + `model:`); `tests/test_agent_prompts_canon.py` зелёный, добавлен assert наличия контракта вопросов. +- **FAIL:** канал не документирован, либо нарушен канон 52d, либо тест канона красный. + +--- + +## AC-8 — Канон артефакта `01-questions.md` + +**Условие:** наличие скелета и записи в манифесте. +- **PASS:** `docs/_templates/01-questions.md` существует; `docs/_standards/PIPELINE_DOCS.md` содержит + строку манифеста для `01-questions.md` (владелец `analyst`, категория `when-applicable`, механизм + «ветка Needs Input», не machine-verdict). +- **FAIL:** скелет или строка манифеста отсутствуют. + +--- + +## AC-9 — Обратимость / нулевая регрессия + +**Условие:** kill-switch нового поведения выключен ИЛИ репо вне self-hosting-области (enduro-trails). +- **PASS:** ветвление `_handle_analysis_approved_flow` и serial-gate работают **байт-в-байт** как до + ORCH-120; `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict/схема БД не изменены. +- **FAIL:** обнаружено отличие поведения при выключенном флаге / для enduro. + +--- + +## AC-10 — never-raise / self-hosting-безопасность + +**Условие:** сбой новой логики (ошибка чтения файла, park-вызова, определения приоритета). +- **PASS:** `advance_stage`/launcher не падает, деградирует к безопасному прежнему поведению + WARNING; + поток не деплоит, не рестартит прод-контейнер, не пушит в `main`. +- **FAIL:** исключение всплывает наружу / встаёт конвейер / затронут прод/`main`. + +--- + +## AC-11 — Полный регресс зелёный + +**Условие:** `pytest tests/ -q`. +- **PASS:** вся сюита зелёная; новый обязательный регресс-тест (AC-1 / TC-01) красный на коде до фикса + и зелёный после. +- **FAIL:** любой тест красный, либо регресс-тест проходит и на дофиксовом коде (не доказывает баг). + +--- + +## Сводная матрица AC ↔ FR/BR +| AC | Покрывает | +|----|-----------| +| AC-1 | BR-2 / FR-2 (регресс) | +| AC-2 | BR-2 / FR-2 | +| AC-3 | BR-5 / FR-2 | +| AC-4 | BR-3 / FR-3 | +| AC-5 | BR-4 / FR-4 | +| AC-6 | BR-4 / FR-5 | +| AC-7 | BR-1 / BR-6 / FR-1 | +| AC-8 | BR-6 / FR-1 | +| AC-9 | BR-5 / FR-6 / NFR-2 | +| AC-10 | NFR-1 / NFR-4 | +| AC-11 | NFR-2 | diff --git a/docs/work-items/ORCH-120/04-test-plan.yaml b/docs/work-items/ORCH-120/04-test-plan.yaml new file mode 100644 index 0000000..9bb7019 --- /dev/null +++ b/docs/work-items/ORCH-120/04-test-plan.yaml @@ -0,0 +1,88 @@ +work_item: ORCH-120 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +title: "Аналитик: открытые вопросы → Needs Input (приоритет, неблокирование serial-gate, resume)" +framework: pytest +scope: > + Покрывает поток «блокирующие открытые вопросы аналитика → Needs Input»: + приоритет ветки вопросов над «файлы готовы» (_handle_analysis_approved_flow), + неблокирование per-repo serial-gate (ось паузы ORCH-124), resume+unpark, + гигиена устаревшего 01-questions.md, контракт промпта и канон артефакта, + never-raise и нулевая регрессия. Вне покрытия: расширение Needs Input на + других агентов, новые QG/рёбра STAGE_TRANSITIONS, авто-ответ на вопросы. +notes: > + TC-01 — ОБЯЗАТЕЛЬНЫЙ регресс-тест: красный на коде ДО фикса (ветка files_ok + побеждает → In Review), зелёный ПОСЛЕ. Тесты движка прогоняют + _handle_analysis_approved_flow напрямую (launcher-путь), мокая plane_sync- + сеттеры и используя временный worktree (паттерн tests/test_analyst_status_only_regression.py + и tests/test_auto_approve_brd.py). Полный регресс pytest tests/ остаётся зелёным. + +tests: + - id: TC-01 + type: unit + description: "РЕГРЕСС: 4 файла + активный 01-questions.md одновременно -> set_issue_needs_input вызван, note=='analysis-needs-input', set_issue_in_review НЕ вызван (приоритет вопросов, AC-1). Красный до фикса." + module: tests/test_orch120_analyst_needs_input.py + expected: PASS + + - id: TC-02 + type: unit + description: "01-questions.md есть, 4 файлов нет -> Needs Input, текст вопросов в Plane-комментарии и Telegram (AC-2, сохранение существующего поведения)." + module: tests/test_orch120_analyst_needs_input.py + expected: PASS + + - id: TC-03 + type: unit + description: "Happy-path: нет 01-questions.md, 4 файла на месте -> set_issue_in_review, note=='analysis-in-review', запрос статуса Approved (AC-3, нет регресса)." + module: tests/test_orch120_analyst_needs_input.py + expected: PASS + + - id: TC-04 + type: integration + description: "Задача A в analysis переведена в Needs Input (paused_at NOT NULL) -> serial_gate исключает её из активного предиката; задача B того же репо может войти в analysis (AC-4, неблокирование FIFO)." + module: tests/test_orch120_serial_gate_needs_input.py + expected: PASS + + - id: TC-05 + type: integration + description: "Resume: возврат issue в рабочий статус на analysis при отсутствии активного job -> handle_status_start снимает паузу (paused_at->NULL) и enqueue'ит analyst-job; relaunch-guard ORCH-090 (только analysis) соблюдён (AC-5)." + module: tests/test_orch120_resume_unpark.py + expected: PASS + + - id: TC-06 + type: unit + description: "Гигиена устаревания: перезапущенный аналитик выпустил полный валидный пакет без свежих вопросов -> In Review, НЕ повторный Needs Input; supersede-правило детерминировано и offline (AC-6)." + module: tests/test_orch120_analyst_needs_input.py + expected: PASS + + - id: TC-07 + type: unit + description: "Анти-дрейф промпта: .openclaw/agents/analyst.md документирует канал 01-questions.md (блокирующие вопросы -> Needs Input, не фабриковать deliverables) и сохраняет канон 52d (5 секций, 6 полей, без model:) (AC-7)." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-08 + type: unit + description: "Канон артефакта: docs/_templates/01-questions.md существует и docs/_standards/PIPELINE_DOCS.md содержит строку манифеста для 01-questions.md (владелец analyst, when-applicable, не machine-verdict) (AC-8)." + module: tests/test_orch120_questions_artifact_canon.py + expected: PASS + + - id: TC-09 + type: unit + description: "never-raise: сбой новой логики (ошибка чтения 01-questions.md / park-вызова) не роняет advance_stage, деградирует к безопасному прежнему поведению + WARNING (AC-10)." + module: tests/test_orch120_analyst_needs_input.py + expected: PASS + + - id: TC-10 + type: unit + description: "Обратимость: kill-switch выключен ИЛИ репо вне self-hosting (enduro-trails) -> ветвление _handle_analysis_approved_flow и serial-gate байт-в-байт как до ORCH-120 (AC-9)." + module: tests/test_orch120_analyst_needs_input.py + expected: PASS + + - id: TC-11 + type: integration + description: "Полный регресс pytest tests/ зелёный; STAGE_TRANSITIONS/QG_CHECKS/check_* снапшот не изменён (AC-11, NFR-2)." + module: tests/test_stage_transitions_snapshot.py + expected: PASS