14 KiB
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 к этому диффу неприменимы:
менять нечего, кода не существует. См. «Чего не проверял».
Как проверялось
- Прочитан
docs/SCOPE.md,AGENTS.md,PROCESS.md(§1, §2.3–2.10, §4, §5, §7, §12). - Прочитано тело issue #82 и все три комментария (аналитика 2026-08-14, заведение ТЗ 2026-08-15, актуализация 2026-08-30).
- Прочитан полный текущий текст
docs/specs/082-smooth-zoom.md(24 раздела). - Прочитан канонический
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). - Проверены фактические утверждения ТЗ о текущем коде против
src/houseplan-card.ts(_zoomAt,_onWheel,_stepZoom,_resetZoom,_fitAll,_fitFar,_baseVb,_clampView,ZOOM_MAX/ZOOM_MIN,LS_ZOOM,_saveZoom, lazyimport('./houseplan-editor-runtime')) иsrc/mode-transition.ts(ModeTransitionController,interpolateModeVisualState— уже интерполируетpixelsPerUnitв log-space и center линейно тем же easing, что подтверждает реализуемость предложенного §9 механизма). - Проверена терминология кнопок/подсказок против
src/i18n/ru.json(title.zoom_fit= «Вписать всё»,canvas.show_far= «Показать») — совпадает с матрицей §6 ТЗ. - Проверено существование смоков, на которые ссылается план тестов
(
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. - Проверены обязательные разделы ТЗ по 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, easingcubic-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 → в задаче