# ADR-001: testing-гейт читает `result:` наравне с `verdict:`/`status:`

- **Статус:** Accepted
- **Дата:** 2026-06-05
- **Задача:** ORCH-047
- **Область:** SHARED quality-gate `check_tests_passed` (общий прод-инстанс: orchestrator + enduro-trails)

## Контекст

Quality Gate `check_tests_passed` (`src/qg/checks.py`, парсер `_parse_tests_verdict`) гейтит
переход `testing → deploy-staging`, читая машиночитаемый вердикт ТОЛЬКО из YAML-frontmatter
артефакта `13-test-report.md` (канон гейтов: frontmatter, никогда не проза — см.
`docs/architecture/README.md`).

Существует рассинхрон контракта между производителем и потребителем вердикта:

- **Потребитель** (`_parse_tests_verdict`) читает поля `verdict:` и `status:`.
- **Производитель** (`.openclaw/agents/tester.md`, строки 51–56, 78–80) предписывает тестеру
  эмитить машиночитаемое поле **`result: PASS|FAIL`** и НЕ упоминает `verdict:`/`status:`.

Тестер, честно следуя своей инструкции, пишет `result: PASS` без `verdict:`/`status:`. Парсер
попадает в ветку «ни verdict, ни status не заданы» → `(False, "No machine-readable
verdict/status…")` → откат `testing → development` и петля до исчерпания
`MAX_DEVELOPER_RETRIES`. Это наблюдалось на ORCH-17; ORCH-016 прошёл лишь потому, что его отчёт
избыточно нёс И `verdict:`, И `result:`.

Корень — несовпадение имён поля контракта, а не логики токенов. Наборы positive/negative-токенов
исправны и менять их нельзя (обратная совместимость с реальными отчётами enduro-trails
ET-001…ET-014).

## Решение

Привести контракт гейта к тому, что тестеру УЖЕ велено эмитить — со стороны гейта, не трогая
промпт тестера.

1. `_parse_tests_verdict` читает **три равноправных** машиночитаемых поля из frontmatter:
   `result:` (канон промпта тестера), `verdict:`, `status:` (легаси/enduro-trails). Достаточно
   ЛЮБОГО одного непустого.
2. Семантика приоритетов сохраняется и распространяется на все три поля через объединённую строку
   `fields = f"{verdict} {status} {result}"`:
   - negative-токен (`_TESTS_NEGATIVE_TOKENS`) в любом поле → FAIL и **авторитетен** (проверяется
     первым, перебивает positive в другом поле);
   - иначе positive-токен (`_TESTS_POSITIVE_TOKENS`) в любом поле → PASS;
   - ни одно из трёх не задано → FAIL («No machine-readable verdict/status/result…»);
   - заданы, но не распознаны → FAIL.
3. Наборы токенов **не изменяются**.
4. Парсер не бросает исключений ни на каком вводе (битый YAML, пустой файл, frontmatter-не-mapping)
   → всегда `(False, reason)`.
5. Сигнатура `check_tests_passed`, имя гейта и реестр `QG_CHECKS` **не меняются** — снапшот
   `tests/test_qg_registry_snapshot.py` остаётся зелёным.

### Альтернативы (отклонены)

- **Править промпт тестера** (`verdict:` вместо `result:`) — отклонено: контракт уже задокументирован
  для тестера как `result:`; единичная правка гейта дешевле и не требует переучивать агента, плюс
  ломала бы совместимость со старыми отчётами, где встречается `verdict:`/`status:`.
- **Глобальный ADR в `docs/architecture/adr/`** — не требуется: изменение не добавляет гейт/стадию/
  компонент и не меняет топологию; это приведение парсинга существующего гейта к контракту. Канон
  гейтов в README обновляется точечно.

## Последствия

- **Плюс:** корректные отчёты `result: PASS` проходят гейт; `result: FAIL` надёжно откатывает.
  Петля `testing ↔ development` устранена для всех проектов общего инстанса.
- **Плюс:** полная обратная совместимость — отчёты только с `verdict:`/`status:` работают как
  раньше; существующие тесты `TestCheckTestsPassed` зелёные без правок (кроме обновления reason-текста
  «…verdict/status…» → «…verdict/status/result…»).
- **Минус/ограничение:** число распознаваемых имён поля растёт до трёх — формально шире поверхность
  «случайного PASS». Митигируется тем, что negative-токен авторитетен и читается только frontmatter.
- **SHARED-риск:** изменение затрагивает enduro-trails наравне с orchestrator. Регресс по наборам
  токенов недопустим → они заморожены; покрытие — `04-test-plan.yaml` (AC-04/AC-05).
- **Self-hosting:** деплой строго через `deploy-staging` (8501); прод-контейнер `orchestrator`
  (8500) не перезапускать в рамках разработки/тестинга.

## Связи

- BRD/ТЗ: `docs/work-items/ORCH-047/01-brd.md`, `02-trz.md`.
- Канон гейтов и вердикт-парсер: `docs/architecture/README.md`.
- Промпт-производитель: `.openclaw/agents/tester.md` (`result: PASS|FAIL`).
- adr-0003 (staging-гейт) — обязательная страховка перед прод-деплоем self.
