Files
houseplan-card/docs/reviews/SPEC-REVIEW-373-r1.md
2026-08-29 21:13:28 +03:00

13 KiB

SPEC-REVIEW-373-r1

Issue: #373 — Add a fit-to-content / crop option for houseplan-space-card. ТЗ: 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 <commit> для #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