mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 06:59:46 +00:00
@@ -0,0 +1,160 @@
|
||||
# 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-находки
|
||||
сняты ревьюером с записью выше — доработки не требуют.
|
||||
Reference in New Issue
Block a user