From 5646552f02ea3820fd5e587cb8ad7b4de5131c39 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Wed, 17 Jun 2026 14:01:48 +0300 Subject: [PATCH] analyst(ET): auto-commit from analyst run_id=783 --- docs/work-items/ORCH-119/01-brd.md | 151 ++++++++++++++++++ docs/work-items/ORCH-119/02-trz.md | 86 ++++++++++ .../ORCH-119/03-acceptance-criteria.md | 109 +++++++++++++ docs/work-items/ORCH-119/04-test-plan.yaml | 64 ++++++++ 4 files changed, 410 insertions(+) create mode 100644 docs/work-items/ORCH-119/01-brd.md create mode 100644 docs/work-items/ORCH-119/02-trz.md create mode 100644 docs/work-items/ORCH-119/03-acceptance-criteria.md create mode 100644 docs/work-items/ORCH-119/04-test-plan.yaml diff --git a/docs/work-items/ORCH-119/01-brd.md b/docs/work-items/ORCH-119/01-brd.md new file mode 100644 index 0000000..c38a027 --- /dev/null +++ b/docs/work-items/ORCH-119/01-brd.md @@ -0,0 +1,151 @@ +--- +work_item: ORCH-119 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +--- + +# 01 — BRD / Bug-report: ORCH-119 — `00-business-request.md` всегда `TBD`, теряется source-backed контекст запроса + +Work Item: **ORCH-119** · Repo: **orchestrator** · Стадия: analysis · Трек: **Bug** (укороченный маршрут, пропуск стадии `architecture`) + +> 🐞 **Багфикс-трек (ORCH-019).** Облегчённый пакет (bug-report + обязательный регресс-тест), но +> все 4 файла analysis (гейт `check_analysis_complete` не меняется). Экономия — в пропуске стадии +> `architecture`, не в числе файлов. +> +> **Эскалация в full-cycle рассмотрена и отклонена.** Дефект — контейнерный data-flow + рендеринг, +> чинится **точным зеркалированием уже существующего прецедента `tasks.title`** (персист при создании +> задачи → чтение в `_materialize_deferred_branch`). Нет нового компонента, нового QG, политического +> решения или визуального артефакта → ADR/макет не требуется. Если разработчик в ходе фикса упрётся в +> архитектурное решение (напр. иной механизм персиста, влияющий на схему/контракты) — снять трек: +> `POST /bug-fast-track/escalate?work_item=ORCH-119` и пометить здесь `escalate: full-cycle`. + +--- + +## 1. Бизнес-контекст и проблема + +### Симптом (наблюдаемое) +Для **каждой** созданной задачи файл `docs/work-items//00-business-request.md` генерируется +с телом раздела «Description» равным буквальному `TBD`. Реальный текст запроса (описание Plane-issue, +обогащённое из Plane API) **не попадает** в персистентный артефакт. Пример — сам этот work item: + +``` +# Business Request: BUG: 00-business-request.md is always TBD, losing source-backed request context +Work Item ID: ORCH-119 +## Description +TBD +``` + +### Последствие (бизнес-боль) +`00-business-request.md` — **точка входа конвейера** и источник для analyst (вход стадии `analysis`, +см. `PIPELINE_DOCS.md` §2). Когда тело всегда `TBD`: +- source-backed контекст запроса теряется из durable-артефакта репозитория (остаётся только эфемерно + в `task_content` analyst-job'а и в Plane); +- последующее чтение work item «глазами» (reviewer, человек, ретроспектива, петля уроков) видит пустой + бизнес-запрос — невозможно сверить, ту ли задачу решал конвейер; +- на **self-hosting** (`orchestrator`) задача почти всегда идёт **отложенным срезом ветки** (serial + gate, ORCH-088), где контекст теряется безвозвратно (см. §3, причина B). + +### Причина симптома (установленный факт, по коду) +`src/webhooks/plane.py::_create_initial_docs` (строка ~925) **хардкодит** тело: +```python +content = f"# Business Request: {name}\n\nWork Item ID: {work_item_id}\n\n## Description\n\nTBD\n" +``` +Функция принимает только `(repo, branch, work_item_id, name)` — **`description` ей не передаётся**, +хотя у вызывающего `start_pipeline` оно есть в области видимости и уже используется для analyst-job +(`task_desc`, строка ~725: `Description:\n{description}`). То есть данные **есть**, но в артефакт не +доходят. + +### Локализация (куда смотреть разработчику) — два пути создания + +**Путь A — прямой** (`serial_gate` не применим к репо): +`start_pipeline` (`src/webhooks/plane.py`) имеет `description` (строки ~518; обогащается из Plane API, +~539–551) → зовёт `_create_initial_docs(repo, branch, work_item_id, name)` (строка ~710) **без** +`description`. Достаточно дотянуть аргумент. + +**Путь B — отложенный (критичный для self-hosting)** (`serial_gate_applies(repo)` → для `orchestrator`): +`start_pipeline` **не** создаёт ветку/доки (ORCH-088, анти-stale-base); срез релоцирован в +`src/agents/launcher.py::_materialize_deferred_branch` (строки ~514–538), который вызывает то же +`_create_initial_docs`, **располагая только `title`** из строки `tasks` (`description` нигде не +персистится). Установленный факт схемы: таблица `tasks` **не имеет** колонки `description`; `title` +персистится через `_ensure_column` (`src/db.py:125`) и читается в `_spawn`/`_materialize_deferred_branch` +именно так. ⇒ Чтобы путь B рендерил описание, `description` надо **сохранить durable при создании +задачи** (зеркало `tasks.title`). + +### Предусловие истинности данных (установленный факт) +QG-0 (`_qg0_errors`, `src/webhooks/plane.py:490`) отклоняет создание при `description` короче 20 +символов (строка ~500). ⇒ любая задача, дошедшая до `_create_initial_docs`, **гарантированно имеет +непустое осмысленное описание** — терять его тем более недопустимо. Защитный fallback на случай +пустого описания всё равно предусмотреть (NFR-2). + +## 2. Объём (scope) + +### В объёме +- Рендер фактического `description` (предпочтительно `description_stripped`, plain-text) в раздел + «Description» файла `00-business-request.md` — на **обоих** путях (A прямой, B отложенный). +- Durable-персист `description` при создании задачи (зеркало `tasks.title`), чтобы путь B имел доступ + к нему на момент claim. +- Защитный fallback при отсутствии/пустом описании (без падения). +- Обязательный регресс-тест (красный до фикса, зелёный после). + +### Вне объёма +- Изменение `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict-ключей / семантики гейтов. +- Изменение поведения serial-gate / отложенного среза ветки ORCH-088 (только **дополнить** данными, + не менять момент/условие среза). +- Ретро-генерация `00-business-request.md` для **уже существующих** задач (только новые создания). +- Переформатирование/обогащение структуры самого `00-business-request.md` сверх вставки описания + (заголовки/название источника остаются как есть). +- Любая запись в Plane (артефакт пишется только в Gitea-ветку, как сейчас). + +## 3. Заинтересованные стороны +- **Заказчик/оператор** — получает читаемый durable бизнес-запрос вместо `TBD`. +- **Агент analyst и reviewer** — могут сверять решённое с запросом по репозиторию. +- **Петля уроков / ретроспектива (ORCH-098)** — корректный контекст в артефакте. +- Приёмку результата выполняет конвейер (reviewer + Quality Gates), не аналитик. + +## 4. Бизнес-требования (BR) +- **BR-1** — Раздел «Description» в `00-business-request.md` содержит **фактический текст запроса** + (из Plane-issue, как он используется для analyst-job'а), а не литерал `TBD`, для вновь создаваемых + задач. +- **BR-2** — Поведение одинаково на **обоих** путях создания: прямом (A) и отложенном срезе ветки (B, + self-hosting/serial-gate). Путь B — приоритетный сценарий (доминирует на `orchestrator`). +- **BR-3** — При отсутствующем/пустом описании артефакт создаётся с **явным безопасным fallback**- + маркером (напр. «описание отсутствует в источнике»), без падения создания задачи. +- **BR-4** — Сохранён состав/имена артефактов: создаётся ровно `00-business-request.md` по тому же + пути; downstream-конвейер (analyst и далее) не затронут. + +## 5. Нефункциональные требования (NFR) +- **NFR-1 (обратная совместимость / never-break)** — изменение аддитивно: создание задачи **никогда** + не должно падать из-за нового рендера/персиста. Любая ошибка обогащения → деградация на безопасное + значение (fallback-маркер), а не отказ создания. Идемпотентность `_create_initial_docs` (422 = уже + существует → no-op) сохранена. +- **NFR-2 (целостность данных)** — описание рендерится **как есть** (plain-text `description_stripped`), + без обрезки/искажения; многострочный текст сохраняется. `00-business-request.md` — информационный + док (гейтом не парсится), поэтому markdown-спецсимволы в описании безопасны для гейтов. +- **NFR-3 (инварианты ORCH-088)** — момент и условие отложенного среза ветки не меняются; описание + лишь дополнительно переносится через durable-хранилище (зеркало `tasks.title`), анти-stale-base + логика цела. +- **NFR-4 (self-hosting-безопасность)** — фикс не деплоит/не рестартит прод, не трогает `main`, не + добавляет сетевых вызовов в горячий `claim_next_job`. + +## 6. Допущения и ограничения +- `description`/`description_stripped` доступны в `start_pipeline` и достаточны как источник (уже + используются для analyst-job). Plane-обогащение (ORCH «name_missing/desc_missing» блок) остаётся + единственным источником описания — новых сетевых обращений не вводим. +- QG-0 гарантирует ≥20 символов описания для прошедших задач (см. §1) — нормальный путь всегда имеет + реальный текст. +- Персист описания следует **установленному прецеденту `tasks.title`** (аддитивная колонка через + `_ensure_column`); это не новое архитектурное решение. + +## 7. Критерии успеха +Новые задачи получают `00-business-request.md` с реальным описанием на обоих путях; обязательный +регресс-тест красный до фикса и зелёный после; полный `pytest tests/ -q` зелёный. Детальные PASS/FAIL +— `03-acceptance-criteria.md`. + +## 8. Риски +- Путь B забыт (чинят только прямой путь A) → на self-hosting баг остаётся. Митигируется обязательным + integration-тестом пути B (TC-03). +- Регресс схемы/создания задачи при добавлении персиста → митигируется аддитивным `_ensure_column` и + тестом обратной совместимости (TC-05). Детальные тех-риски архитектором не выпускаются (bug-track). diff --git a/docs/work-items/ORCH-119/02-trz.md b/docs/work-items/ORCH-119/02-trz.md new file mode 100644 index 0000000..30afc0f --- /dev/null +++ b/docs/work-items/ORCH-119/02-trz.md @@ -0,0 +1,86 @@ +--- +work_item: ORCH-119 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +--- + +# 02 — ТЗ (TRZ): ORCH-119 — source-backed генерация `00-business-request.md` + +Work Item: **ORCH-119** · Repo: **orchestrator** · Стадия: analysis · Трек: **Bug** + +> Bug-track: стадия `architecture` пропускается, поэтому ТЗ конкретизирует затрагиваемые модули и +> требование к данным. ТЗ описывает **что должно измениться и где**; точная форма реализации (имена +> символов, сигнатуры) — за разработчиком, в рамках указанного прецедента `tasks.title`. + +## 1. Сводка изменения +Source-backed `description` (текст запроса из Plane-issue) должен попадать в раздел «Description» +файла `00-business-request.md` вместо хардкода `TBD`. Для этого: (1) `description` рендерится в тело +артефакта; (2) `description` **персистится при создании задачи** (зеркало `tasks.title`), чтобы +отложенный путь среза ветки (ORCH-088, доминирует на self-hosting) имел к нему доступ на момент claim. +Изменение аддитивно, never-break, fail-safe. + +## 2. Задействованные модули / пути +| Путь | Действие | +|------|----------| +| `src/webhooks/plane.py` | изменить — `_create_initial_docs`: принять `description`, рендерить его в тело вместо `TBD`; рекомендуется выделить чистый рендер-хелпер (напр. `_render_business_request(work_item_id, name, description) -> str`) для unit-тестируемости без сети | +| `src/webhooks/plane.py` | изменить — `start_pipeline`: (а) прямой путь — передать `description` в `_create_initial_docs` (строка ~710); (б) персистить `description` при создании задачи (рядом со стампом `title`) | +| `src/agents/launcher.py` | изменить — `_materialize_deferred_branch`: прочитать персистнутое `description` из строки `tasks` и передать в `_create_initial_docs` (зеркало того, как уже читается/используется `title`) | +| `src/db.py` | изменить — аддитивная колонка `tasks.description` через `_ensure_column` (паттерн `tasks.title`, строка ~125); хелпер чтения/записи при необходимости; **не менять** базовый `CREATE TABLE tasks` | +| `tests/test_orch119_business_request.py` | создать — регресс + edge-кейсы (см. `04-test-plan.yaml`) | +| `CHANGELOG.md` | изменить — запись о фиксе (правило сопровождения) | + +## 3. Функциональные требования + +### FR-1 — Рендер описания в артефакт (BR-1) +Тело раздела «Description» в `00-business-request.md` = фактический `description` (предпочтительно +`description_stripped`, plain-text), без обрезки/искажения, многострочный текст сохраняется. Заголовок +(`# Business Request: {name}`) и `Work Item ID` — без изменений. + +### FR-2 — Прямой путь (BR-2, путь A) +`start_pipeline` при `serial_gate` НЕ применим → передаёт `description` в `_create_initial_docs`; +артефакт создаётся с реальным описанием в одном вызове. + +### FR-3 — Отложенный путь / персист (BR-2, путь B — критичный) +`description` персистится durable при создании задачи (зеркало `tasks.title`). +`_materialize_deferred_branch` читает его из строки `tasks` и передаёт в `_create_initial_docs`, так +что артефакт, материализованный на момент claim analyst-job, содержит реальное описание. Момент/условие +отложенного среза (ORCH-088) **не меняются** — только источник данных дополняется (NFR-3). + +### FR-4 — Fallback и устойчивость (BR-3, NFR-1) +Пустое/отсутствующее/нечитаемое `description` → явный безопасный маркер (напр. +`_(описание отсутствует в источнике)_`), **без падения** создания задачи. Любая ошибка рендера/чтения +персиста → деградация на fallback-маркер, не отказ. Идемпотентность сохранена: повторная +материализация (Gitea 422 = файл уже существует) → no-op, ранее записанное тело не перезаписывается. + +## 4. Изменения API +Нет. Эндпоинты не добавляются и не меняются. Запись артефакта остаётся в Gitea-ветку через +существующий `contents` API; в Plane ничего не пишется. + +## 5. Изменения схемы БД +Аддитивная колонка **`tasks.description TEXT`** через `_ensure_column` (идемпотентный ALTER, no-op на +существующей таблице — safe на боевой БД), строго по прецеденту `tasks.title`. Базовый +`CREATE TABLE tasks` не трогается. Индексы не требуются. Для уже существующих задач колонка `NULL` +(ретро-генерация — вне объёма). + +> Допустимая эквивалентная реализация (на усмотрение разработчика): переиспользовать уже доступный +> в `_spawn`/`_materialize_deferred_branch` источник данных, если он durable до момента claim. Если +> такой надёжно нет — канон именно колонка `tasks.description` (как `tasks.title`). Решение не должно +> вводить сетевой вызов в горячий путь claim (NFR-4). + +## 6. Требования к новым/изменённым QG checks +Нет. `00-business-request.md` — информационный документ (гейтом не парсится, `PIPELINE_DOCS.md` §2–§3). +`STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict-ключи — байт-в-байт не трогаются. + +## 7. Совместимость / регресс +- **Обратная совместимость:** аддитивная колонка + аддитивный аргумент с дефолтом; существующие задачи + и enduro-trails не затронуты (для них тоже просто рендерится их описание — улучшение, не регресс). +- **Kill-switch:** отдельный флаг не требуется — изменение это исправление дефекта (улучшение + «всегда»), не рискованная фича; безопасность обеспечивается fail-safe fallback и never-break-контрактом. +- **Обратимость:** revert PR полностью возвращает прежнее поведение (колонка остаётся, инертна). +- **Self-hosting:** не деплоит/не рестартит прод, не трогает `main`, без новых сетевых вызовов в + `claim_next_job` (NFR-4). Анти-stale-base инвариант ORCH-088 цел (NFR-3) — перед правкой + `_materialize_deferred_branch`/отложенного среза свериться с `docs/work-items/ORCH-088/06-adr/` + (TRACEABILITY). diff --git a/docs/work-items/ORCH-119/03-acceptance-criteria.md b/docs/work-items/ORCH-119/03-acceptance-criteria.md new file mode 100644 index 0000000..94d9ce4 --- /dev/null +++ b/docs/work-items/ORCH-119/03-acceptance-criteria.md @@ -0,0 +1,109 @@ +--- +work_item: ORCH-119 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +--- + +# 03 — Критерии приёмки: ORCH-119 — source-backed `00-business-request.md` + +Work Item: **ORCH-119** · Repo: **orchestrator** · Стадия: analysis · Трек: **Bug** + +Формат: каждый критерий — **PASS** (что должно быть истинно для приёмки) и **FAIL** (что считается +провалом). Reviewer/тесты проверяют буквально по файлам репозитория. + +--- + +## AC-1 — Реальное описание в артефакте вместо `TBD` + +**Условие:** при создании задачи с непустым описанием раздел «Description» файла +`00-business-request.md` содержит фактический текст запроса. +- **PASS:** тело раздела «Description» равно переданному `description` (plain-text); подстрока с + реальным текстом присутствует; литерал `TBD` как ВСЁ тело раздела отсутствует. +- **FAIL:** раздел «Description» = `TBD` (или иной плейсхолдер) при наличии непустого описания на входе. + +--- + +## AC-2 — Прямой путь создания (путь A) + +**Условие:** когда `serial_gate` не применим, `start_pipeline` передаёт `description` в +`_create_initial_docs`. +- **PASS:** артефакт, созданный прямым путём, содержит реальное описание (см. AC-1). +- **FAIL:** на прямом пути `description` не доходит до артефакта (остаётся `TBD`). + +--- + +## AC-3 — Отложенный путь / self-hosting (путь B, критичный) + +**Условие:** когда `serial_gate_applies(repo)` (напр. `orchestrator`), описание персистится при +создании задачи и рендерится при материализации ветки на момент claim +(`launcher._materialize_deferred_branch`). +- **PASS:** артефакт, материализованный отложенным путём, содержит реальное описание; описание + durable-доступно из строки `tasks` (не теряется между созданием задачи и claim). +- **FAIL:** на отложенном пути описание утрачено → артефакт = `TBD`; либо описание нигде не + персистится до момента claim. + +--- + +## AC-4 — Безопасный fallback при пустом описании + +**Условие:** описание отсутствует/пустое/нечитаемо. +- **PASS:** артефакт создаётся с явным безопасным fallback-маркером (не «голый» `TBD`-баг), создание + задачи не падает. +- **FAIL:** создание задачи падает/бросает исключение, либо тихо теряет контекст без маркера. + +--- + +## AC-5 — Никаких изменений гейтов / схемы сверх аддитивной колонки + +**Условие:** правка не трогает машинерию конвейера. +- **PASS:** `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, имена/семантика `check_*`, machine-verdict-ключи + — байт-в-байт прежние; единственное изменение схемы — аддитивная `tasks.description` (или + эквивалент без сетевого вызова в claim); базовый `CREATE TABLE tasks` не тронут. +- **FAIL:** изменены гейты/переходы/вердикт-ключи; неаддитивная миграция; сетевой вызов добавлен в + `claim_next_job`. + +--- + +## AC-6 — Идемпотентность и обратная совместимость + +**Условие:** повторная материализация и существующие задачи. +- **PASS:** повторный вызов (`_create_initial_docs`, Gitea 422) — no-op, не перезаписывает тело; + `_ensure_column` идемпотентен (no-op при существующей колонке); enduro-trails и прочие репо не + затронуты негативно. +- **FAIL:** повторная материализация затирает/дублирует артефакт; миграция падает на боевой БД; + регресс для других репо. + +--- + +## AC-7 — Обязательный регресс-тест (red→green) и зелёный полный прогон + +**Условие:** дефект зафиксирован тестом. +- **PASS:** `tests/test_orch119_business_request.py::TC-01` падает на коде ДО фикса (доказывает баг) и + проходит ПОСЛЕ; полный `pytest tests/ -q` зелёный. +- **FAIL:** регресс-теста нет, либо он зелёный и до фикса (ничего не доказывает), либо полный прогон + красный. + +--- + +## AC-8 — Сопровождение + +**Условие:** документация/история обновлены в том же PR. +- **PASS:** `CHANGELOG.md` содержит запись о фиксе ORCH-119. +- **FAIL:** изменён функционал без обновления `CHANGELOG.md`. + +--- + +## Сводная матрица AC ↔ FR/BR +| AC | Покрывает | +|----|-----------| +| AC-1 | BR-1 / FR-1 | +| AC-2 | BR-2 / FR-2 | +| AC-3 | BR-2 / FR-3 / NFR-3 | +| AC-4 | BR-3 / FR-4 / NFR-1 | +| AC-5 | BR-4 / NFR-4 | +| AC-6 | NFR-1 / FR-4 | +| AC-7 | BR-1..BR-3 (доказательство) | +| AC-8 | правило сопровождения | diff --git a/docs/work-items/ORCH-119/04-test-plan.yaml b/docs/work-items/ORCH-119/04-test-plan.yaml new file mode 100644 index 0000000..47e6e92 --- /dev/null +++ b/docs/work-items/ORCH-119/04-test-plan.yaml @@ -0,0 +1,64 @@ +work_item: ORCH-119 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-17 +model_used: claude-opus-4-8 +title: "Source-backed генерация 00-business-request.md (фикс хардкода TBD)" +framework: pytest +scope: > + Покрывается: рендер фактического description в 00-business-request.md вместо литерала TBD на обоих + путях создания (прямой A и отложенный срез ветки B / self-hosting ORCH-088), durable-персист + description (зеркало tasks.title), безопасный fallback при пустом описании, аддитивность схемы и + обратная совместимость. ВНЕ покрытия: реальные сетевые вызовы Gitea/Plane (мокаются), ретро-генерация + артефактов для уже существующих задач. +notes: > + TC-01 — ОБЯЗАТЕЛЬНЫЙ регресс-тест: красный на коде ДО фикса (доказывает баг хардкода TBD), зелёный + ПОСЛЕ. Сетевые вызовы (_create_gitea_branch / _create_initial_docs httpx, Plane API) мокаются — + тесты без сети/прода. Рекомендуется тестировать чистый рендер-хелпер (_render_business_request) на + уровне unit, а пути A/B — на уровне integration с моками httpx и временной SQLite-БД. Полный регресс + pytest tests/ -q должен оставаться зелёным. Перед правкой отложенного среза ветки свериться с + docs/work-items/ORCH-088/06-adr/ (анти-stale-base инвариант, TRACEABILITY). + +tests: + - id: TC-01 + type: unit + description: "ОБЯЗАТЕЛЬНЫЙ РЕГРЕСС. Рендер 00-business-request.md при непустом description содержит фактический текст запроса в разделе 'Description' и НЕ равен литералу TBD. Красный до фикса (хардкод TBD), зелёный после." + module: tests/test_orch119_business_request.py + expected: PASS + + - id: TC-02 + type: unit + description: "Fallback: при пустом/whitespace/None description рендер даёт явный безопасный маркер (напр. 'описание отсутствует в источнике'), функция не бросает исключение." + module: tests/test_orch119_business_request.py + expected: PASS + + - id: TC-03 + type: integration + description: "Путь B (отложенный, self-hosting): description персистнут при создании задачи и доступен из строки tasks; launcher._materialize_deferred_branch рендерит реальное описание в артефакт (мок httpx; description не теряется между созданием и claim)." + module: tests/test_orch119_business_request.py + expected: PASS + + - id: TC-04 + type: integration + description: "Путь A (прямой, serial_gate не применим): start_pipeline передаёт description в _create_initial_docs; артефакт содержит реальное описание (мок httpx, перехват записанного content)." + module: tests/test_orch119_business_request.py + expected: PASS + + - id: TC-05 + type: integration + description: "Обратная совместимость схемы: init_db на пустой БД и на БД со старой таблицей tasks (без колонки description) проходит; _ensure_column идемпотентен (повторный init_db — no-op); создание задачи не падает." + module: tests/test_orch119_business_request.py + expected: PASS + + - id: TC-06 + type: unit + description: "Целостность данных: многострочное описание со спецсимволами markdown рендерится без обрезки/искажения; идемпотентность — повторная материализация (Gitea 422) не перезаписывает уже записанное тело." + module: tests/test_orch119_business_request.py + expected: PASS + + - id: TC-07 + type: unit + description: "Анти-регресс гейтов: STAGE_TRANSITIONS / реестр QG_CHECKS / имена check_* импортируются без изменений (00-business-request.md остаётся информационным, не гейтится)." + module: tests/test_orch119_business_request.py + expected: PASS