Compare commits

...

2 Commits

Author SHA1 Message Date
fe0dbea97e analyst(ET): auto-commit from analyst run_id=652
All checks were successful
CI / test (push) Successful in 58s
2026-06-12 08:30:45 +03:00
1d8ea1459d docs: init ORCH-106 business request
All checks were successful
CI / test (push) Successful in 1m2s
2026-06-12 08:25:54 +03:00
5 changed files with 397 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
# Business Request: Fix onboard_project.py: add color field for state creation
Work Item ID: ORCH-106
## Description
TBD

View File

@@ -0,0 +1,150 @@
---
work_item: ORCH-106
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-12
model_used: claude-opus-4-8
track: bug
escalate: none
---
# 01 — Bug-report (облегчённый BRD): ORCH-106 — Fix onboard_project.py: add color field for state creation
Work Item: **ORCH-106** · Repo: **orchestrator** · Стадия: analysis · Трек: **🐞 Bug** (укороченный маршрут, ORCH-019)
> Багфикс-трек: стадия `architecture` пропускается. Это **облегчённый** пакет (короткий
> bug-report + обязательный план регресс-теста), но все 4 файла analysis присутствуют
> (гейт `check_analysis_complete` не меняется). Эскалация в полный цикл — **не требуется**
> (см. §8).
---
## 1. Симптом
CLI онбординга `scripts/onboard_project.py` (режим `apply`) **не может создать статусы Plane**
для нового проекта. На каждом статусе, отсутствующем в проекте (в том числе **STOP**), шаг
`plane.state:<name>` падает с исходом `ERROR`: запрос `POST .../states/` к Plane CE возвращает
**HTTP 400**.
Практический эффект: turnkey-онбординг (ORCH-009) и Bundled-bootstrap (ORCH-103,
`onboard_project.py apply+verify`) **не доводят доску до 22 статусов** автоматически — оператор
вынужден создавать статусы вручную (теряется ключевое обещание «один проход»). Без статусов
группы `cancelled` (STOP, ORCH-090) и `Confirm Deploy` (ORCH-059) затронутый проект остаётся без
fail-closed-кнопок отмены/подтверждения деплоя.
## 2. Шаги воспроизведения
1. Иметь доступный Plane CE с валидным API-токеном (env `ORCH_PLANE_*`).
2. Запустить онбординг нового (либо неполностью сконфигурированного) проекта:
`python scripts/onboard_project.py apply --project <NAME> ...` (любой путь, доходящий до
секции «2. Plane: статусы», строки ~760-779 `scripts/onboard_project.py`).
3. **Факт:** в отчёте каждый отсутствующий статус — `plane.state:STOP … ERROR` (и аналогично для
прочих), причина — `Client error '400 Bad Request'` от `POST …/projects/<id>/states/`.
4. **Ожидание:** статус создаётся, шаг — `CREATED`; `POST …/states/` возвращает `200/201`.
> Детерминированный (без сети) повтор для CI — на уровне формируемого тела запроса: тело,
> которое `create_state` передаёт в `_post`, **не содержит** ключа `color` (см. §3). Это и есть
> корневой наблюдаемый дефект, проверяемый регресс-тестом (`04-test-plan.yaml`).
## 3. Локализация
| Что | Где |
|-----|-----|
| Дефектный метод | `scripts/onboard_project.py``PlaneClient.create_state` (строки **424-428**) |
| Дефектная строка | тело POST: `{"name": name, "group": group}`**отсутствует поле `color`** |
| Точка отправки | `PlaneClient._post` (строки 382-389): `200/201` → ок; `401/403/404/405/501``ManualStep`; **прочее (включая 400) → `resp.raise_for_status()` → исключение** |
| Точка проявления | секция «2. Plane: статусы», цикл по `_PLANE_NAME_TO_KEY` (строки 767-779): исключение ловится `except Exception → report.add(..., ERROR, str(e))` |
| Таблица групп (контекст) | `STATE_GROUPS` (строки 81-104) — имена→группы, в т.ч. `STOP→cancelled` |
## 4. Причина (root cause)
Эндпоинт создания статуса в Plane CE (`POST /workspaces/<ws>/projects/<id>/states/`) **требует
непустое поле `color`** (валидный hex-цвет) в теле запроса. `create_state` его не отправляет →
серверная валидация отклоняет запрос с **HTTP 400**. Поскольку `_post` не относит `400` к
«ручным» статусам (`401/403/404/405/501`), срабатывает `raise_for_status()`, исключение
поднимается в цикл онбординга и фиксируется как `ERROR`.
Сопутствующий факт: соседний `create_label` (строки 430-434) и `create_project` (418-422)
работают, т.к. их CE-эндпоинты не требуют `color`; проблема **локальна** для создания статусов.
## 5. Направление исправления (требование, не реализация)
Тело запроса, формируемое `create_state`, должно содержать **валидное непустое поле `color`**
(hex-строка, принимаемая Plane CE), чтобы `POST …/states/` возвращал `200/201` и статус
создавался. Конкретный способ (единый дефолт-цвет на все статусы либо палитра по группам/именам)
и значение цвета — **решает developer** (для багфикс-трека `architecture` пропущена; это
точечная правда тела запроса, не архитектурное решение).
> ⚠️ Зона аналитика — требование и его проверяемость, не выбор реализации. Минимально достаточно:
> любой валидный hex-цвет, единый или пер-статусный.
## 6. Объём (scope)
### В объёме
- Добавить валидное поле `color` в тело POST, формируемое `PlaneClient.create_state`
(`scripts/onboard_project.py`).
- Обязательный регресс-тест: тело `create_state` несёт непустой валидный `color` (red до фикса,
green после) — см. `04-test-plan.yaml`.
### Вне объёма
- Изменение публичной сигнатуры `create_state(project_id, name, group)` (color добавляется
**внутри** тела, не как новый обязательный аргумент) — чтобы не ломать call-site (строка 773) и
существующие тесты/моки.
- Любые правки рантайма `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, схемы БД, compose,
Dockerfile.
- Цвета/палитра статусов как «дизайн» — не задача; нужен лишь валидный для CE цвет.
- Поведение `create_label`/`create_project` — не трогается.
## 7. Заинтересованные стороны / влияние
- **Заказчик:** оператор платформы, разворачивающий новый проект (ORCH-009 turnkey, ORCH-103
Bundled). Боль — ручное досоздание 22 статусов, обещание «один проход» нарушено.
- **Затронуто:** `scripts/onboard_project.py` (CLI вне рантайма), runbook'и онбординга/тиража
(ссылаются на `apply+verify`). Действующий прод-конвейер и проект enduro-trails **не
затронуты** (скрипт — операторский, активируется только явным запуском).
- **Принимает результат:** reviewer/tester по критериям `03` и плану `04`.
## 8. Решение по треку (bug-track, ORCH-019)
Баг **простой и точечный**: правка тела запроса одного метода (`create_state`), без ADR, без
макетов, без затрагивания рантайма/гейтов. → Остаётся на **облегчённом багфикс-треке**, стадия
`architecture` пропускается. **`escalate: none`** — эскалация в полный цикл (`POST
/bug-fast-track/escalate`) **не требуется**.
## 9. Бизнес-требования (BR)
- **BR-1** — `onboard_project.py apply` создаёт статусы Plane без ошибки 400: тело
`POST …/states/` содержит валидное непустое поле `color`.
- **BR-2** — Сценарий из описания задачи воспроизводится зелёным: создание статуса с именем
**STOP** и цветом возвращает `200/201` (на живом Plane CE) / тело несёт `color`
(детерминированно в CI).
- **BR-3** — Все статусы онбординга (весь `_PLANE_NAME_TO_KEY`, включая STOP) создаются единообразно
с непустым `color`.
## 10. Нефункциональные требования (NFR)
- **NFR-1 (совместимость)** — публичная сигнатура `create_state(project_id, name, group)` и
call-site (строка 773) не меняются; существующие онбординг-тесты
(`tests/test_onboarding_script.py` и пр.) остаются зелёными.
- **NFR-2 (изоляция рантайма)** — правка только в операторском CLI `scripts/onboard_project.py`
(+ тест); `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД/compose — байт-в-байт. Self-hosting
безопасно: скрипт не деплоит/не рестартит прод/не трогает `main`.
- **NFR-3 (валидность цвета)** — отправляемый `color` — валидная hex-строка, принимаемая Plane CE
(не пустая, не `null`).
- **NFR-4 (полный регресс зелёный)** — `pytest tests/ -q` остаётся зелёным.
## 11. Допущения и ограничения
- Plane CE требует именно `color` (hex); поле обязательно и непустое — установленный факт из
описания задачи и наблюдаемого 400.
- Живая проверка `200` против реального Plane CE — ручной шаг приёмки (CI работает на уровне тела
запроса, без сети — детерминированность).
- Точное значение/палитра цвета — на усмотрение developer; аналитик фиксирует лишь «валидный
непустой hex».
## 12. Критерии успеха
Резюме: после фикса онбординг создаёт все статусы (включая STOP) без 400; регресс-тест на наличие
валидного `color` в теле `create_state` зелёный; существующий набор тестов не сломан. Детальные
PASS/FAIL — `03-acceptance-criteria.md`.

View File

@@ -0,0 +1,90 @@
---
work_item: ORCH-106
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-12
model_used: claude-opus-4-8
track: bug
---
# 02 — ТЗ (TRZ, bug-shaped): ORCH-106 — Fix onboard_project.py: add color field for state creation
Work Item: **ORCH-106** · Repo: **orchestrator** · Стадия: analysis · Трек: **🐞 Bug**
> Облегчённое ТЗ багфикс-трека: фиксирует **конкретное изменение тела запроса**, выведенное из
> bug-report (`01-brd.md`) и фактического кода. Архитектурного обоснования нет (стадия
> `architecture` пропущена для bug-трека) — изменение точечное.
## 1. Сводка изменения
В `PlaneClient.create_state` (`scripts/onboard_project.py`, строки 424-428) тело запроса
`POST …/projects/<id>/states/` дополняется обязательным для Plane CE полем **`color`** (валидная
непустая hex-строка). Сейчас тело — `{"name": name, "group": group}`; после фикса —
`{"name": name, "group": group, "color": <valid-hex>}`. Поведение `_post`, цикл онбординга,
сигнатуры и прочие методы не меняются. Добавляется обязательный регресс-тест.
## 2. Задействованные модули / пути
| Путь | Действие |
|------|----------|
| `scripts/onboard_project.py` | изменить — `PlaneClient.create_state`: добавить `color` в тело POST |
| `tests/test_onboarding_script.py` *(или новый `tests/test_onboard_state_color.py`)* | создать/дополнить — регресс-тест на наличие валидного `color` в теле `create_state` |
> Выбор файла теста — на усмотрение developer; ключевое требование — тест экзерсайзит **реальный**
> `PlaneClient.create_state` (через перехват `_post`/`httpx.post`), а не `FakePlane` из существующего
> набора (мок не строит реальное тело и баг не ловит).
## 3. Функциональные требования
### FR-1 — `color` в теле создания статуса (BR-1, BR-3)
`create_state` ДОЛЖЕН включать в тело POST ключ `color` с валидной непустой hex-строкой для
**каждого** создаваемого статуса. Инвариант: ни один путь `create_state` не отправляет тело без
`color` (либо с пустым/`null` `color`).
### FR-2 — Сценарий STOP (BR-2)
Создание статуса с именем **STOP** (группа `cancelled`, `STATE_GROUPS["STOP"]`) формирует тело с
непустым валидным `color`; на живом Plane CE такой `POST …/states/` возвращает `200/201`.
### FR-3 — Сохранение контракта метода (NFR-1)
Публичная сигнатура `create_state(project_id, name, group)` НЕ меняется (color добавляется внутри
тела). Call-site `plane.create_state(project_id, name, group)` (строка 773) и существующие
тест-моки (`FakePlane.create_state(self, project_id, name, group)`) остаются валидны без правок.
## 4. Изменения API
**Исходящий вызов к Plane CE** — тело `POST /workspaces/<ws>/projects/<id>/states/` получает
дополнительное поле `color`. Эндпоинтов **самого оркестратора** (FastAPI `src/**`) изменение **не
вводит и не меняет**. Новых/изменённых orchestrator-endpoint'ов — **Нет.**
## 5. Изменения схемы БД
**Нет.** SQLite-схема, таблицы, миграции — не затрагиваются (правка в операторском CLI).
## 6. Требования к новым/изменённым QG checks
**Нет.** `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, `check_*`, machine-verdict ключи
(`verdict:`/`result:`/`deploy_status:`/`staging_status:`/`security_status:`/`coverage_status:`) —
байт-в-байт прежние. Багфикс-трек срезает только аналитику/архитектуру, не гейты (NFR-1 ORCH-019).
## 7. Артефакты pipeline, создаваемые/обновляемые
- Создаются analysis-доки `01-brd.md`, `02-trz.md`, `03-acceptance-criteria.md`,
`04-test-plan.yaml` (этот пакет).
- На дальнейших стадиях: `12-review.md` (reviewer), `13-test-report.md` (tester),
`14-deploy-log.md` (deployer). ADR (`06-adr/`) **не создаётся** — bug-трек, `architecture`
пропущена.
- Документация: при необходимости отметить факт в `CHANGELOG.md`; правка скрипта онбординга не
меняет нормативные шаги runbook'ов (LITE_SETUP/BUNDLED_SETUP/ONBOARDING) по существу — решает
developer/reviewer.
## 8. Совместимость / регресс
- **Обратная совместимость:** сигнатура и call-site неизменны; существующие онбординг-тесты
зелёные (NFR-1).
- **Область раската:** только операторский CLI `scripts/onboard_project.py`; активируется лишь
явным человеческим запуском (kill-switch не нужен — паттерн ORCH-009/102/103).
- **Self-hosting безопасность:** скрипт не деплоит/не рестартит прод-контейнер/не пушит `main`
(NFR-2). Idempotent-семантика `apply` (ensure без delete) сохраняется: повторный прогон по
существующему статусу остаётся `SKIPPED`.
- **Обратимость:** изменение тривиально откатывается (одна строка тела запроса).

View File

@@ -0,0 +1,82 @@
---
work_item: ORCH-106
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-12
model_used: claude-opus-4-8
track: bug
---
# 03 — Критерии приёмки (Acceptance Criteria, bug-shaped): ORCH-106
Work Item: **ORCH-106** · Repo: **orchestrator** · Стадия: analysis · Трек: **🐞 Bug**
Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL**
(что считается провалом). Reviewer/tester проверяют буквально по файлам репозитория и прогону тестов.
---
## AC-1 — `create_state` отправляет валидное поле `color`
**Условие:** тело POST, формируемое `PlaneClient.create_state` (`scripts/onboard_project.py`),
содержит ключ `color` с непустым значением.
- **PASS:** в теле присутствует ключ `color`, значение — непустая строка (`color is not None and
color != ""`). Для STOP (и любого имени) тело = `{"name", "group", "color", …}`.
- **FAIL:** тело не содержит `color`, либо `color` пустой/`null`.
---
## AC-2 — `color` — валидный hex-цвет
**Условие:** значение `color` — валидная hex-строка, принимаемая Plane CE.
- **PASS:** значение соответствует hex-формату цвета (например, `^#?[0-9A-Fa-f]{6}$` — конкретный
формат на усмотрение developer, но валидный для CE и непустой).
- **FAIL:** значение не является валидным hex-цветом (произвольный мусор, пустая строка, не строка).
---
## AC-3 — Сценарий STOP создаётся без 400 (BR-2)
**Условие:** создание статуса с именем **STOP** проходит.
- **PASS (детерминированно, CI):** регресс-тест подтверждает, что `create_state(project_id,
"STOP", "cancelled")` формирует тело с непустым валидным `color`.
- **PASS (живая проверка, ручной шаг приёмки):** реальный `POST …/projects/<id>/states/` с
`name=STOP` и `color` возвращает **`200/201`** (онбординг-отчёт: `plane.state:STOP … CREATED`).
- **FAIL:** живой `POST …/states/` для STOP возвращает `400`; или шаг отчёта — `ERROR`.
---
## AC-4 — Все статусы онбординга единообразно с `color` (BR-3)
**Условие:** цикл создания статусов (секция «2. Plane: статусы», по `_PLANE_NAME_TO_KEY`) для
каждого создаваемого статуса формирует тело с непустым валидным `color`.
- **PASS:** ни один статус из набора онбординга (включая STOP, `Confirm Deploy`, `Done`,
`Cancelled`, …) не отправляется без `color`.
- **FAIL:** хотя бы один статус отправляется с отсутствующим/пустым `color`.
---
## AC-5 — Нулевая регрессия и изоляция рантайма (NFR-1, NFR-2, NFR-4)
**Условие:** правка не ломает существующее поведение и не выходит за рамки CLI онбординга.
- **PASS:**
- Публичная сигнатура `create_state(project_id, name, group)` и call-site (строка 773) не
изменены; существующие онбординг-тесты (`tests/test_onboarding_*.py`) зелёные.
- `git diff` не затрагивает `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, схему БД,
`docker-compose*.yml`, `Dockerfile` (изменены только `scripts/onboard_project.py` и тест).
- Полный прогон `pytest tests/ -q` — зелёный.
- **FAIL:** сломан хоть один существующий тест; изменён рантайм/гейты/схема БД; изменена сигнатура
`create_state`.
---
## Сводная матрица AC ↔ FR/BR
| AC | Покрывает |
|----|-----------|
| AC-1 | BR-1 / FR-1 |
| AC-2 | NFR-3 / FR-1 |
| AC-3 | BR-2 / FR-2 |
| AC-4 | BR-3 / FR-1 |
| AC-5 | NFR-1 / NFR-2 / NFR-4 / FR-3 |

View File

@@ -0,0 +1,68 @@
work_item: ORCH-106
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-12
model_used: claude-opus-4-8
track: bug
title: "Регресс-тест: create_state шлёт валидный color (onboard_project.py)"
framework: pytest
scope: >
Покрывается ТОЛЬКО тело POST, формируемое PlaneClient.create_state в
scripts/onboard_project.py (наличие непустого валидного hex-поля color) + неизменность
существующего поведения онбординга. Вне покрытия: реальные сетевые вызовы к Plane CE
(детерминированный CI без сети — перехват _post/httpx.post), выбор палитры/значения цвета,
правки рантайма src/**.
notes: >
Обязательный регресс-тест багфикс-трека (ORCH-019): TC-01 ДОЛЖЕН быть КРАСНЫМ на коде до фикса
(тело без color) и ЗЕЛЁНЫМ после фикса. Тесты детерминированы и без сети: инстанцируется
РЕАЛЬНЫЙ PlaneClient, его _post (или httpx.post) монкипатчится для перехвата (url, payload);
FakePlane из существующего набора НЕ годится для регресса (мок не строит реальное тело).
Полный регресс `pytest tests/ -q` должен оставаться зелёным.
tests:
- id: TC-01
type: unit
description: >
[ОБЯЗАТЕЛЬНЫЙ РЕГРЕСС, red→green] Реальный PlaneClient.create_state(project_id, "STOP",
"cancelled") формирует тело POST с ключом 'color', значение — непустая строка. Перехват
через монкипатч PlaneClient._post: assert 'color' in payload and payload['color'].
КРАСНЫЙ до фикса (тело {'name','group'}), ЗЕЛЁНЫЙ после.
module: tests/test_onboarding_script.py
expected: PASS
- id: TC-02
type: unit
description: >
Значение payload['color'], отправляемое create_state, является валидной hex-строкой цвета
(соответствует hex-формату, напр. ^#?[0-9A-Fa-f]{6}$). Защита от непустого, но невалидного
значения.
module: tests/test_onboarding_script.py
expected: PASS
- id: TC-03
type: unit
description: >
Тело сохраняет существующие ключи: payload содержит корректные 'name' и 'group' (для STOP —
group 'cancelled' из STATE_GROUPS) ВМЕСТЕ с новым 'color'; добавление color не вытесняет
name/group.
module: tests/test_onboarding_script.py
expected: PASS
- id: TC-04
type: integration
description: >
Прогон секции создания статусов онбординга по нескольким статусам (включая STOP, Done,
Confirm Deploy): для КАЖДОГО перехваченное тело create_state несёт непустой валидный color;
ни один статус не отправляется без color.
module: tests/test_onboarding_script.py
expected: PASS
- id: TC-05
type: unit
description: >
Нулевая регрессия контракта: публичная сигнатура create_state(project_id, name, group)
неизменна; существующие онбординг-тесты (FakePlane mutations ('create_state', name, group),
call-site строка 773) остаются зелёными.
module: tests/test_onboarding_script.py
expected: PASS