# SPEC-REVIEW-373-r1 Issue: [#373](https://github.com/Matysh/houseplan-card/issues/373) — Add a fit-to-content / crop option for `houseplan-space-card`. ТЗ: [`docs/specs/373-space-card-house-fit.md`](https://github.com/Matysh/houseplan-card/blob/issue/373-space-card-house-fit/docs/specs/373-space-card-house-fit.md) Материал ревью (SHA): `e7ba22c623f6e9c0e5493cfe2a1706443621184b` (= текущий `HEAD`, `git rev-parse HEAD` сверен перед выводом). Заход: r1 · блокирующих циклов израсходовано 0 из 4 (первый заход, лимита ещё нет; полный трек, лимит 4). ## Скоуп проверки Первый заход — разбор полный, дельты нет. Прочитано в порядке инструкции: `docs/SCOPE.md`, `PROCESS.md`, тело issue #373 и все четыре комментария (владелец → OUARZA → владелец → аналитика владельца → хендофф автора ТЗ), `docs/USER-GUIDE.md` (раздел 18 «Static space card»), `docs/CANVAS.md` целиком (§4, §4.1, §4.2, «every place that assumed the unit square»), `docs/TOUCH-SUPPORT.md`, `docs/CONFIG-COMPATIBILITY.md` (фрагменты про `view_box`/новые поля), `docs/ARCHITECTURE.md` (раздел про `houseplan-space-card`), и сам файл ТЗ целиком. Дополнительно сверено с текущим кодом, чтобы отличить факт от досочинённого поведения: - `src/space-render.ts:260-316` — подтверждает буквально описанный в ТЗ вызов `spaceFrame(space, placed, o.compactTopFrame ? {...} : 0.05)`, включая сбор `zeroWalls`, `physicalBodyParts(...).all` (drafts/partitions/columns) и device-маркеров как voter'ов текущего content frame; - `src/space-geometry.ts:382-446` — `contentFrame`/`spaceFrame` реализуют ровно ту логику 5%-паддинга и outlier-голосования, которую ТЗ называет «текущим поведением» и явно исключает из нового `house`-режима; - `src/render/opening-symbol.ts:53-150` — подтверждает, что jamb/leaf/arc для door/window/gate — существующая, общая для static и full карточек геометрия, а не новая подсистема; - `src/space-card.ts:58-71`, `src/space-editor.ts:40-54` — подтверждают форму `SpaceCardConfig`, паттерн `ha-form`/`selector.select.dropdown` (уже используется для `space`) и отсутствие конфликта имени `fit` с существующими полями; - `src/i18n/{en,ru,de,fr}.json` — все четыре локали существуют, план i18n реалистичен; - `package.json` — `bundle:budget`, `bundle:sync` существуют; `demo/smoke_space_card.mjs` существует (`ls demo/smoke_*.mjs | wc -l` = 203); - `docs/specs/README.md` и `git show ` для #372 (`d696541a`) и #373 (`e7ba22c6`) — сверка соглашения об индексе, см. находку L1 ниже. Гейты в этом заходе не гонялись: этап — ревью ТЗ, кода к диффу нет (в ветке задачи только новый файл `docs/specs/373-space-card-house-fit.md`, класс C). `typecheck`/`test`/`build` не применимы к этапу spec-review. ## Как проверялось Не «согласился с автором», а состязательно: для каждого утверждения о «текущем поведении» найдена конкретная строка кода, которая его подтверждает (список выше). Для каждого продуктового решения — конкретный комментарий в issue, который его фиксирует. Отдельно искал: (а) утверждения о поведении, которого нет ни в одном документе и которое не помечено как предположение; (б) технические вопросы, ошибочно поднятые как продуктовые к владельцу — не найдено ни одного, все продуктовые вопросы уже закрыты перепиской владелец↔OUARZA до аналитики; (в) несоответствие треку `small`/`trivial` — аналитик обоснованно выбрал полный трек и назвал нарушенный критерий (новый публичный UX-контракт). ## Находки ### L1 (Low) — `docs/specs/README.md` не обновлён Коммит `e7ba22c6`, добавивший `docs/specs/373-space-card-house-fit.md`, меняет только этот один файл. Действующее соглашение репозитория — индекс `docs/specs/README.md` обновляется в том же коммите, что и сам файл ТЗ: коммит `d696541a` (#372) правит одновременно новый файл ТЗ и `docs/specs/README.md` (+1 строка). Для #373 такой строки в индексе нет — `grep -n "373" docs/specs/README.md` не находит ничего. **Почему Low, не Medium.** Не влияет ни на один AC, не меняет проверяемость и не продуктовое поведение — чисто индекс. Правится одной строкой в любой момент до слияния; не обязано блокировать переход в «Готово к разработке». **Что сделать:** автор добавляет строку в таблицу `docs/specs/README.md` (вероятно `P2`, судя по метке issue) в этом же ТЗ-коммите или в первом коммите реализации. Не требует отдельного цикла ревью. ## Проверено и корректно - **Продуктовая рамка (§7.1, первые два раздела).** «Сценарий» называет персону из `docs/SCOPE.md` (домашний администратор встраивает `custom:houseplan-space-card` в компактный dashboard) и поверхность (View/kiosk на планшете/телефоне). «Что человек увидит» сформулировано без терминов реализации. Задача закрывает J1 («компактная houseplan-space-card должна использовать ограниченную площадь dashboard») — соответствует `docs/SCOPE.md`, не задевает ни один пункт «Out of scope». - **Продуктовые вопросы закрыты перепиской, не додуманы.** Проверил дословно: владелец спросил, должен ли backdrop/объект-вне-комнат входить в кадр или только геометрия дома; OUARZA ответил категорично — «fitting bounds should use room/house geometry» (не только «вне комнат»), что и оправдывает контрактный пункт 8 ТЗ (исключение backdrop/decor/labels/markers/badges из голосования кадра как единого правила, а не частного случая «вне комнат»). Это не догадка, а прямое следствие ответа репортёра. - **Каждое утверждение о «текущем поведении» проверено по коду**, а не принято на слово (список файлов/строк в разделе «Скоуп проверки» выше). Ни одного расхождения не найдено. - **AC1–AC8 однозначны и указывают способ доказательства** (`unit`/`smoke`/ code review), включая обратную совместимость (AC1), геометрию (AC2/AC3), безопасный fallback на вырожденных/пустых пространствах (AC4), редактор и i18n (AC5), touch/View паритет (AC6), перформанс (AC7), документацию и релиз (AC8). Каждый снабжён конкретной фикстурой или сценарием, а не общей фразой «работает корректно». - **Раздел «Assumptions accepted provisionally» явно отделяет техническое решение от продуктового** и помечен как оспоримый ревьюером — ни одно из семи предположений не маскирует продуктовое решение под техническое; все семь — действительно нейтральные для пользователя технические выборы (имена внутренних хелперов, аналитический envelope без `getBBox()`, точные фикстуры тестов). - **Не-скоуп корректно отсекает соседнее поведение**: полная `custom:houseplan-card`, её Fit all/zoom/pan/outlier-hint, `title:""` семантика #372, изменение модели/схемы/`view_box`, произвольный пользовательский паддинг — всё явно исключено, что не даёт диффу реализации расползтись за рамки одной карточки. - **Данные/миграция/откат**: новое поле живёт только в конфиге карточки Lovelace, не в House Plan config/layout — обоснованно не требует ни backend-схемы, ни миграции, ни отдельного compatibility-поля по `docs/CONFIG-COMPATIBILITY.md`. Откат («убрать проекцию/опцию редактора») безопасен и не требует очистки данных. - **i18n-план реалистичен**: все четыре объявленных локали (`en/ru/de/fr`) существуют в `src/i18n/`, паттерн добавления строк такой же, как для существующих ключей (`editor.title`, `title.zoom_fit` и т.д.). - **Производительность и touch/themes разделы формулируют проверяемые ограничения** (без DOM-измерений/`getBBox()`, один `viewBox` на все слои, без роста initial-бандла), согласованные с `docs/CANVAS.md` §6 (тот же `iconUnit()`/канонический подход к рамке) и `docs/TOUCH-SUPPORT.md` (View — release-blocking, конкретные ширины 320/900 px). ## Чего не проверял - Реализуемость «zero-thickness room and independent wall axes» и «columns» как voter'ов кадра проверена только чтением существующих функций (`physicalBodyParts`, `resolveZeroWalls` — уже используются в `space-render.ts`), не исполнением: код для нового `house`-режима ещё не написан, поэтому не могло быть ни unit-, ни smoke-прогона. - Golden/скриншоты, `bundle:budget`, полный Validate — не гонялись: этап spec-review не производит diff по `src/**`; правки применимы к коду ревью, не к этому этапу. - Не оценивал трудозатраты/оценку сложности (5/10 в аналитике) — не входит в мандат ревью ТЗ. - Не проверял финальные тексты en/de/fr переводов (они появятся в реализации) — только то, что файлы существуют и структура ключей совместима. ## Вердикт Только Low-находка, не блокирует. AC полны, продуктовые вопросы закрыты переговорами до ТЗ (не додуманы), контракт однозначен и проверяем, скоуп/не-скоуп корректно отсекают соседнее поведение, откат и i18n решены. **Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0**