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, отсутствие догадок, выданных за факт,
корректность продуктового вопроса и решения владельца, трейлеры коммита.
Как проверялось
- Прочитаны
docs/SCOPE.md,AGENTS.md,PROCESS.md(§2, §3, §4, §7, §8, §12). - Прочитано тело issue #372 и все 4 комментария: аналитика → продуктовый вопрос Q1 → решение владельца (Default) → публикация ТЗ.
- Прочитан
docs/USER-GUIDE.ru.md§18 (houseplan-space-card) — терминологияtitle/show_button/button_targetв ТЗ совпадает с действующим гайдом. - Прочитан
docs/CANVAS.md§4, §4.1, §6 — канонический документ подсистемы, так как задача трогаетspaceFrame/contentFrame, разделяемые с иконочным масштабированием. - Каждое фактическое утверждение ТЗ сверено с текущим кодом на
dev-состоянии рабочей копии (сам ТЗ-коммит кода не трогает):src/space-card.ts:812,827—title = config.title !== undefined ? config.title : sp?.title || '';.hp-static-titleрендерится только еслиtitletruthy → подтверждён факт «явная пустая строка уже не создаёт 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.mddiff — строка добавлена корректно, ссылка и путь совпадают.
- Файлы, названные в разделе «Затронутые файлы», существуют:
demo/smoke_space_card.mjs,test/space-geometry.test.mjs. - Коммит
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-находкой без правки ТЗ.