---
verdict: APPROVED
work_item: ORCH-095
stage: review
author_agent: reviewer
status: approved
created_at: 2026-06-10
model_used: claude-opus-4-8
type: review
work_item_id: ORCH-095
version: 1
---

# Review ORCH-095

## Summary

Фикс HTML-инъекции `<1м` в live-карточке трекера. Точечное, аддитивное, never-raise изменение
в индикативном слое (`src/notifications.py`): новый модуль-локальный хелпер `_esc(x) =
html.escape(str(x))` оборачивает каждый **data**-слот (`dur`/`_fmt_minutes`/`_capped_review_str`,
`status_label`, `model`, `effort`, токены/стоимость) ровно один раз на границе рендера
(`render_task_tracker`/`_stage_line`); **markup**-слоты (`num_html`/`link_for`/`_done_link`/
уже-экранированный `esc_title`) не трогаются. Источники (`_fmt_minutes`, `src/usage.py`) остаются
HTML-агностичными.

Проверены все четыре оси. Реализация соответствует ТЗ (FR-1…FR-5) и ADR-001 (D1…D6) буквально;
все 5 AC выполнены. `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / схема БД / транспорт нотификаций
— не тронуты (`git diff` пуст по `src/stages.py`, `src/qg/`, `src/stage_engine.py`, `src/db.py`).
Полный регресс `pytest tests/ -q` зелёный (**1437 passed**), новый `tests/test_tracker_html_escape.py`
(TC-01…TC-11) — зелёный.

**Соответствие осям:**
1. **ТЗ / AC** — FR-1/AC-1 (`<1м`→`&lt;1м` на границе, источник не меняется), FR-2/AC-2 (все
   D-слоты экранированы — сверено по коду стр. 471/517-523/529/594/607/614-615/620-621/629),
   FR-3/AC-3 (M-слоты не экранированы, двойного экранирования нет), FR-4/AC-4 (авто-восстановление
   следующим рендером, без рискованной переклассификации `EDIT_FAILED` — корректно, защищает
   инвариант ORCH-087), FR-5/AC-5 (never-raise + зелёный регресс + CHANGELOG). ✓
2. **ADR + трассировка** — реализация 1:1 с ADR-001 (escape на границе рендера, не в источнике;
   M-слоты неприкосновенны). Блоки с маркерами ORCH-042/067/087/091 правлены аддитивно: код лишь
   оборачивает уже вычисленные D-значения в `_esc`, не меняя состав строк/порядок/логику подавления
   и суммирования — инварианты сохранены по построению. Сквозной `adr-NNNN` обоснованно не заведён
   (локальный indication-only фикс). ✓
3. **Качество кода** — `_esc` с docstring и never-raise; тесты содержательные (11 TC покрывают
   каждый AC, включая регресс кликабельного `<a href>`-номера, `_done_link` и анти-дубль ORCH-087
   на транзиентном фейле). ✓
4. **Документация** — обновлены в том же PR: `CHANGELOG.md`, `docs/architecture/README.md`
   (блок Notifications/Live-tracker), `docs/architecture/internals.md` §7, ADR-001. ✓

## Findings

### P0 — Blocker
- Нет.

### P1 — Must fix
- Нет.

### P2 — Should fix
- Нет.

### P3 — Nice-to-have
- [ ] `attempt` (`f"… попытка {attempt} …"`, ~стр. 572) и статичные лейблы стадий
  (`_TRACKER_STAGES`/`_BRD_LABEL`) не проходят через `_esc`. ADR-001 D1 упоминает их в категории D
  «ради единообразного инварианта», но `attempt` — всегда `int` (`len(agent_runs)`), а лейблы —
  статичные константы → фактической поверхности инъекции нет, расхождение безвредно. Не блокирует;
  можно унифицировать при будущем касании блока (оставляю на усмотрение, не требую правки).

## Документация

**Обновлена полностью в том же PR — требование правила 6 (CLAUDE.md) выполнено:**
- `CHANGELOG.md` — детальная запись ORCH-095 (механизм бага, D1–D5, восстановление, трассировка, тесты).
- `docs/architecture/README.md` — компонент «Notifications / Live-tracker» дополнен абзацем ORCH-095
  (data/markup-слоты, инвариант экранирования на границе, ссылка на ADR).
- `docs/architecture/internals.md` §7 — новая подсекция «HTML-безопасность данных карточки (ORCH-095)».
- `docs/work-items/ORCH-095/06-adr/ADR-001-html-safe-card-data-render.md` — архитектурное обоснование
  (выбор точки экранирования, альтернативы, последствия).

Пункт `README.md` «Известные ограничения» данным фиксом не закрывается (баг корректности, не числился
в витрине ограничений) → обновление обзорной витрины (ORCH-079) не требуется.

**Вывод:** `src/` изменён — документация обновлена синхронно. P0 «документация не обновлена» не
применяется.
