# SPEC-REVIEW — issue #209 · заход r1 - Этап: spec (PROCESS.md §2.4) - Артефакт ТЗ: `docs/specs/209-vacuum-trail-smoothing.md` (SHA `43516cc1`, ветка `issue/209-vacuum-trail-smoothing`) - Вердикт: **зелёный** - Блокирующих циклов израсходовано: 0 из 4 ## Скоуп ревью Первый раунд, дельта не применяется (§2.9 — правило дельты действует только для r2+). Разобрано полностью: 1. Тело issue #209 и все 3 комментария (анализ Codex, два решения владельца, финальная передача на ревью). 2. Полный текст `docs/specs/209-vacuum-trail-smoothing.md`. 3. `docs/SCOPE.md` — соответствие Core user jobs. 4. `PROCESS.md` §2.4, §7.1 — обязательные разделы ТЗ и порог продуктовых вопросов. 5. `docs/VACUUM.md`, `docs/USER-GUIDE.ru.md` (раздел «Позиция и путь»), `docs/USER-GUIDE.md` (раздел «Robot vacuums»), `docs/ARCHITECTURE.md` — как канонические источники терминологии и текущего контракта. 6. Исходный код `src/houseplan-card.ts` (`_renderVacuums`, ~11819–11905) и `src/vacuum.ts` (`normalizeVacPath`, `resolveCurrentVacPath`, `trimVacPathTarget`, `VAC_PATH_MAX_SEGMENTS/POINTS`, `TRAIL_MAX`) и `custom_components/houseplan/trails.py` (`TRAIL_CAP`) — сверка каждого факта, на который ссылается ТЗ. 7. `docs/TESTING.md` — существование `scripts/mutation-gate.mjs` и практики мутационных гвардов, на которые ссылается план автотестов ТЗ. ## Как проверялось Продуктовая часть (docs/SCOPE.md, персона, сценарий) читалась первой и отдельно от технической, как требует §7.1. Технические утверждения ТЗ (номера строк, имена функций, константы лимитов, порядок affine → build → `_scenePoint()`) верифицированы построчным чтением текущего кода, а не доверием тексту ТЗ. Отдельно перепроверено математическое обоснование предела 17,5 см в §6.2 (см. ниже) — это не тривиальное утверждение, и ложное доказательство было бы поводом для High. ## Находки Находок нет — ни High, ни Medium, ни Low. Разобранные кандидаты, которые не подтвердились как дефект: - **Отсутствие docs/USER-GUIDE.md (EN) в списке release-артефактов §13.** Проверено: `docs/USER-GUIDE.md` раздел «16. Robot vacuums» не описывает форму линии следа (M/L vs кривая) — он делегирует детали в `VACUUM.md` одной фразой. `VACUUM.md` (канонический для этого контракта, EN) в списке §13 присутствует. Правки нечего вносить в `USER-GUIDE.md`, значит его отсутствие в перечне не является пропуском. - **Утверждение issue-body про поверхность «статический рендер».** Тело issue перечисляет «View, киоск, статический рендер» как поверхности показа следа. Проверено: `src/space-card.ts` и `src/space-render.ts` (инертная `houseplan-space-card`) не содержат ни одного упоминания vacuum/trail — сейчас след там не рисуется. Само ТЗ, однако, этой ошибки не наследует: §1 описывает только View/киоск/редакторы, а §4 Scope формулирует охват как «поверхности, где след уже рисуется» — не перечисляя конкретный список. §5 Non-scope прямо запрещает «добавление следа ... на поверхности, которые его сейчас не имеют». Формулировка ТЗ самодостаточно корректна независимо от неточности в исходном issue-анализе. - **Математика предела в §6.2.** Проверено: для точки на `conv{P, B, Q}` расстояние до `B` ограничено `r = min(17.5см, |AB|/2, |BC|/2)` в силу выпуклости нормы (максимум выпуклой функции на симплексе достигается в вершине); P и Q лежат на исходных отрезках, поэтому граница до исходной ломаной действительно ≤ r ≤ 17,5 см. Отдельно проверено недопущение наложения соседних скруглений: раз `r_B2 ≤ |s2|/2` и `r_B3 ≤ |s2|/2` для общего отрезка `s2`, точка `Q` вершины `B2` не может оказаться дальше по отрезку, чем точка `P` вершины `B3` — соседние скругления не меняются местами. Довод в тексте ТЗ корректен, не голословен. - **Эпсилон разворота ~180°.** Помечен явно как техническое решение автора реализации, фиксируемое unit-тестом (§14 п.3), а не выдан за факт — это ровно то отличие «предположения» от «незамеченной догадки», которое требует проверять инструкция ревью. ## Что проверено и корректно - **Персона/сценарий/до-после** — первые два раздела (§7.1) заполнены корректно и без терминов реализации; соответствуют J1 (`docs/SCOPE.md`: «Show the whole home and what's happening right now»). - **Продуктовые вопросы владельцу** заданы по существу (граница сглаживания, вкл/выкл, previous run) — все три ровно того типа, что разрешён §7.1 («что видно», «объём видимых изменений»), без технических вопросов, вынесенных наружу. Ответы получены и зафиксированы в issue до отправки ТЗ на ревью — блокировка снята в срок. - **Все обязательные разделы §7.1 присутствуют**: сценарий, до/после, проблема, scope/non-scope, контракт поведения, UX/a11y/touch/i18n, модель данных и совместимость, AC1–AC11 с методом доказательства для каждого, план автотестов, риски, release-артефакты, откат. - **AC однозначны и проверяемы**: каждый сформулирован как измеримое утверждение (endpoints совпадают, отклонение ≤17,5 см, число команд ≤2N, таблица режимов не меняется и т.д.), не «сделать нормально»/«работает как ожидается». - **Факты о коде подтверждены построчно**: `VAC_PATH_MAX_SEGMENTS = 64`, `VAC_PATH_MAX_POINTS = 4000` (`src/vacuum.ts:14-15`), `TRAIL_MAX = 600` (`src/vacuum.ts:477`), `TRAIL_CAP = 2000` (`custom_components/houseplan/trails.py:27`), `resolveCurrentVacPath`, `trimVacPathTarget`, `normalizeVacPath` (`src/vacuum.ts:169,201,222`) — все совпадают с ТЗ. Текущий рендер (`_renderVacuums`, `src/houseplan-card.ts:11819-11905`) действительно строит current как единый `path` с `M`/`L` по подпутям и previous — отдельным `polyline` (`:11855`), обе ветки применяют affine и затем `_scenePoint()` на каждую точку — план ТЗ перенести этот порядок на «affine всего подпути → builder → проекция» архитектурно согласован с существующим кодом, не выдумка. `src/vacuum.ts` уже существует и уже владеет `normalizeVacPath`/ `resolveCurrentVacPath`/`trimVacPathTarget` — ровно тот модуль, куда ТЗ просит добавить pure builder (§9), это не новый файл и не смена владения. - **Non-scope согласован со scope**: явно исключены изменения хранения, лимитов, arbitration, trail modes, добавление следа туда, где его нет — ни один пункт scope не противоречит этому списку. - **Терминология согласована** с `docs/USER-GUIDE.ru.md` («Никогда/Во время уборки/Всегда», «текущий путь»/«последний завершённый путь») — таблица §6.4 ТЗ не меняет и не путает этот контракт. - **i18n/touch/миграция/откат** — по каждому пункту явное «нет изменений» с обоснованием, а не молчание. - **Track = полный** обоснован верно: видимый контракт подачи меняется, задействовано несколько поверхностей и golden-эталоны — критерий `small` не проходит. - **Продуктовая оценка (польза/сложность/риск)** реалистична и не противоречит `docs/SCOPE.md`: ценность признана косметической (4/10), что корректно для P3-полировки, не блокирующей и не требующей ускоренного трека. ## Чего не проверял - Реализацию — её не существует, стадия spec. - `npx tsc --noEmit` / `npm test` / `npm run build` — не прогонял: гейт не применим к этапу ревью ТЗ (нет кода для компиляции), а не пропущен по экономии. - Golden/смоки/инварианты модели — не прогонял: задача геометрию модели (рёбра, `layout`, `marker.space`, `open_spans`) не трогает (явный non-scope), это этап spec, инструментальные гейты этого раунда не применимы. - Не проверял, действительно ли выбранный владельцем способ (quadratic Bézier по хордам) — лучший вариант из трёх предложенных в issue (Catmull-Rom/Chaikin/Безье): ТЗ прямо отдаёт выбор техники автору реализации, зафиксировав только измеримую границу (17,5 см) и семантику (endpoints, gaps, no bridge) — это корректная граница ответственности spec-ревью, а не пробел. ## Итог ТЗ полное, однозначное, без выданных за факт догадок, предметно верифицировано по коду и документам. Продуктовые вопросы владельцу заданы и закрыты до отправки на ревью. Готово к статусу «Готово к разработке».