Files
houseplan-card/docs/reviews/SPEC-REVIEW-400-r1.md
T
2026-08-31 04:45:04 +00:00

161 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW #400 · заход r1
- Issue: https://github.com/Matysh/houseplan-card/issues/400
- Артефакт ТЗ: `docs/specs/400-beta-polish.md`
- SHA материала: `60c211c82ad12940e68040de1455f13a1410683e` (HEAD ветки `issue/400-beta-polish`,
единственный коммит `docs: specify the beta polish batch (#400)`)
- Трек: полный (три несвязанные поверхности — рендер `.dtframe`, состав
ленивых чанков, `_alignCandidates`); критерий `small` «одна поверхность»
явно назван как нарушенный в шапке ТЗ. Заход r1, лимит цикла 4 (полный трек).
## Скоуп
ТЗ объединяет три пункта из аудита беты v1.70.0-beta.1 (прецеденты #369, #385
подтверждают практику батчить несколько Low/Medium-находок аудита в одно ТЗ):
1. **M5** — осевые ручки трансформ-рамки декора (`.dtframe`) рисуются поверх
угловых и на мелкой мебели (≲80 см) забирают часть хит-зоны угла.
2. **Low «б»** — 38 ключей помощи `en`/`ru` лежат в initial-чанке, а не в
ленивом чанке редактора.
3. **Low «г»** — исключение перетаскиваемого маркера в `_alignCandidates`
(режим `devices`) опирается на мёртвый `this.host._drag?.id` вместо
`_deviceDrag?.id`.
## Как проверялось
Ревью ТЗ — не код-ревью: продуктовый код не менялся, гейты (`typecheck`,
`test`, `build`) неприменимы на этом этапе и не запускались. Проверка велась
чтением исходников на SHA выше и сверкой с ТЗ.
Проверено построчно:
- `src/houseplan-card.ts:8439` — `hr = Math.max(view.w, view.h) * 0.018`,
общий для угловых (`:8467`, `corners.map`) и осевых (`:8474`, `sides.map`)
ручек. `sides.map` рендерится **после** `corners.map` в одном шаблоне →
на overlap-зоне хит достаётся последним отрисованным (осевым) кругам.
Комментарий кода на месте (`:8434-8438`) подтверждает решение владельца от
2026-08-05 «уменьшить в 4 раза» — оно относится к видимой бусине `kr = hr/4`,
не к хит-радиусу `hr`; ТЗ корректно исключает `hr` из скоупа.
- `src/houseplan-editor-runtime.ts:10943,10970` — метод называется
`_alignCandidates()` (ТЗ ссылается на него верно); в ветке `devices`
исключение действительно идёт по `this.host._drag?.id` (`:10970`).
`_drag` (`houseplan-card.ts:2547`) и `_deviceDrag` (`:2548`, заполняется
`:6736`) — разные поля; в режиме `devices` перетаскивание всегда через
`_deviceDrag`, `_drag` там не присваивается — подтверждает «мёртвый
источник».
- `demo/smoke_align_guides.mjs` (ветка «редактор устройств», строки 30-47) —
прочитан целиком. Сценарий 3 подтверждает диагноз ТЗ буквально: `_layout[b]`
выставляется на ту же Y, что и цель выравнивания, `_deviceDrag` включается
для `b`, и `guides() >= 1` не различает «гид от `a`» и «гид от самого `b`»,
потому что кандидат `b` (не исключённый живым `_drag=null`) физически
совпадает с целью. Смок не умеет упасть на этот дефект — ТЗ прав, что нужна
правка теста (AC5), а не только кода.
- `src/logic.ts:1977` — `alignGuides(pt, candidates, tol)` существует и
принимает список кандидатов, как описано.
- `dist/houseplan-assets/houseplan-card-*.js` — `grep -c '\.help\.'` находит
совпадение только в initial-чанке (`houseplan-card-*.js`), не в
`editor-*.js`; `de-*.js`/`fr-*.js` существуют как отдельные lazy-чанки
(`src/i18n/de.ts`, `src/i18n/fr.ts` помечены «lazy» в комментариях) — ровно
та архитектура, которую описывает ТЗ («en/ru всегда в initial, лениво только
de/fr»).
- `scripts/bundle-budget.mjs:34` — `INITIAL_VIEW_GZIP_BUDGET = 300_000` —
совпадает с числом в ТЗ (примечание: `AGENTS.md` в другом месте называет
устаревшее «256000 B» из #337 — расхождение самого `AGENTS.md` с текущим
кодом, не дефект этого ТЗ).
- Сверены все внешние ссылки: issue #383 (мебель resize, closed), #369/#385
(прецеденты батч-ТЗ), #352/#355 (архитектура ленивых словарей), #337
(ленивый editor-чанк), #74 (undo/redo устройств, откуда возникло состояние
`_deviceDrag`) — все существуют и релевантны заявленному контексту.
- `docs/FURNITURE.md`, `docs/DECOR-EDITOR.md`, `docs/USER-GUIDE.ru.md` (строка
1293) — описывают поведение угловых/средних ручек мебели в общих словах, ни
один не фиксирует приоритет хита при пересечении кругов. Правка AC1 не
противоречит ни одному канону, новый контракт не заменяет и не ломает
задокументированное поведение.
- `docs/TOUCH-SUPPORT.md` — редактор подложки (декор) отнесён к
«best-effort»-поверхностям; правка — только хит-приоритет уже существующей
десктоп-ручки, нового touch-контракта не создаёт.
- `scripts/mutation-gate.mjs` — существующие id мутантов оформлены как
`kebab-case`-описание дефекта; два новых id из ТЗ (`furniture-edge-handles-
steal-the-corner`, `align-guides-exclude-dead-source`) следуют тому же
стилю.
## Находки
Ни одной находки High или Medium. Две Low, обе сняты ревьюером с записью (не
требуют возврата автору):
- **Low.** Раздел «Release-артефакты» не проговаривает пункт
`docs/specs/README.md` «затронутая пользовательская документация» явно (в
отличие от i18n/модели данных, где ТЗ пишет «не применимо»). По факту
`docs/USER-GUIDE.ru.md:1293` уже описывает угловой/осевой resize мебели в
терминах, которые AC1 не меняет (контракт «углы двигаются плавно и по
умолчанию сохраняют пропорции» остаётся верным, меняется только надёжность
попадания) — обновление не требуется по существу. **Снято**: добавление
пустой строки «USER-GUIDE не меняется» ничего не изменило бы в приёмке.
- **Low.** ТЗ не проговаривает влияние на touch явно (пункт DoR §2.5), хотя
явно проговаривает i18n и модель данных. Поверхность — редактор подложки,
который весь целиком best-effort на touch (`docs/TOUCH-SUPPORT.md`), а
правка — приоритет хита у уже существующей десктоп-ручки без изменения
touch-контракта. **Снято**: добавлять строку «touch: без изменений»
формально корректно, но не несёт информации сверх уже действующей политики.
## Что проверено и корректно
- Оба обязательных продуктовых раздела на месте: персона/поверхность/момент
(Сценарий) и «что человек увидит» одной фразой на каждый из трёх пунктов —
включая честное «видимой разницы нет» для пунктов (2) и (3).
- Трек выбран верно и обоснован явным нарушенным критерием («одна
поверхность»), а не общей отговоркой — соответствует правилу AGENTS.md
после #338.
- Все три технических контракта проверяемы и не оставляют скрытых допущений,
выданных за факт: формулировка M5 из исходного аудита («ресайз углом
недоступен») explicitly исправлена на подтверждённую замером и явно более
слабую («зона урезана вдвое»), с указанием, что именно осталось непроверено
(`elementFromPoint` через shadow root) и почему выбран другой способ
доказательства (синтетический `pointerdown` вместо `elementFromPoint`).
- Технические решения, не видимые пользователю (два исхода по help-чанку,
порядок отрисовки vs скрытие осевых ручек, имена тестовых файлов и
мутантов), явно поданы как варианты с обоснованным дефолтом и оставлены
реализации — ровно то, что требует §7.1, без скрытых догадок, выданных за
решение.
- AC1…AC6 пронумерованы, у каждого указан способ доказательства
(smoke/unit/gate-команда/код-ревью), и план автотестов покрывает AC1, AC2,
AC4, AC5 конкретными тестовыми сценариями; AC3 и AC6 — gate-командами
(`grep`+замер бюджета, `npm run bundle:budget`), что допустимо по DoR §2.5.
- Мутанты в плане нацелены именно на регрессии, которые новый тест должен
ловить (возврат старого порядка отрисовки → красный AC1; возврат
`_drag?.id` → красные AC4/AC5) — дисциплина «тест умеет падать» заложена в
сам план, а не декларирована.
- Скоуп/не-скоуп корректно исключает смежные, но чужие задачи: `hr`
(владелец, 2026-08-05), механику ресайза мебели (#383), тексты помощи
(#86), архитектуру ленивой поставки словарей (#352–#355) — предотвращает
расползание батча.
- Откат прост и не трогает пользовательские данные (два блока шаблона,
состав чанка/строка документации, одно поле условия).
- Ссылки issue ↔ ТЗ на месте в обе стороны (шапка ТЗ → issue; в issue есть
единственный комментарий S2-анализа, который ведёт к тому же батчу).
## Чего не проверял
- Не запускал `typecheck`/`test`/`build`/`bundle:budget` — на этапе ревью ТЗ
продуктовый код не менялся, гейты неприменимы; они станут предметом
код-ревью.
- Не проверял браузером фактическое перекрытие ручек в px (14 px, порог
«≈80 см») — принято как измерение автора в реальном DOM, зафиксированное в
комментарии S2-анализа; для этапа спек-ревью это описание проблемы, а не
критерий приёмки (критерий приёмки — бинарный AC1/AC2, он проверяем
независимо от точной пороговой цифры).
- Не проверял точное текущее значение запаса бюджета (15 945 Б) и суточный
расход (6,1 КБ) — это мотивационный фон проблемы (2), не предмет AC; AC3/AC6
сформулированы как проверяемые независимо от этих чисел.
- Не оценивал техническую реализуемость исхода (1) для пункта (2) (вынос
help-ключей без второго сетевого запроса) — ТЗ прямо оставляет это
реализации с честным запасным исходом (2), что и требуется от технического
решения на этом этапе.
## Вердикт
Зелёный. ТЗ полное по §7.1, критерии проверяемы, трек обоснован, ссылки и
факты в тексте подтверждены чтением кода на SHA `60c211c8`. Обе Low-находки
сняты ревьюером с записью выше — доработки не требуют.