6.6 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-095 | analysis | analyst | ready-for-review | 2026-06-09 | claude-opus-4-8 |
03 — Критерии приёмки (Acceptance Criteria): ORCH-095 — HTML-инъекция «<1м» в render_task_tracker
Work Item: ORCH-095 · Repo: orchestrator · Стадия: analysis
Формат: каждый критерий имеет PASS (что должно быть истинно для приёмки) и FAIL (что считается провалом). Любой машинный/ручной reviewer проверяет их буквально по файлам репозитория.
AC-1 — Стадия < 1 мин не ломает парсер Telegram
Условие: render_task_tracker для задачи, у которой хотя бы одна стадия длилась < 60 с,
выдаёт текст, безопасный для parse_mode=HTML (нет неэкранированного < в данных длительности).
- PASS: В выходном тексте подстрока длительности «меньше минуты» представлена как
<1м(или переформулированный безопасный вид~0м/< 1 минбез сырого<);editMessageTextс этим текстом не вернул бы400 can't parse entities: Unsupported start tag "1м". Юнит-тест на_fmt_minutes(30)/render_task_tracker(...)подтверждает отсутствие сырого<от длительности. - FAIL: Текст содержит сырой
<1м(или иной литерал<+нецифра) из данных длительности; тест на парсинг/наличие сырого<падает.
AC-2 — Все динамические поля карточки HTML-безопасны (юнит)
Условие: Существует юнит-тест, проверяющий, что каждое подставляемое данные-поле
render_task_tracker экранировано: длительность, токены, стоимость ($), заголовок с
спецсимволами < > &, статус-лейбл, имя модели/эффорт.
- PASS: Тест рендерит карточку с заголовком, содержащим
<,>,&(напр."A <b>x</b> & <1"), и стадией < 1 мин; ассертит, что эти спецсимволы из ДАННЫХ присутствуют в выводе только в экранированном виде (</>/&) и НЕ как сырые теги; одновременно нет двойного экранирования (&lt;). - FAIL: Тест отсутствует, либо любое из перечисленных данных-полей попадает в текст без экранирования, либо обнаруживается двойное экранирование.
AC-3 — Регресс намеренной разметки (ссылка-номер, форматирование)
Условие: После фикса намеренная HTML-разметка карточки продолжает рендериться валидной и кликабельной.
- PASS: Кликабельный номер задачи (
<a href="…">ORCH-095</a>отplane_issue_link) присутствует в выводе как валидный незаэкранированный<a>-тег; строки🔗 PR #n/📦(_done_link) и любое форматирование-обёртка рендерятся; существующие тестыtest_tracker_issue_link.py/test_notify_issue_links.py/test_telegram_tracker.pyзелёные. Двойного экранирования href/label нет. - FAIL: Номер задачи перестал быть кликабельным (
<a>заэкранирован в<a>), либо любой регресс-тест разметки красный.
AC-4 — Застрявшая карточка возобновляет обновления
Условие: Карточка, ранее застрявшая на 400 can't parse entities (класс ORCH-093), после
фикса снова обновляется.
- PASS: На следующем переходе стадии текст рендера больше не содержит небезопасной
подстроки →
editMessageTextпроходит (200); ИЛИ (если выбрана стратегия FR-4-опц.) перманентный parse-фейл классифицируется как повод переотправить свежую карточку, иupdate_task_trackerотправляет новую. Поведение покрыто тестом (рендер валиден → edit-путь не возвращаетEDIT_FAILEDиз-за parse-ошибки). - FAIL: После фикса карточка с прежним содержимым по-прежнему даёт
EDIT_FAILEDи не обновляется/не переотправляется; либо защита от дублей (ORCH-087) сломана — транзиентный фейл теперь плодит дубликаты карточек.
AC-5 — never-raise, зелёный регресс, CHANGELOG
Условие: Контракт надёжности и гигиена изменения сохранены.
- PASS:
render_task_tracker/update_task_tracker/edit_telegramне выбрасывают исключение наружу при любом входе (включая «битый» заголовок/None);pytest tests/ -qполностью зелёный; вCHANGELOG.mdесть запись о фиксе ORCH-095;STAGE_TRANSITIONS/QG_CHECKS/check_*/схема БД не изменены (diff их не трогает). - FAIL: Любой тест в
tests/красный; обнаружено непойманное исключение в пути рендера; тронуты машина стадий/гейты/схема БД; нет записи вCHANGELOG.md.
Сводная матрица AC ↔ FR/BR
| AC | Покрывает |
|---|---|
| AC-1 | BR-1, BR-3 / FR-1 |
| AC-2 | BR-2 / FR-2 |
| AC-3 | BR-4 / FR-3 |
| AC-4 | BR-5 / FR-4 |
| AC-5 | NFR-1, NFR-2 / FR-5 |