# SPEC-REVIEW-122-r1 - **Issue:** https://github.com/Matysh/houseplan-card/issues/122 - **ТЗ под ревью:** `docs/specs/122-isometric-stage2.md` (коммит `76ce755`, ветка `issue/122-isometric-stage2`) - **Роль:** ревьюер ТЗ (не автор), этап `S4-spec-review` - **Трек:** обычный (не `small`) — аналитика владельца оценила сложность и риск 9/10, поверхностей больше одной (рендер/геометрия/UX/perf); лёгкий трек корректно не применён, `docs/specs/-*.md` присутствует - **Цикл:** r1/4 (полный трек, лимит 4) ## Скоуп ревью Проверялось соответствие ТЗ: - `docs/SCOPE.md` — попадание в Core user jobs (J1/J2/J3), узкое owner-approved исключение из фотореалистичного/3D-запрета, инвариант lock; - `PROCESS.md` §2.4/§2.5 (DoR), §7.1 (обязательные разделы ТЗ), §12 (запреты), §3 (правила 1, 2, 6, 9); - `AGENTS.md` — классы файлов (только `docs/**` в этом коммите), ветка, трейлеры; - нормативному предшественнику `docs/specs/089-isometric-view-stage1.md` и `docs/adr/089-isometric-stage1-renderer.md` — что Stage 2 не переписывает принятые решения Stage 1 задним числом; - каноническим документам затронутой подсистемы: `docs/ISOMETRIC.md`, `docs/WALL-THICKNESS.md`, `docs/LIGHT.md`, `docs/TOUCH-SUPPORT.md`, `docs/UX-MODES.md`; - `docs/USER-GUIDE.ru.md` — что видимого пользователю текста не появляется и документ не должен меняться; - фактическому состоянию кода (`src/render/opening-symbol.ts`, `src/logic.ts`, `src/labs.ts`, `demo/golden/matrix.mjs`) — на предмет того, что технические утверждения ТЗ не являются непроверенной догадкой; - треду issue #122 целиком, включая аналитику S2, вопросы владельца Q0–Q6 и их принятие, а также связанному issue #124 (performance-блокер). ## Как проверялось 1. Прочитан весь тред issue #122: аналитика (ценность 7/10, P2, сложность 9/10), пакет вопросов владельцу Q0–Q6 с default-вариантами, принятие всех default'ов владельцем 2026-08-13, хендофф автора «ТЗ готово к ревью». Подтверждено, что ни один вопрос не был техническим, замаскированным под продуктовый: Q0 (приоритет/ценность), Q1 (объём видимого сравнения Stage1/Stage2 — второй флаг или нет), Q2 (что задают референсы: материал или замена содержимого), Q3 (визуальное представление проёмов), Q4 (новый декоративный свет — да/нет), Q5 (floor edge/тени), Q6 (деградация) — все про то, что видит пользователь, и про объём этого issue. 2. Сверены обязательные разделы ТЗ (§7.1 `PROCESS.md`) — таблица ниже. 3. Прочитан `docs/specs/089-isometric-view-stage1.md` целиком и `docs/ISOMETRIC.md` — подтверждено, что Stage 2 ссылается на реальные решения Stage 1 (`wallBodiesGeometry()`, единая проекция, fingerprint без `_cfgEpoch`/HA state, LRU-кэш на 8 сцен, latched flat fallback, `projectedFrame()`, Labs-грамматика `iso`/`since 1.62.0`/`expires 1.65.0`) и не выдаёт их за собственное изобретение и не переписывает Stage 1 задним числом (ADR-89 не трогается, заводится новый `docs/adr/122-*.md`). 4. Прочитан `src/labs.ts` (`since: '1.62.0'`, `expires: '1.65.0'`) и `package.json` (`version: 1.63.0`) — утверждение ТЗ «expiry не расширяется, `1.65.0-beta.1` уже мёртв» технически корректно и согласуется с описанным в `docs/ISOMETRIC.md` числовым сравнением ядра версии. 5. Прочитан `src/render/opening-symbol.ts` — подтверждены буквально: gate открывается ровно на 10° (`10 * amount`, `docs/specs/122:213-215` против `opening-symbol.ts:97`), window — двухстворчатый casement (`rotate(${-90*amount})`/`rotate(${90*amount})`, `opening-symbol.ts:83-89` против §6.5 «two-leaf/casement»). Это не догадка автора, а точное описание существующего кода. 6. Прочитан `openingAmount()` в `src/logic.ts:316-323` — подтверждено поведение «no contact → door/gate open (1), window closed (0)», которое ТЗ называет «static-open default when no contact exists» (§6.5, Door/Gate). 7. Прочитан `docs/LIGHT.md` и `docs/WALL-THICKNESS.md` — подтверждено, что «один регион света на источник», «wallBodiesGeometry как единственный источник тела стены», «exterior silhouette из union центральных линий» — реальные, а не придуманные инварианты, на которые ТЗ ссылается в §6.3, §6.7, §7. 8. Прочитан `docs/TOUCH-SUPPORT.md` целиком — формулировки ТЗ §6.8 («pointer/focus/ARIA inert», «View и kiosk остаются полностью поддерживаемыми», «touch-only failure — product defect») дословно отражают контракт, а не изобретены заново. 9. Прочитан `docs/USER-GUIDE.ru.md` — поиском подтверждено отсутствие каких- либо упоминаний изометрии/объёмного вида; утверждение ТЗ §9 «документ остаётся молчащим про скрытый Iso» верно на текущий момент. 10. Проверено существование golden-сцены `isometric-no-borders-dark` в `demo/golden/matrix.mjs:23` — AC15/§12.3 ссылаются на реальный, а не вымышленный сценарий. 11. Прочитан issue #124 (`gh issue view 124`) — подтверждено: воспроизводимый performance-регресс `viewToggleMs.median 192.8мс` vs лимит `131.7мс` на exact-SHA профиле `large-house-isometric-v1`, статус `S1-new` (ещё не проанализирован). ТЗ §10 корректно описывает его как DoR-блокер реализации (не ревью), с двумя явными путями снятия (закрытие #124 или отдельное явное решение владельца) — ни то, ни другое не подменяет и не ослабляет существующий бюджет. 12. Проверено `docs/specs/README.md:80` — строка на #122 добавлена в том же коммите, ссылка issue ↔ ТЗ двусторонняя. 13. Проверены трейлеры коммита `76ce755`: `Issue: #122`, `User-Visible: no`; `git show --stat` подтверждает изменение только `docs/specs/122-*.md` и `docs/specs/README.md` — класс C, продуктовый код (класс A) не менялся, правило №1 `AGENTS.md`/`PROCESS.md` соблюдено на этапе `spec`. ## Обязательные разделы (§7.1 PROCESS.md) | Раздел | Есть | Комментарий | |---|---|---| | Сценарий (персона/поверхность/момент) | ✅ | §1 | | Что человек увидит до/после (без терминов реализации) | ✅ | §1, одной фразой | | Проблема | ✅ | §2 | | Скоуп / не-скоуп | ✅ | §4 / §5 | | Контракт поведения | ✅ | §6 (8 подразделов) | | UX | ✅ | §9 (явно: нет нового UI/i18n) | | Модель данных и миграция | ✅ | §8 | | i18n | ✅ | §9 (пусто, обосновано) | | AC1…ACn с доказательством | ✅ | §11, 16 штук, каждый с типом | | План автотестов | ✅ | §12 (unit/smoke/golden/performance/backend) | | Риски | ✅ | §14, 12 строк с вероятностью/impact/mitigation | | Откат | ✅ | §15 | | Release-артефакты | ✅ | §13 | Все обязательные разделы присутствуют и содержательны. Дополнительно присутствуют продуктовые под-разделы §7.1 (персона/поверхность/момент внутри §1) и явный блок §16 «принятые технические предположения — можно менять без пересмотра продукта», корректно отделённый от решений владельца §3. ## Находки Находок уровня **High** и **Medium** нет. ### Low-1 — отсутствует буквальная декларация «Touch editor: …» **Файл:** `docs/specs/122-isometric-stage2.md` (весь документ) `docs/TOUCH-SUPPORT.md` («Documentation rule») требует от новых спецификаций редакторных фич явно указывать одно из: `Touch editor: supported` / `best effort / intentionally degraded` / `not exposed`. Предшественник, Stage 1 (`docs/specs/089-isometric-view-stage1.md`, §7 «Touch editor: не exposed»), эту декларацию давал буквально, хотя ситуация идентична — редакторы Stage 2 не касается вовсе (§6.1 «Editors and houseplan-space-card stay Flat»). Stage 2 такой буквальной строки не содержит, хотя по содержанию §6.1/§6.8 поведение однозначно совпадает с «not exposed» — реальной неоднозначности для разработчика или ревьюера кода нет. **Решение ревьюера:** Low, не блокирует. Формально Stage 2 не вводит новую редакторную фичу (правило `TOUCH-SUPPORT.md` адресовано именно им), а содержательно контракт «редакторы не меняются» зафиксирован дважды (§6.1, §9). Оставляю на усмотрение автора — можно добавить строку `Touch editor: not exposed` при следующей правке для единообразия с Stage 1, можно снять этой записью. ### Low-2 — три AC используют тип доказательства вне буквального перечня §2.5 **Файл:** `docs/specs/122-isometric-stage2.md:445-457` (AC13, AC15, AC16) DoR (`PROCESS.md` §2.5) перечисляет типы доказательства как `unit` / `backend` / `smoke` / `golden` / «ревью кода». AC13 помечен только `(performance)`, AC15 — `(golden + documentation review)`, AC16 — `(typecheck + unit + build)`. Буквально `performance`, `typecheck`, `build` и `documentation review` в этот перечень не входят. По существу все три доказуемы существующими механизмами: `performance_smoke`/Full Performance и `typecheck`/`build` — реальные release-blocking гейты `PROCESS.md` §8, а «documentation review» — часть обычного код-ревью (§2.7). Тот же паттерн уже был отмечен как Low-1 в `SPEC-REVIEW-123-r1` и оставлен автору без блокировки цикла. **Решение ревьюера:** Low, не блокирует. Переформулировка в термины §2.5 (например, «ревью кода со ссылкой на `performance_smoke`/exact-SHA Full Performance» и «ревью кода со ссылкой на зелёный `typecheck`/`test`/`build`») повысила бы буквальную трассируемость, но не меняет, чем на практике будет доказан критерий. Оставляю на усмотрение автора или снимаю этой записью. ## Что проверено и корректно - Соответствие `docs/SCOPE.md`: задача закрывает J1/J2/J3, остаётся внутри узкого owner-approved исключения (2.5D, не фотореализм/не свободная камера/ не interior editor), не трогает единственную санкционированную поверхность actuation замков (`docs/SCOPE.md` «The lock invariant»). - Все вопросы владельцу (Q0–Q6) — продуктовые (видимое поведение, объём видимых изменений), ни один технический вопрос не был переадресован владельцу; все ответы получены и внесены в ТЗ без расширения скоупа. - ТЗ явно наследует, а не переопределяет Stage 1: проекция, `wallBodiesGeometry`, fingerprint без `_cfgEpoch`/HA-состояния, LRU-кэш 8 сцен, latched flat fallback, Labs-грамматика `iso`/`1.62.0`/`1.65.0` — все повторно использованы корректно, ни одно принятое решение Stage 1 не переписывается задним числом (историческая ADR-89 остаётся неприкосновенной, заводится новая `docs/adr/122-isometric-stage2-composition.md`). - Технические утверждения о существующем коде (10° gate, two-leaf window, static-open default при отсутствии contact, один регион света на источник, `wallBodiesGeometry` как единственный источник геометрии стен) подтверждены чтением реального кода, а не приняты на слово автора. - Скоуп/не-скоуп (§4/§5) корректно отсекает публичный rollout, второй флаг `iso2`, свободную камеру/#82, новые material-настройки, объёмные редакторы и `houseplan-space-card`, схемные поля высоты/подоконника, WebGL/Three.js — все типичные места, где скоуп мог бы незаметно расшириться, явно закрыты. - Зависимость от #124 обработана корректно: не как продуктовое решение Q0–Q6 (не переоткрывает их), а как отдельный DoR-блокер реализации (§10) с двумя явными путями снятия; ревью ТЗ по правилам `PROCESS.md` §10 разрешено вести, пока #124 открыт. - i18n/UX (§9): подтверждено отсутствием новых строк, README/CHANGELOG/ USER-GUIDE не обещают публичную функцию — соответствует `User-Visible: no` контракту для всей будущей реализации. - Модель данных и совместимость (§8): подтверждено отсутствием новых схемных полей, ключей `localStorage` версий и compatibility-записей; откат (§15) не требует миграции и работает немедленно через `?hp-labs=-iso`/`off`, как и в Stage 1. - AC1–AC16 однозначны, у каждого указан тип доказательства (за вычетом Low-2) и план автотестов (§12) даёт конкретный, воспроизводимый по коду маршрут; §12.1 явно требует, чтобы каждый unit-мутант «умел падать» (удаление внешнего edge, сдвиг leaf с jamb, включение HA-состояния в cache key, дублирование floor symbol, лишний light layer). - Не найдено ни одного продуктового утверждения о поведении, которое не следует ни из канонических документов, ни из решений владельца Q0–Q6, ни из чтения существующего кода, и при этом не помечено как предположение; раздел §16 корректно отделяет свободно изменяемые технические детали (именование модулей, точные проценты высоты панелей, число SVG-корней) от зафиксированных владельцем решений. - Реестр `docs/specs/README.md` обновлён тем же коммитом, коммит несёт корректные трейлеры (`Issue: #122`, `User-Visible: no`) и не содержит изменений класса A — правило №1 соблюдено на этапе `spec`. ## Чего не проверял - Не проверял реализуемость конкретной геометрической декомпозиции (`iso-openings.ts`/`iso-scene.ts`, точные проценты высоты панелей, число SVG-defs) — по правилам ТЗ (§16) это свободно изменяемое техническое предположение автора кода, не предмет ревью ТЗ. - Не проверял реальный performance-профиль `large-house-isometric-v1` и не прогонял #124 — на этапе `spec` это не требуется; факт открытости и статус #124 (`S1-new`) проверен через `gh issue view`, не исполнением бенчмарка. - Не запускал никаких автотестов, golden или смоков — на этапе `spec` это не требуется; существование референсных артефактов (`isometric-no-borders-dark` в `demo/golden/matrix.mjs`) проверено чтением файла, не исполнением. - Не проверял содержимое `docs/adr/089-isometric-stage1-renderer.md` построчно на полное покрытие всех констант камеры — доверился ссылке ТЗ и `docs/ISOMETRIC.md` как согласованному пересказу; расхождений при чтении обоих источников не обнаружено. - Не оценивал субъективную эстетическую адекватность референсов владельца (issue #89, комментарий с изображениями) — это продуктовое решение, принятое владельцем через Q2 и не подлежащее пересмотру ревьюером ТЗ. ## Вердикт Зелёный. High: 0, Medium: 0. Две находки Low (отсутствие буквальной декларации `Touch editor: …` и три AC с типом доказательства вне буквального перечня §2.5) — не блокируют, оставлены автору на усмотрение с записью в этом документе; при желании их можно поправить в этом же цикле без возврата в `S3-spec`.