# SPEC-REVIEW-230-r2 - **Issue:** https://github.com/Matysh/houseplan-card/issues/230 - **ТЗ:** `docs/specs/230-hatch-density-normalization.md` - **Ветка / SHA:** `issue/230-hatch-density-normalization` @ `44a35fd` - **Этап:** spec (PROCESS.md §2.4) · трек: `small` (метка на issue) - **Заход:** r2 · блокирующих циклов израсходовано 1/4 до этого вердикта - **Ревьюер:** Claude, роль «ревьюер ТЗ» - **Предыдущий раунд:** `SPEC-REVIEW-230-r1.md`, вердикт жёлтый на SHA `2396044` (комментарий issue от 2026-08-21T11:46:32Z). SHA в вердикте r1 не был назван явно в тексте — восстановлен по коммиту `docs: review document for #230` (`6872dc4`, родитель `2396044`) и подтверждён телом комментария. ## Скоуп раунда Разбор по дельте (PROCESS.md §2.10): сравнивается `2396044` (SHA r1) → `44a35fd` (текущий HEAD). Дельта — правка того же файла ТЗ, `git diff 2396044..44a35fd -- docs/specs/230-hatch-density-normalization.md`, 42 вставки / 12 удалений, локализована в §11 п.2, §12 (AC10, новые AC11/AC12), §13, §14, §15. Код не менялся (реализации всё ещё нет, этап S4). Дельта локальна и продуктовое решение владельца не меняет — полный повторный разбор всего документа не требуется; §1–§10, §16, §17 и AC1–AC9 наследуются из r1 (см. раздел «Унаследовано»). Отдельно проверено, не создала ли правка новое противоречие с неизменённой частью документа — нашла (см. Low-1 ниже). ## Как проверялось 1. Найден вердикт r1 и SHA, на котором он получен (см. выше). 2. Получены `git diff 2396044..44a35fd` и текущий полный текст файла ТЗ. 3. По каждой находке r1 (High-1, Medium-1, Low-1) построчно сверено, что именно в дельте её закрывает — раздел «Закрытие раунда r1» ниже. 4. Пересчитана таблица шагов в §11 п.2 самостоятельно, по той же формуле, что цитирует ТЗ: `inv = max(0.4, 1/max(zoom,0.4))`, шаг-сейчас = `8·inv`. - `zoom 0.4`: `inv = max(0.4, 1/0.4) = 2.5` → `8·2.5 = 20.0` — совпадает со строкой ТЗ. - `zoom 2.5`: `inv = max(0.4, 1/2.5) = max(0.4,0.4) = 0.4` → `8·0.4 = 3.2` — совпадает. - «шаг после» для обеих сцен — `wallHatchStepUnits(5) = 8·(5/5) = 8` — совпадает с заявленным. 5. Прочитан текущий AC10–AC12 и сверен с §13 (план тестов) и §14 (мутанты): каждый новый AC покрыт ровно одним пунктом плана тестов и как минимум одним мутантом-гвардом; осиротевших AC нет, осиротевших мутантов нет. `wallHatchStepUnits(25) = 8·(5/25) = 1.6`, что отличимо от старой константы `8` в `space-render.ts` — AC12 способен упасть, если правку второго рендерера не сделают. 6. Прочитано тело issue #230 и все три комментария (r1-заявка автора, вердикт r1, ответ автора на r2) — расчёт таблицы в комментарии автора совпадает с расчётом в файле ТЗ и с пересчётом в п.4. 7. Проверено, не противоречит ли скорректированный §11/AC10–12 неизменённым разделам документа. Нашлось: §5 («Цели»), второй пункт — см. Low-1. 8. Код, гейты, автотесты не запускались — реализации нет, это ожидаемо на этапе ревью ТЗ (как и в r1). ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | **High-1** — риск-анализ и AC10 построены на неверной посылке «фикстуры не меняют зум» | §11 п.2 переписан: явно называет обе зумовые сцены, приводит таблицу «шаг сейчас/после», признаёт изменение ожидаемым следствием §4.2. AC10 сужен до «расходится ровно на двух сценах, третья — провал». Добавлен AC11 на осознанное переснятие через `golden:accept -- --reviewed` | `docs/specs/230-hatch-density-normalization.md:189-207` (§11 п.2), `:244-246` (AC10), `:248-250` (AC11) | | **Medium-1** — контракт «оба рендерера» не имел собственного AC для `space-render.ts` | Добавлен AC12: `space-render.ts` при `cell_cm: 25` строит `width`/`height` по `wallHatchStepUnits(25)`, без `scale`. Смок `demo/smoke_wall_hatch_density.mjs` расширен на статический рендерер. Добавлен мутант `hatch-static-renderer-untouched` | `:252-256` (AC12), `:261-263` (§13), `:276` (§14) | | **Low-1** — `WALL-THICKNESS.md` не входил в release-артефакты | §15 явно называет `docs/WALL-THICKNESS.md` каноническим документом подсистемы и требует правки в нём в том же коммите | `:282-285` (§15) | Все три находки r1 закрыты по существу, не декларативно: формулы в новой таблице §11 п.2 пересчитаны независимо (см. «Как проверялось», п.4) и совпадают; AC12 действительно различим от старого поведения численно (8 vs 1.6), то есть способен упасть, если правку в `space-render.ts` не внесут; AC10 в новой формулировке — falsifiable в обе стороны (ловит и «третью» разошедшуюся сцену, и случай, когда ожидаемые две сцены не разошлись). ## Унаследовано из r1 Принято без повторной проверки, документ `SPEC-REVIEW-230-r1.md` на SHA `2396044`; дельта r1→r2 их не касается: - §1–2 продуктовая рамка (сценарий/персона, видимое изменение до/после) — отвечает на оба обязательных вопроса, персона из `docs/SCOPE.md` J4/J6. - §3 подтверждённая причина и вывод «формула issue неверна, верный множитель — `5/cell_cm`» — проверено алгебраическим пересчётом в r1. - §4 продуктовые решения владельца — зафиксированы, не менялись. - §6–7 Scope / Не входит в задачу — включая проверку по коду, что `space-card.ts` не третий независимый потребитель паттерна. - §8 Контракт (шаг, применение, пределы, защита от каши) и алгебраическое доказательство AC2/AC3 (независимость числа полос от `cell_cm`, пропорциональность толщине). - §9–10 данные/i18n/perf. - AC1, AC4–AC9 — однозначны, проверяемы, указывают способ доказательства; не затронуты дельтой. - §16 Откат, §17 Принятые предположения. - Обязательные разделы §7.1 присутствуют по существу (полный перечень — в r1). - Мутационный гейт для мутантов, унаследованных из r1 (`hatch-step-ignores-cell-cm`, `hatch-step-inverted`, `hatch-step-unclamped`, `hatch-stroke-not-scaled`, `hatch-zoom-compensation-back`, `hatch-density-solid-threshold-off`). ## Находки ### Low-1 — §5 «Цели» не обновлён вслед за исправлением §11/AC10–11 и противоречит им **Файл:** `docs/specs/230-hatch-density-normalization.md:92-93` (§5, второй пункт). **Формулировка ТЗ:** «При `cell_cm: 5` вид не меняется — ни на карте, ни в статическом рендерере, ни в golden-эталонах.» **Проблема.** Это безусловное утверждение написано до правки r2 и не обновлено вместе с ней. После правки §11 п.2 и AC10/AC11 прямо утверждают обратное для двух конкретных golden-эталонов: `large-house-zoom-040-dark` и `large-house-zoom-250-dark` — обе сцены сняты на фикстуре `cell_cm: 5` (`perf-floor-1`), и обе, по признанию самого документа, «изменятся — и это прямое следствие решения владельца §4.2». Причина расхождения — не в `cell_cm`-зависимой части правки (при `cell_cm: 5` шаг действительно не меняется, `8 → 8`), а во втором, отдельном решении владельца — удалении зумовой компенсации. §5 говорит только про `cell_cm: 5` и не делает оговорки про зум, поэтому как написано противоречит §11/AC10/AC11 в одном и том же документе для того же значения `cell_cm`. **Почему не блокирует.** ACs 10, 11 и 12 — обязывающая, точная и самодостаточная часть контракта; они не ссылаются на §5 и не зависят от его формулировки. Разработчик, который реализует задачу по AC, получит верное поведение независимо от того, поправлена ли фраза в §5. Риск — не в неверной реализации, а в том, что читатель, остановившийся на §5, сделает неверный вывод о golden без учёта §11 — то есть это дефект качества текста, не дефект контракта. **Что нужно.** Одна фраза-оговорка в §5, например: «...кроме двух golden-сцен, снятых при зуме ≠ 1 (`large-house-zoom-040-dark`, `large-house-zoom-250-dark`) — см. §11 п.2» — либо сузить формулировку до «шаг паттерна не меняется» (что действительно верно при `cell_cm: 5` безусловно), не упоминая golden-эталоны в этом пункте вовсе. **Решение ревьюера.** Low, не блокирует и не влияет на проверяемость AC10–AC12 — снимаю с рекомендацией поправить одной фразой при следующей правке файла (не обязательно отдельным циклом). ## Что проверено и корректно - Все три находки r1 закрыты по существу (см. таблицу выше), включая пересчёт числовых значений, а не просто присутствие текста. - Новый AC10 falsifiable в обе стороны: провалится и если разойдётся третья сцена, и если ожидаемые две не разойдутся вовсе (что покрывает ранее открытый в r1 вопрос — попадает ли `perf-floor-1` в режим `solid` на этих зумах; правильный исход теперь виден по факту прогона, а не предсказывается заранее). - AC11 и AC12 указывают способ доказательства (процесс `golden:accept -- reviewed` с записью в отчёте; браузерный смок) и не оставляют пространства для интерпретации. - §13/§14 согласованы с новыми AC: ни один AC не остался без пункта плана тестов, ни один новый мутант не остался без AC-обоснования. - §15 теперь называет все три необходимые точки: оба CHANGELOG, `USER-GUIDE.ru.md`, канонический `WALL-THICKNESS.md`. ## Продуктовые вопросы владельцу Не требуются. Единственная находка раунда (Low-1) — текстовая несогласованность внутри документа, решается редактурой, не требует нового продуктового решения: владелец уже высказался (§4 п.2, комментарий от 2026-08-21), автору дельты нужно распространить эту формулировку на §5. ## Чего не проверял - Реализацию — её не существует на этом этапе (не изменилось с r1). - Полный построчный обзор `demo/golden/matrix.mjs` (~40 сцен) на предмет третьих сцен с `zoom ≠ 1` вне `large`/`perf-floor-1` — не требуется для вывода этого раунда: AC10 в текущей формулировке сам ловит любую такую сцену как провал критерия на этапе код-ревью/CI, независимо от того, нашёл ли её ревьюер ТЗ заранее. - Действительно ли `perf-floor-1` на этих двух зумах уже сегодня в режиме `solid` — не пересчитывалось; не влияет на вывод, так как новая формулировка AC10 корректно обрабатывает оба исхода (см. «Что проверено и корректно»). - Гейты (`tsc`, `npm test`, `npm run build`, смоки, `golden:verify`) не прогонялись — стадия spec, кода нет; это симметрично r1 и ожидаемо. ## Вердикт Все блокирующие и заявленные-в-скоупе находки r1 (High-1, Medium-1, Low-1) закрыты по существу и проверены пересчётом, а не на слово автора. Единственная находка этого раунда — Low, текстовая несогласованность §5 с исправленным §11/AC10–11, не влияющая на проверяемость контракта; снимается решением ревьюера с рекомендацией автору. High: 0, Medium: 0. Итог — зелёный: ТЗ готово к статусу «Готово к разработке» (PROCESS.md §2.5).