# SPEC-REVIEW-82-r1 — Плавное масштабирование плана (issue #82) - **Issue:** https://github.com/Matysh/houseplan-card/issues/82 - **Артефакт ТЗ:** `docs/specs/082-smooth-zoom.md`, коммит `141d79a923f5703eb3beceebdabbd5508821f0fe` - **Заход:** r1 · блокирующих циклов израсходовано 0 из 4 (полный трек, лимит 4) - **Ревьюер:** Claude, роль «Ревьюер ТЗ» (PROCESS.md §2.4, §6) ## Скоуп ревью Задача полного трека (нет метки `small`), поэтому ТЗ живёт файлом `docs/specs/082-smooth-zoom.md`, а не в теле issue. Это первый заход ревью на этом ТЗ (r1), поэтому §2.10 (разбор по дельте) не применяется — разбор полный. Диф на пути к текущему SHA: ``` git diff 453c3e3d..141d79a9 -- docs/specs/082-smooth-zoom.md # 352+/232- строк, актуализация ТЗ ``` Диф не трогает `src/**`, `custom_components/**`, `test/**`, `demo/**` — только `docs/specs/082-smooth-zoom.md`. Класс C (документация), гейты типа `typecheck`/`test`/`build`/`check-docs` к этому диффу неприменимы: менять нечего, кода не существует. См. «Чего не проверял». ## Как проверялось 1. Прочитан `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` (§1, §2.3–2.10, §4, §5, §7, §12). 2. Прочитано тело issue #82 и все три комментария (аналитика 2026-08-14, заведение ТЗ 2026-08-15, актуализация 2026-08-30). 3. Прочитан полный текущий текст `docs/specs/082-smooth-zoom.md` (24 раздела). 4. Прочитан канонический `docs/CANVAS.md` (§5 Zoom and pan, «View/editor camera handoff», §4 content frame) и `docs/TOUCH-SUPPORT.md` (raздел про View/editor visual transition, deliberate degradation rule). 5. Проверены фактические утверждения ТЗ о текущем коде против `src/houseplan-card.ts` (`_zoomAt`, `_onWheel`, `_stepZoom`, `_resetZoom`, `_fitAll`, `_fitFar`, `_baseVb`, `_clampView`, `ZOOM_MAX`/`ZOOM_MIN`, `LS_ZOOM`, `_saveZoom`, lazy `import('./houseplan-editor-runtime')`) и `src/mode-transition.ts` (`ModeTransitionController`, `interpolateModeVisualState` — уже интерполирует `pixelsPerUnit` в log-space и center линейно тем же easing, что подтверждает реализуемость предложенного §9 механизма). 6. Проверена терминология кнопок/подсказок против `src/i18n/ru.json` (`title.zoom_fit` = «Вписать всё», `canvas.show_far` = «Показать») — совпадает с матрицей §6 ТЗ. 7. Проверено существование смоков, на которые ссылается план тестов (`demo/smoke_mode_transition.mjs`, `demo/smoke_visual_continuity.mjs`, `demo/smoke_zoom_out.mjs`, `demo/smoke_pan_any_zoom.mjs`, `demo/screencast_visual_continuity.mjs`, `npm run continuity:screencast` в `docs/TESTING.md`) — не изобретены, это существующий инструментарий #73. 8. Проверены обязательные разделы ТЗ по PROCESS.md §7.1 построчно (список ниже). ## Проверка обязательных разделов (§7.1) | Раздел §7.1 | Есть в ТЗ | Где | | --- | --- | --- | | Сценарий (персона/поверхность/момент) | ✅ | §1 | | Что человек увидит до/после | ✅ | §2 | | Проблема | ✅ | §3 | | Скоуп и не-скоуп | ✅ | §4 (цели) + §5 (не входит) | | Контракт поведения / UX | ✅ | §6–§13 | | Модель данных и миграция | ✅ | §14 — «не меняется, миграции нет» | | i18n | ✅ | §20 — новых ключей нет | | AC1…ACn с доказательством | ✅ | §17, 14 критериев, у каждого назван способ | | План автотестов | ✅ | §18 (unit / browser smoke / golden-perf) | | Риски | ✅ | §22, 8 рисков с мерой | | Откат | ✅ | §23 | | Release-артефакты | ✅ | §21 | Все обязательные разделы присутствуют и содержательны, не переписывают issue дословно. ## Находки Блокирующих (High) находок нет. В скоупе-Medium находок нет. Вне скоупа — нет. Ниже — три Low-наблюдения; ни одно не требует правки ТЗ, оставлены с записью решения ревьюера (не блокируют переход в `S5-ready`). ### Low-1 — AC13 частично доказывается гейтом, недоступным на этой стадии цикла `AC13` называет доказательством «Performance profile + screencast smoke». Per `docs/STATUS.md`, CDP-скриншот-профиль (`continuity:screencast`) — **stable-release-only** гейт, а не гейт код-ревью или даже бета-гейта; то есть формально AC13 не может быть закрыт автотестом до промоушена в стабильный релиз. Это не изобретение автора ТЗ: тот же паттерн уже принят для #73 (`docs/superpowers/specs/2026-08-10-plan-visual-continuity-design.md` явно делит проверку на обязательный CI rAF sampler и pre-stable CDP screencast). Решение ревьюера: не блокировать — прецедент уже принят продуктом, а перформанс-часть AC13 (RAF/rebuild-счётчики) всё равно доказывается раньше, в pre-beta профиле (§8). Отметить только, что на код-ревью AC13 будет разобран по коду плюс перформанс-профиль, а screencast-часть закроется не раньше стабильного гейта — это стоит явно писать в хендоффе, а не подразумевать. ### Low-2 — общий easing-хелпер между `ModeTransitionController` и новым camera controller не специфицирован технически §24 помечает разделение camera/mode-контроллеров с общим easing как «принято предположительно, поменять свободно» — формально корректно по PROCESS.md §7.1 (технический вопрос, не продуктовый). Замечание чисто наблюдательное: `interpolateModeVisualState` в `src/mode-transition.ts:124-156` уже реализует ровно ту же схему (log-space для масштаба, линейно для центра, общий `ease()`), так что риск, который §22 таблицы называет «Wheel ощущается медленным» / «SVG и HTML расходятся», технически прецедентно снят — эта же формула уже работает в проде для перехода режимов. Не требует правки ТЗ. ### Low-3 — «дальний hint «Показать»» добавлен в матрицу §6, не упомянутый в исходном тексте issue Матрица §6 ТЗ добавляет девятую строку («Far hint «Показать»») относительно восьми строк в исходном issue. Проверено по коду: `_fitFar()` (`src/houseplan-card.ts:5879`) сам вызывает `_resetZoom()` — тот же примитив, что и «Вписать всё»/home-arrow/double-tap, так что это не новая функциональность, а актуализация того, что «Вписать всё» в issue уже подразумевало устройство разных источников одного и того же действия. Не расширение скоупа, не требует нового вопроса владельцу. ## Что проверено и корректно - Все фактические утверждения раздела 3 «Актуальность и проблема» о текущем коде (`_zoomAt`, `_onWheel`, `_stepZoom`, `_resetZoom`, `_fitAll`/`_fitFar`, lazy editor runtime, общий `_view` для flat/isometric) — сверены с исходным кодом и точны. - Константы `MIN_ZOOM = 1/3`, `ZOOM_MAX = 8`, easing `cubic-bezier(0.2, 0.7, 0.2, 1)` — совпадают с `docs/CANVAS.md` §5 и `src/mode-transition.ts`. - Persistence-контракт §13 («editor zoom не пишется в View intent», «`_saveZoom()` работает только в `mode === 'view'`») сверен с `src/houseplan-card.ts:6212-6228` — комментарий в коде подтверждает тот же инвариант дословно. - Терминология кнопок («Вписать всё», «Показать») сверена с `src/i18n/ru.json` — совпадает буквально. - `docs/TOUCH-SUPPORT.md` не противоречит требованию §12 «stage не получает overlay/inert» во время camera-only tween: канонический `inert` относится к отдельному, другому контракту View/editor mode-перехода (#101), а не к zoom-действию внутри режима — ТЗ корректно разграничивает эти два случая в §8. - AC1–AC14 однозначны, у каждого указан способ доказательства; ни одна формулировка не читается как два разных критерия одновременно. - Раздел 24 «Принятые технические предположения» корректно отделяет технические решения от продуктовых; продуктовых вопросов к владельцу действительно не осталось — сценарии из issue (interruption matrix, accessibility, no-op) уже были фактами, подтверждёнными комментарием владельца 2026-08-14 («вопросы: нет»). - Раздел «Не входит в задачу» корректно исключает inertia/kinetic pan, анимацию mode/space/projection-переходов, новый storage-формат, что не даёт скоупу расползтись на смежные issue (#101, #73, #89/122). - Двунаправленные ссылки issue ↔ ТЗ на месте (`docs/specs/README.md` строка 115, тело ТЗ строка 3). ## Чего не проверял - **Код-гейты `typecheck`/`test`/`build`/`check-docs` не прогонялись** — диф этого раунда состоит только из `docs/specs/082-smooth-zoom.md` (класс C), продуктового кода/тестов нет, прогонять нечего. Это сознательное решение по объёму, а не пропуск: `git diff --stat` выше подтверждает единственный изменённый файл. - Не проверялась реализуемость точной wheel-anchor математики (0.5 CSS px) на реальном RAF-цикле — это вопрос будущей реализации и код-ревью («тест умеет падать»), не спецификации: ТЗ формулирует требование измеримо (AC4, unit на presented-state retarget), способ доказательства назван. - Не запускал browser/perf/golden-смоки — на этой стадии кода нет, им неоткуда взяться; они будут частью реализации (§18) и предметом код-ревью. - Не проверял детально `iso-projection.ts` построчно на предмет скрытых зависимостей от zoom помимо общего `_view`/`_zoom` состояния — ограничился подтверждением, что модуль не содержит собственных ссылок на `viewBox`/`zoom` (grep пуст), что достаточно для вывода «камера едина для flat/iso» из §9/AC10. ## Вердикт ТЗ полное, разделы §7.1 закрыты, критерии приёмки однозначны и снабжены способом доказательства, продуктовых вопросов к владельцу не осталось, фактические утверждения о текущем коде и канонических документах проверены и точны. Блокирующих находок нет. **Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0 → в задаче**