Files
orchestrator/docs/work-items/ORCH-046/02-trz.md
claude-bot c7bca51d4b
All checks were successful
CI / test (push) Successful in 12s
analyst(ET): auto-commit from analyst run_id=139
2026-06-06 04:09:41 +00:00

210 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ТЗ — ORCH-046: встраивание текста findings reviewer/tester в task_desc
Work Item ID: ORCH-046
Stage: analysis
Author: analyst
Date: 2026-06-06
> Вариант A (минимальный, низкий риск). Это правка ЯДРА — обязателен ADR
> (per-work-item, `docs/work-items/ORCH-046/06-adr/`).
## 1. Задействованные модули `src/`
| Модуль | Изменение |
|--------|-----------|
| `src/review_parse.py` | **НОВЫЙ** хелпер-парсер: `extract_review_findings(path) -> str`, `extract_test_failures(path) -> str`. |
| `src/stage_engine.py` | Две ветки в `_handle_qg_failure_rollbacks`: reviewer REQUEST_CHANGES (~стр. 419) и tester `check_tests_passed` FAIL (~стр. 455) — встраивают извлечённый текст в `task_desc`. |
Источники-образцы (не менять, использовать как референс паттерна «never raise» и
формата артефактов):
- `src/qg/checks.py::_parse_tests_verdict` — образец «never raise», split по `---`, `yaml.safe_load`.
- `src/frontmatter.py::read_frontmatter_value` — образец defensive-парсера.
- `.openclaw/agents/reviewer.md` — канонический формат `12-review.md`.
- `.openclaw/agents/tester.md` — канонический формат `13-test-report.md`.
## 2. Новый модуль `src/review_parse.py`
### 2.1. `extract_review_findings(path: str) -> str`
Назначение: вернуть **дословный** текст must-fix findings (P0 + P1) из
`12-review.md` для встраивания в `task_desc`.
Формат входного файла (канон reviewer.md, секция `## Findings`):
```markdown
## Findings
### P0 — Blocker
- [ ] <описание>
### P1 — Must fix
- [ ] <описание>
### P2 — Should fix
- [ ] <описание>
```
Требования к реализации:
1. **Никогда не бросает исключение.** Любая ошибка (нет файла, IOError, кривой
markdown, нет секции `## Findings`) → возврат `""` (пустая строка).
2. Парсит **только** подсекции P0 и P1 (must-fix). P2/P3 игнорируются.
3. Заголовки подсекций распознаются устойчиво к регистру и к тире/дефису:
соответствие по наличию токена `P0` / `P1` в строке-заголовке уровня `###`.
4. Из распознанных подсекций берётся текст до следующего заголовка `###`/`##`
(т. е. тело подсекции дословно: пункты списка `- [ ] …` / `- …`).
5. Пустые подсекции (нет содержательных пунктов, только `(если есть)`-плейсхолдер
или ничего) — пропускаются. Если ни одного содержательного P0/P1 пункта нет
→ возврат `""`.
6. Результат — компактный многострочный текст, пригодный для вставки в
`task_desc` (например, заголовок подсекции + её пункты). Длина результата
ограничивается разумным лимитом (`MAX_FINDINGS_CHARS`, напр. 2000) с
усечением и маркером `…(truncated)`; полный контекст всё равно остаётся в
файле.
7. Frontmatter (верхний `--- … ---`) при необходимости отбрасывается, чтобы не
попасть в тело; парсинг секции делается по телу markdown.
Сигнатура и контракт (стабильны):
```python
def extract_review_findings(path: str) -> str:
"""Дословный текст P0/P1 findings из 12-review.md. Never raises; '' при ошибке/пусто."""
```
### 2.2. `extract_test_failures(path: str) -> str`
Назначение: вернуть текст причины падения тестов из `13-test-report.md` для
встраивания в `task_desc`.
Формат входного файла (канон tester.md): frontmatter `result: PASS|FAIL`, далее
тело с секциями `## Результаты` (таблица TC), `## Вывод pytest`, `## Итог`.
Требования к реализации:
1. **Никогда не бросает исключение.** Любая ошибка → возврат `""`.
2. Извлекает релевантный фрагмент тела, помогающий понять причину FAIL.
Приоритет источников (берём первый непустой):
- секция `## Вывод pytest` (вывод прогона — где видно упавшие тесты), и/или
- строки таблицы `## Результаты`, содержащие `FAIL`, и/или
- секция `## Итог`.
3. Результат усекается до `MAX_FAILURES_CHARS` (напр. 2000) с маркером
`…(truncated)`.
4. Если ничего извлечь не удалось → возврат `""` (вызывающий код делает
fallback на ссылку).
> Примечание: «reason» из самого гейта (`check_tests_passed` → второй элемент
> кортежа) у вызывающего кода уже есть (`reason`) — он добавляется в `task_desc`
> вызывающим кодом (как и сейчас в комментарии тестера). `extract_test_failures`
> добавляет **фрагмент тела отчёта** поверх этого reason.
Сигнатура и контракт (стабильны):
```python
def extract_test_failures(path: str) -> str:
"""Релевантный фрагмент тела 13-test-report.md (причина FAIL). Never raises; '' при ошибке/пусто."""
```
### 2.3. Общие требования модуля
- Модуль логирует диагностические сообщения на уровне `logger.debug`
(`logging.getLogger("orchestrator.review_parse")`), как `frontmatter.py`.
- Никаких сетевых вызовов, только чтение файла с диска.
- Константы лимитов вынесены модульными (`MAX_FINDINGS_CHARS`,
`MAX_FAILURES_CHARS`).
## 3. Изменения `src/stage_engine.py`
### 3.1. Ветка reviewer REQUEST_CHANGES (внутри `_handle_qg_failure_rollbacks`)
Текущее (~стр. 418424):
```python
task_desc = (
f"Work item: {work_item_id}\nRepo: {repo}\nBranch: {branch}\n"
f"Stage: development\nNote: REQUEST_CHANGES from reviewer "
f"(attempt {retry_count+1}/3). Fix findings in "
f"docs/work-items/{work_item_id}/12-review.md"
)
```
Целевое поведение:
- Сформировать путь к `12-review.md` через `get_worktree_path(repo, branch)` +
`docs/work-items/{work_item_id}/12-review.md` (как в `_check_review_approved_by_branch`).
- Вызвать `extract_review_findings(path)`.
- Если результат непустой — встроить findings **дословно** в `task_desc`
(под подзаголовком, напр. `Findings (P0/P1):\n<text>`), а ссылку на файл
оставить как «полный контекст» (`Полный контекст: docs/work-items/<id>/12-review.md`).
- Если результат пустой (graceful fallback) — `task_desc` остаётся **как
сейчас** (ссылка-строка). Никаких исключений.
- Префиксная часть (`Work item / Repo / Branch / Stage / Note: REQUEST_CHANGES …
(attempt N/3)`) сохраняется без изменений.
### 3.2. Ветка tester FAIL (`check_tests_passed`, внутри `_handle_qg_failure_rollbacks`)
Текущее (~стр. 454459):
```python
task_desc = (
f"Work item: {work_item_id}\nRepo: {repo}\nBranch: {branch}\n"
f"Stage: development\nNote: Tests FAILED. "
f"Fix failures described in docs/work-items/{work_item_id}/13-test-report.md"
)
```
Целевое поведение:
- Сформировать путь к `13-test-report.md` аналогично.
- Вызвать `extract_test_failures(path)`.
- В `task_desc` всегда включить `reason` (он уже доступен в этой ветке —
передаётся в `_handle_qg_failure_rollbacks`).
- Если фрагмент тела непустой — встроить его дословно
(`Причина: {reason}\nДетали:\n<fragment>`), плюс ссылку на файл как полный
контекст.
- Если фрагмент пустой — `task_desc` содержит `reason` + ссылку (graceful
fallback, не хуже текущего поведения). Никаких исключений.
- Префиксная часть и существующий Plane-комментарий тестера
(`❌ Тесты не прошли: {reason}…`) НЕ меняются.
### 3.3. Инварианты (НЕ менять поведение)
- Последовательность rollback в обеих ветках: `update_task_stage(task_id,
"development")` → `notify_stage_change` → `plane_notify_stage` →
(`set_issue_in_progress` для тестера) → проверка `_developer_retry_count` <
`MAX_DEVELOPER_RETRIES` → `enqueue_job("developer", …)` либо
`send_telegram` alert. Порядок и условия идентичны.
- `result.rolled_back_to`, `result.enqueued_agent`, `result.enqueued_job_id`,
`result.alerted` выставляются как сейчас.
- Меняется **только содержимое строки `task_desc`**, передаваемой в
`enqueue_job`.
- Импорт нового модуля — `from .review_parse import extract_review_findings,
extract_test_failures` в шапке `stage_engine.py`.
## 4. Изменения API
Нет. Публичные HTTP-эндпоинты (`/health`, `/status`, `/queue`,
`/webhook/plane`, `/webhook/gitea`) не затрагиваются.
## 5. Изменения схемы БД
Нет. Таблицы `tasks`, `agent_runs`, `jobs`, `events` не меняются.
`enqueue_job` вызывается с прежней сигнатурой.
## 6. Требования к новым QG checks
Нет. Реестр `QG_CHECKS` и все `check_*` не трогаются (явно out of scope).
## 7. Артефакты pipeline (создать/обновить в этом PR)
- `src/review_parse.py` — новый модуль.
- `tests/test_review_parse.py` — юнит-тесты парсера (см. 04-test-plan.yaml).
- Возможные дополнения в `tests/test_stage_engine.py` — проверка встраивания
текста в `task_desc` (rollback-ветки).
- `docs/work-items/ORCH-046/06-adr/ADR-001-*.md` — ADR (правка ядра).
- `docs/architecture/README.md` / `internals.md` — описание нового хелпера и
поведения заворотов (если reviewer сочтёт необходимым; компонент описать в
разделе Stage Engine / Откаты).
- `CHANGELOG.md` — запись о ORCH-046.
## 8. Контроль качества / проверка
```bash
python -m pytest tests/ -q # в контейнере; все тесты зелёные
```
Обязательно: стадия `deploy-staging` (8501) перед прод-деплоем (self-hosting).