# 03-Acceptance Criteria — ORCH-017

Work Item: **ORCH-017** · Repo: `orchestrator`
Каждый критерий формулирует условие PASS/FAIL. Источник — 01-brd.md / 02-trz.md.

## AC-1 — Ссылка на BRD присутствует в уведомлении
- **PASS:** Текст, сформированный `notify_approve_requested`, содержит кликабельную ссылку
  на `docs/work-items/<WI>/01-brd.md` вида
  `{gitea_public_url|gitea_url}/{owner}/{repo}/src/branch/{branch}/docs/work-items/{WI}/01-brd.md`.
- **FAIL:** Ссылки на BRD нет, либо она ведёт не на `01-brd.md`/не на нужный WI.

## AC-2 — Ссылка на Plane-issue присутствует в уведомлении
- **PASS:** Тот же текст содержит кликабельную ссылку на issue в Plane, построенную на
  внешнем web-URL Plane + workspace + project + `plane_issue_id` (или согласованный браузер-формат).
- **FAIL:** Ссылки на issue нет, либо она указывает на внутренний `localhost`/неверную issue.

## AC-3 — Базовый URL берётся из внешних настроек
- **PASS:** BRD-ссылка использует `gitea_public_url`, при его пустоте — `gitea_url`; Plane-ссылка
  использует `plane_web_url` (env `ORCH_PLANE_WEB_URL`), при пустоте — `plane_api_url`.
- **FAIL:** Захардкожен хост, либо ссылка нерабочая снаружи деплой-хоста.

## AC-4 — Существующий призыв сохранён
- **PASS:** Текст по-прежнему содержит призыв перевести задачу в статус Approved (смысл строки
  «Переведите задачу в статус Approved … для продолжения» сохранён).
- **FAIL:** Призыв удалён/искажён.

## AC-5 — Одно отдельное пингующее сообщение, без дублей
- **PASS:** `notify_approve_requested` отправляет ровно одно сообщение через `send_telegram`
  (пингующее, не silent). Живой трекер (`update_task_tracker`) обновляется как раньше и не
  дублируется новым сообщением.
- **FAIL:** Появляется второе/дубль-сообщение, либо трекер шлётся повторно как новое сообщение.

## AC-6 — Graceful degradation (никогда не ронять оркестратор)
- **PASS:** При отсутствии `branch` / `plane_issue_id` / незаданном Plane web-URL функция НЕ
  бросает исключение: уведомление уходит с доступными ссылками (или без отсутствующей), орк жив.
- **FAIL:** Отсутствие данных приводит к исключению/падению потока уведомлений.

## AC-7 — HTML-безопасность
- **PASS:** Сохранён `parse_mode="HTML"`; динамические подписи экранируются (`html.escape`),
  URL валиден и не ломает разметку сообщения.
- **FAIL:** Сообщение приходит с битой HTML-разметкой или с неэкранированным пользовательским текстом.

## AC-8 — Логика апрува не затронута
- **PASS:** `:approved:`-handler, `check_analysis_approved`, переходы стадий и реестр `QG_CHECKS`
  без изменений; правка касается только текста/формата уведомления.
- **FAIL:** Изменена логика гейта/перехода стадий.

## AC-9 — Документация обновлена в том же PR
- **PASS:** Обновлены `CHANGELOG.md` и `.env.example` (новая `ORCH_PLANE_WEB_URL`); если добавлена
  настройка — отражено в env-карте (`CLAUDE.md`/`docs/operations/INFRA.md`); заведён ADR на
  выбранный формат. (Reviewer проверяет доку → нет обновления = REQUEST_CHANGES.)
- **FAIL:** Код изменён, документация — нет.

## AC-10 — Тесты зелёные
- **PASS:** Новые/затронутые тесты (`tests/test_notify_approve_links.py` и существующие
  `tests/test_telegram_tracker.py`, `tests/test_notify_done_regression.py`) проходят; `pytest tests/ -q` зелёный.
- **FAIL:** Любой связанный тест падает.

---
### Зависит от решений Owner (open questions 01-brd §8)
- Если выбран вариант **inline-кнопок** — AC-1/AC-2 считаются выполненными при наличии кнопок
  «📄 Открыть BRD» / «✅ К задаче в Plane» с теми же URL; дополнительно AC: обратная совместимость
  `send_telegram` (старые вызовы без `reply_markup` работают).
- Если охват расширен до **всех точек решения** — AC-1/AC-2 проверяются для каждой такой точки.
