19 KiB
SPEC-REVIEW-584-r1
Issue: #584 · Этап: ТЗ на ревью (S4-spec-review) · Заход: r1 · Трек: полный (аналитика от 2026-09-15 назвала критерий: сложность/риск >3, несколько модулей и поверхностей, меняется публичный контракт «см. в свойствах = видимый габарит»).
Вердикт: жёлтый
Скоуп проверки
ТЗ живёт в теле issue #584 под заголовком ## ТЗ (комментарий владельца от
2026-09-16T19:33:08Z объявляет переход в S4-spec-review). Материал ревью —
текст issue на момент чтения (последний комментарий владельца, тот же, что
поставил статус); отдельного файла в docs/specs/ нет и не создаётся (архив
закрыт с #517).
Прочитано перед разбором: docs/SCOPE.md, AGENTS.md, PROCESS.md целиком
(включая §2.4, §2.5, §2.7, §7.1, §12), тело issue #584 и все 6 комментариев
(аналитика → вопросы Q1/Q2 → ответ владельца → полное ТЗ дизайнеру →
присланный архив → приёмка пака дизайнером-исполнением). docs/TOUCH-SUPPORT.md
и канонические документы подсистем (SUN/LIGHT/CANVAS/WALL-THICKNESS/UX-MODES/
CONFIG-COMPATIBILITY) просмотрены на предмет пересечения — задача их не
затрагивает (чистая геометрия SVG-артворка мебели, не стены/не свет/не солнце).
Как проверялось
Поскольку задача целиком про фактическую геометрию, а не про рассказ, каждое
фактическое утверждение ТЗ сверено с текущим состоянием репозитория (SHA
7c5fd32a, dev), а не принято на слово:
| Утверждение ТЗ | Проверка | Результат |
|---|---|---|
44 плановых SVG в svg/plan, 33 иконки в svg/menu |
ls assets/furniture/houseplan-0.3.0/svg/plan | wc -l, svg/menu |
44 и 33 — совпадает |
furnitureRenderTransform существует |
grep -r furnitureRenderTransform src/ |
найден в houseplan-card.ts, houseplan-editor-runtime.ts, furniture.ts |
parseSvgPath — производственный парсер экспорта |
grep -r parseSvgPath src/ |
единственное определение в src/pdf/svg-path.ts |
kitchen_floor — дизайнерский символ 60×60, svg/plan/kitchen_floor.svg |
pack.json строки 580-600 |
width_cm: 60, depth_cm: 60, file: svg/plan/kitchen_floor.svg — совпадает |
dishwasher — legacy-примитив 60×60, заполняющий unit box |
pack.json (единственная запись — svg/menu/dishwasher.svg, без width_cm) + src/furniture.ts:155 (w: 60, h: 60, g: [box(), …]) |
подтверждено: dishwasher не входит в дизайнерский пак, это legacy-примитив из RETAINED_IDS, box() рисует полный 0..1×0..1 |
plant — единственный legacy-символ с полями 2% |
src/furniture.ts:226-231 (e 0.5,0.5,0.22,0.22 + лучи-линии до 0.02/0.98) |
внешний bbox линий действительно 0.02…0.98 — 2% с каждой стороны, совпадает |
Генератор склеивает path простой конкатенацией paths.join(' ') |
scripts/generate-furniture-assets.mjs:97 |
return { d: paths.join(' '), … } — подтверждён ровно описанный дефект |
furniture-plan-art.generated.ts, furniture-plan-catalog.generated.ts существуют |
ls src/*.generated.ts |
оба файла на месте |
furniture:check — существующий гейт |
package.json:47 |
"furniture:check": "node scripts/generate-furniture-assets.mjs --check" |
test/furniture-*.test.mjs — свидетели AC1–AC5 |
прочитаны furniture-stroke-contract.test.mjs, furniture-transform-contract.test.mjs |
оба существуют, но проверяют другие контракты (#361/#376/#383 — экранный stroke-resolver, resize/rotate editor-контракт), а не bounds SVG-геометрии; конфликта с новыми AC нет, но новые unit-тесты AC1–AC4, скорее всего, лягут в отдельный новый файл — это техническая деталь, не продуктовая, решать разработчику |
Все фактические утверждения ТЗ подтвердились чтением кода — ни одной догадки,
выданной за факт, не найдено. Отдельно ценно, что владелец не просто принял
присланный дизайнером пак на слово, а перепроверил его тем же
production-парсером, которым будет проверяться AC1/AC2 (комментарий от
2026-09-16T19:33:08Z: 44 из 44, худшее отклонение 0.000000) — то есть контракт
уже был воспроизведён на реальных файлах ещё до передачи в разработку.
§7.1 — обязательные разделы
| Раздел §7.1 | Есть в ТЗ | Комментарий |
|---|---|---|
| Сценарий | ✅ «Пользовательский сценарий» | персона не названа явно (home admin), но контекст (свойства мебели, план) её однозначно определяет |
| Что человек увидит до/после | ✅ | числа названы (14% по стороне, 30% по площади), формулировка «это исправление, а не регрессия» — по делу |
| Проблема | ✅ | в теле issue выше ## ТЗ («Что происходит») |
| Скоуп и не-скоуп | ⚠️ распределено | явного заголовка нет; не-скоуп фактически задан AC7 («редактор, hit-area, привязка к стенам, схема конфига и i18n не меняются») — содержание есть, форма нет. Low, не блокирует |
| Контракт поведения | ✅ «Контракт», пп. 1–8 | однозначен, проверен по коду (см. таблицу выше) |
| UX | ⚠️ отсутствует явно | по существу — «нет нового UX-контракта» (задача не трогает интеракции), это следует из AC7, но отдельного раздела нет. Low, не блокирует |
| Модель данных и миграция | ✅ «Совместимость и миграция» | миграции нет, обосновано |
| i18n | ✅ | явное «новых ключей нет» |
| AC1…ACn с доказательством | ✅ AC1–AC8 | у каждого назван способ (unit/golden/review/гейты) |
| План автотестов | ⚠️ распределено | не оформлен отдельным разделом, но каждый AC называет тип теста и файлы перечислены в «Затронутые файлы» — по существу план есть |
| Риски | ✅ «Риски» | 3 пункта, по делу (маскировка регрессии golden-пересъёмкой, склейка путей, рост ленивого чанка) |
| Откат | ✅ | «возврат коммита» — корректно для непереносимой миграции |
| Release-артефакты | ⚠️ частично | changelog RU+EN и golden названы (в «Затронутые файлы»), перф — отдельным разделом; security явно не упомянут (ни разу, даже «нет») |
Находки
Medium (в скоупе) — влияние на touch не названо
Файл: тело issue #584, раздел ## ТЗ.
DoR §2.5 явно требует пункт «влияние на touch по docs/TOUCH-SUPPORT.md (View
и киоск — блокирующие)» как обязательный, отдельно от i18n и производительности,
которые в ТЗ присутствуют. В тексте ТЗ слова «touch», «тач», «киоск» не
встречаются вообще — раздел не просто краток, он отсутствует.
Почему это не тривиальная придирка: задача меняет физический видимый
размер мебели в View/kiosk тоже, не только в редакторе (мебель рендерится
и там, и там одним и тем же furnitureRenderTransform). AC7 отдельно
фиксирует «hit-area… не меняются», что фактически закрывает главный
touch-риск (позиция resize/rotate-хэндлов на touch не сдвигается), но это
написано как граница скоупа редактора, а не как ответ на явно
предусмотренный процессом пункт. Реальный риск, скорее всего, нулевой — но
DoR требует явного «нет», а не выводимого из соседнего пункта.
Как закрыть: одно предложение в ТЗ, например: «Touch: изменение непосредственно не затрагивает — View/kiosk остаются readonly-поверхностью, позиции hit-area и resize/rotate-хэндлов не пересчитываются (AC7); мебель в редакторе визуально крупнее/меньше на ту же величину, что и в View, новых touch-жестов не добавляется.» Правки кода не требует.
Серьёзность: Medium, в скоупе — блокирует переход в DoR (§2.5 явно требует пункт), но правится добавлением одного абзаца в тот же issue, без нового цикла имплементации. Без High это жёлтый вердикт.
Low (снимается с записью, не блокирует)
- Раздел «UX» и «скоуп/не-скоуп» не оформлены отдельными заголовками. Содержание по факту присутствует: AC7 однозначно перечисляет, что НЕ меняется (редактор, hit-area, привязка к стенам, схема конфига, i18n), а отсутствие нового UX-контракта следует из того же AC7 и из того, что задача правит только артворк и генератор. Формального заголовка нет, но переспрашивать автора ради двух заголовков дороже, чем цена неясности — снимаю с записью.
- Security как release-артефакт не назван даже «нет». Задача не затрагивает ни одну поверхность из OWASP-класса рисков (правятся SVG-пути мебели и генератор ассетов, без сетевых вызовов, парсинга пользовательского ввода на новом пути или новых прав) — риска не вижу, но пункт DoR формально пуст. Снимаю с записью, а не как Medium: в отличие от touch, здесь нет спорной поверхности (View/kiosk), которую процесс называет блокирующей явно.
- AC3 «допуск, пропорциональный масштабу» не даёт формулу. Это разумно оставить на усмотрение разработчика (технический, не продуктовый вопрос — §7.1: «то, чего пользователь не наблюдает, агенты решают сами») при условии, что реализация зафиксирует конкретную формулу и она останется проверяемой (не «плавает» между прогонами). Ревьюер кода должен убедиться, что тест «умеет падать» на конкретной, а не абстрактной величине допуска — отмечаю на будущее, не как находку ТЗ.
Что проверено и корректно
- AC1–AC8 однозначны и снабжены способом доказательства (unit / unit-generator / golden / review / гейты) — ни один не пришлось домысливать.
- AC2 конкретен: именно пара
kitchen_floor/dishwasher, оба подтверждены вpack.json/furniture.tsкак реальные60×60записи — тест будет проверять то, что реально описано в баг-репорте, а не абстракцию. - AC4 и контракт п.6 точно описывают дефект склейки путей — сверено с
реальным кодом генератора (
paths.join(' ')), включая упомянутыеcoffee_table_round/table_round. - Ни одной догадки, выданной за факт: все количественные утверждения
(44 символа, доля полей 2,7–3,9 у дизайнерских, 2% у
plant, конкретные id и размеры) подтверждаются либо кодом, либо независимым измерением владельца тем же production-парсером, которым будет доказываться AC1. - Продуктовые вопросы закрыты владельцем предметно (Q1 — видимый габарит
= осевая линия контура, обводка не считается; Q2 — без миграции,
центрируется в прежнем физическом боксе), открытых продуктовых вопросов в
тексте не осталось; технические решения (склейка путей,
plant) вынесены в явный блок «Принятые предположения», как требует §7.1. - Границы скоупа (AC7) явно исключают редактор/hit-area/привязку к стенам/схему конфига/i18n — правка не расползается на соседние подсистемы.
- Откат и совместимость описаны корректно для правки без миграции:
x/y,w/h, угол, зеркалирование сохраняются, откат — реверт коммита. - Риски названы предметно, включая специфичный для этой задачи риск — маскировку чужой регрессии golden-пересъёмкой.
Чего не проверял
- Не проверял сам присланный дизайнером ZIP-архив (
fix-584-1) — на этапе ревью ТЗ кода/ассетов ещё нет, они появятся в ветке реализации; их проверка (в том числе повторное измерение AC1/AC2 после факта) — предмет код-ревью. - Не запускал
furniture:check,npm test,golden:verifyи т.п. — на этом этапе нет ни одной строки кода/ассета, гейты нечего гонять; это предмет §8 на код-ревью, не ревью ТЗ. - Не проверял
docs/CONFIG-COMPATIBILITY.mdпострочно на предмет дополнительных полей совместимости сверх того, что решает п.8 контракта — задача явно не трогает схему конфига (AC7), риск счёл пренебрежимым для ревью ТЗ.
Итог
ТЗ содержательно сильное: контракт точен, AC проверяемы и уже частично эмпирически подтверждены владельцем на реальном присланном паке тем же инструментом, которым будет доказываться AC1. Единственная находка, требующая правки текста, — отсутствующий явный пункт про touch/View/kiosk, обязательный по DoR §2.5. High-находок нет, поэтому вердикт жёлтый: одно предложение в ТЗ, без нового раунда имплементации.
Материал раунда
- Issue: https://github.com/Matysh/houseplan-card/issues/584
- Материал ТЗ: тело issue #584, раздел
## ТЗ, редакция комментария от 2026-09-16T19:33:08Z (тот же комментарий ставитS4-spec-review). - Репозиторий на момент ревью:
dev@7c5fd32a08f07177d55905d65990180a354dd0ec(кода по задаче ещё нет — ревью ТЗ смотрит только текст issue и сверяет его утверждения с текущимdev).
Материал раунда
- Ветка:
dev, коммит7c5fd32a08f0— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
2b57427cbed60f2f3b46aaf1b2c9a84147a8ed88git log --all --format='%H %T' | grep 2b57427cbed6 - Тело issue:
a4ec41f212d36f3ec2d313b534161c25a38e58c118ed2f7b97ab437de1593eb3 - Вердикт конвейера:
yellow· High 0