Files
houseplan-card/docs/reviews/SPEC-REVIEW-372-r1.md
T
2026-08-29 13:16:04 +03:00

16 KiB

SPEC-REVIEW-372-r1

Issue: #372 — «Remove empty header spacing when title is empty in houseplan-space-card» Этап: spec (PROCESS.md §2.4) · заход r1 · блокирующих циклов израсходовано 0 из 4 ТЗ: docs/specs/372-space-card-empty-title.md, коммит edb9b6b3 (ветка issue/372-space-card-empty-title) Трек: полный (аналитика назвала критерий small, который задача не проходит — новый UX-контракт для title)

Скоуп ревью

Первый заход по этой задаче — предыдущего вердикта нет, разбор полный. Проверялось: соответствие docs/SCOPE.md, обязательные разделы ТЗ по §7.1, однозначность и доказуемость каждого AC, отсутствие догадок, выданных за факт, корректность продуктового вопроса и решения владельца, трейлеры коммита.

Как проверялось

  1. Прочитаны docs/SCOPE.md, AGENTS.md, PROCESS.md (§2, §3, §4, §7, §8, §12).
  2. Прочитано тело issue #372 и все 4 комментария: аналитика → продуктовый вопрос Q1 → решение владельца (Default) → публикация ТЗ.
  3. Прочитан docs/USER-GUIDE.ru.md §18 (houseplan-space-card) — терминология title/show_button/button_target в ТЗ совпадает с действующим гайдом.
  4. Прочитан docs/CANVAS.md §4, §4.1, §6 — канонический документ подсистемы, так как задача трогает spaceFrame/contentFrame, разделяемые с иконочным масштабированием.
  5. Каждое фактическое утверждение ТЗ сверено с текущим кодом на dev-состоянии рабочей копии (сам ТЗ-коммит кода не трогает):
    • src/space-card.ts:812,827 — title = config.title !== undefined ? config.title : sp?.title || ''; .hp-static-title рендерится только если title truthy → подтверждён факт «явная пустая строка уже не создаёт header».
    • src/space-geometry.ts:419-429 (spaceFrame) — симметричный pad = 0.05 на всех четырёх сторонах; fallback на space.vb без паддинга либо на legacy unit square — подтверждён факт «полоса — верхняя часть 5%-го поля» и корректность контракта п.6 («если content frame отсутствует, compact mode не выдумывает новый crop»).
    • src/space-render.ts:274-275,543 — vb = [fr.x, fr.y, fr.w, fr.h] идёт прямо в viewBox; space-render.ts импортируется только из space-card.ts → подтверждена локальность правки (не задета полная houseplan-card).
    • src/houseplan-card.ts:5516-5517 — тот же spaceFrame вызывается без переопределения pad → допустимость «необязательного параметра» в ТЗ (раздел «Принято предположительно») не меняет дефолтный путь других потребителей.
    • src/space-geometry.ts:457-499 (iconUnit, iconCqw) — масштаб иконок считается от contentFrame(items, {pad:0}) и делится на vb[2] (ширину), а не высоту. Контракт ТЗ (п.4: «меняются только viewBox.y и height, ширина неизменна») технически не задевает формулу размера иконок и declump-раскладку — риск «иконки поедут» в ТЗ закрыт корректно.
    • src/space-render.ts:541 — style="aspect-ratio:${vb[2]}/${vb[3]}..." — подтверждает, что уменьшение height уменьшит и высоту сцены/карточки, как заявлено в «Что человек увидит».
    • demo/docs/ не содержит упоминаний space-card → подтверждена гипотеза ТЗ «канонические PNG документации не содержат static space card, поэтому check-docs не даст визуальной регрессии по этой карточке».
    • docs/specs/README.md diff — строка добавлена корректно, ссылка и путь совпадают.
  6. Файлы, названные в разделе «Затронутые файлы», существуют: demo/smoke_space_card.mjs, test/space-geometry.test.mjs.
  7. Коммит edb9b6b3 проверен git show -s --format=full: трейлеры Issue: #372 / User-Visible: no корректны для документации без изменения поведения (класс C, PROCESS.md §1/AGENTS.md).

Гейты кода не гонялись — на этапе spec-ревью нет диффа продуктового кода; единственный диф — docs/specs/** и docs/specs/README.md, класс C.

Проверка по SCOPE.md

Сценарий закрывает J1 (docs/SCOPE.md: «Show the whole home... live spatial overview») в его компактном read-only варианте — houseplan-space-card явно описана в SCOPE.md как View-поверхность. Изменение чисто визуальное, не расширяет интерактивность, не затрагивает лок-инвариант, не входит в Out-of-scope список. Соответствует.

Проверка обязательных разделов (§7.1)

Присутствуют все обязательные разделы: сценарий · что человек увидит до/после · проблема · скоуп и не-скоуп · контракт поведения (9 пунктов) · UX и доступность · модель данных/миграция/совместимость · i18n · AC1–AC8 с доказательством · план автотестов (7 шагов) · риски (привязаны к AC) · откат · release-артефакты. Дефектов нет.

Первые два раздела («Сценарий», «Что человек увидит») отвечают на требуемые вопросы: персона, поверхность, момент; и «одной фразой, без терминов реализации» — выполнено, реализационные детали (spaceFrame, viewBox) вынесены в раздел «Проблема», как и предписано структурой.

Проверка AC

Все 8 AC пронумерованы, каждый называет способ доказательства (unit, browser smoke, «ревью кода», CI). Формулировки однозначны и допускают проверку числом (координаты viewBox, getBoundingClientRect()), а не описательно. AC5 отдельно защищает вырожденные/fallback случаи от NaN/ Infinity — соответствует существующему коду (spaceGeometry.ts уже обрабатывает DEGENERATE/SANE_LIMIT, ТЗ не выдумывает новый механизм, а требует его сохранения).

Замечаний по AC нет.

Проверка на догадки, выданные за факт

Раздел «Проблема» содержит числовые утверждения (topGap = 0 px при пустом title, topGap = 37 px при непустом) — они взяты из аналитического комментария к issue («Проверенный факт»), а не из самого ТЗ, и подтверждаются кодом (см. «Как проверялось», п.5). Формулировки контракта поведения — не предположения, а прямое отражение того, что уже верно для существующего кода (spaceFrame, iconCqw) плюс явное решение владельца по Q1.

Раздел «Принято предположительно, поменять свободно» корректно ограничен непродуктовыми деталями (имя helper, точное место арифметики, конкретная fixture, способ переиспользования spaceFrame) — эти решения не наблюдаемы пользователем и по §7.1 их не нужно выносить владельцу.

Единственная пограничная зона — поведение title с одними пробелами (whitespace-only). ТЗ явно относит её к «Не-скоуп» («значение, отличное от точной пустой строки, сохраняет текущую семантику») и не нормализует. Это можно было бы счесть продуктовым пограничным случаем (§7.1 перечисляет «поведение в пограничном случае» как повод спросить владельца), но исходный текст issue и Q1 говорят именно о title: "", альтернативное поведение не запрошено пользователем, а решение сохраняет статус-кво (наименьшее удивление) и явно задокументировано, а не скрыто. Не считаю это находкой — уровень Low, снимаю с записью: граница названа, обоснована и не меняет видимое поведение относительно текущего dev.

Проверка вопроса владельцу и его решения

Q1 задан корректно по форме §7.1: что неясно · что изменится от ответа · предлагаемый вариант по умолчанию (Default/Альтернатива). Вопрос продуктовый (влияет на видимый контракт title), не технический. Issue корректно ушёл в S3-spec + blocked на время ожидания и разблокирован после ответа владельца. ТЗ включает принятое решение (Default) без искажений.

Проверка трейлеров и артефактов

  • Issue: #372 / User-Visible: no на коммите edb9b6b3 — корректно, диф не меняет пользовательское поведение (только docs/specs/**).
  • docs/specs/README.md — запись добавлена, путь и заголовок совпадают.
  • Ссылка issue ↔ ТЗ на месте с обеих сторон (тело issue ссылается на файл ТЗ в ветке; файл ТЗ ссылается на issue).

Находки

Нет находок уровня High или Medium. Одна Low-находка (whitespace-only title, см. выше) рассмотрена и снята с записью прямо в этом документе — правка ТЗ не требуется.

Что проверено и корректно

  • Полнота обязательных разделов ТЗ (§7.1).
  • Однозначность и доказуемость всех 8 AC.
  • Фактические утверждения о текущем поведении (topGap, pad=0.05, использование spaceFrame/iconCqw) — подтверждены чтением src/space-card.ts, src/space-geometry.ts, src/space-render.ts, src/houseplan-card.ts.
  • Локальность правки: space-render.ts используется только space-card.ts; spaceFrame также используется в houseplan-card.ts без переопределения pad, поэтому необязательный параметр не сломает дефолтный путь полной карточки — заявленный в ТЗ риск «случайно меняется полная карточка» технически обоснованно закрывается выбранным подходом.
  • Независимость масштаба иконок (iconUnit/iconCqw) от высоты viewBox — компакт-режим меняет только y/height, что не пересекается с формулой размера иконок (делится на ширину vb[2], не на высоту).
  • Отсутствие static space card в канонических docs-скриншотах — заявление ТЗ о нулевом визуальном дифе check-docs подтверждено по дереву demo/docs/.
  • Продуктовый вопрос Q1 задан и решён по процессу, решение отражено в ТЗ без искажений.
  • Трейлеры коммита edb9b6b3 и запись в docs/specs/README.md.
  • Названные файлы для тестов (demo/smoke_space_card.mjs, test/space-geometry.test.mjs) существуют.

Чего не проверял

  • Продуктовый код не написан на этом этапе — код-ревью впереди, при переходе в S6→S7. Технической реализуемости контракта я касался только для того, чтобы убедиться, что ТЗ не строится на неверных фактах о коде (не для оценки будущей реализации).
  • Гейты typecheck/test/build/golden/check-docs/смоки не гонялись — на spec-ревью нет диффа src/**, единственный диф — docs/specs/**.
  • Не проверял историю других issue (#150, #214 и т.д.), кроме тех, что нужны для контекста самого процесса ревью (эта задача — r1, «Унаследовано» не применимо).

Вердикт

Зелёный. ТЗ полное, каждый AC доказуем и однозначен, факты о текущем поведении подтверждены чтением кода, продуктовый вопрос корректно решён владельцем до написания ТЗ, догадок за фактами не найдено. Единственная пограничная зона (whitespace-only title) рассмотрена и снята Low-находкой без правки ТЗ.