Files
houseplan-card/docs/reviews/SPEC-REVIEW-239-r1.md
T
2026-08-22 09:58:21 +00:00

196 lines
18 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-239-r1
- **Issue:** https://github.com/Matysh/houseplan-card/issues/239
- **ТЗ:** `docs/specs/239-grid-scale-invariance.md`
- **Ветка / SHA:** `issue/239-grid-scale-invariance` @ `4c065f5`
- **Этап:** spec (PROCESS.md §2.4) · трек: обычный (`small`/`trivial` явно
отклонены автором в аналитике issue — несколько рендереров, новый default,
i18n, влияние на touch/performance)
- **Заход:** r1 · блокирующих циклов израсходовано 0/4 до этого вердикта
- **Ревьюер:** Claude, роль «ревьюер ТЗ»
## Скоуп
Оценивалось только ТЗ #239 — единый визуальный контракт «масштаб сетки
(`cell_cm`) не должен менять внешний вид плана» плюс новый default нового
пространства (1 см метрика / 1 дюйм imperial). Код не менялся и не
оценивался: этап S4, реализации нет, весь диапазон `origin/dev..HEAD`
состоит из одного docs-коммита.
## Как проверялось
1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` целиком (первый цикл,
правило «по дельте» §2.10 не применяется).
2. Прочитано тело issue #239 и оба комментария (аналитика с оценкой
9/9/8/8/P1/bug/обычный трек, затем хендофф «ТЗ готово»).
3. Прочитан файл `docs/specs/239-grid-scale-invariance.md` целиком (481
строка) и запись в `docs/specs/README.md`.
4. Построчно перепроверены фактические ссылки ТЗ на код (не поверено на
слово — большинство утверждений §3 являются "подтверждено по коду", что
обязывает проверить):
- `src/houseplan-card.ts:14088`, `:14459` — `cellCm: 5` в manual create и
floors-import draft — совпадает;
- `src/houseplan-card.ts:20591-20595` — поле `space.scale_label` /
`space.scale_unit` всегда рендерит сырые см без ветвления на
`hass.config.unit_system.length === 'mi'` — совпадает буквально
(заявление ТЗ §3.3, а не догадка);
- `src/houseplan-card.ts:295-296` — `CELL_CM_MIN = 0.1`, `CELL_CM_MAX =
1000` — совпадает с §5.2/§7.1 (диапазон не расширяется);
- `src/render/opening-symbol.ts:34-42` — `jambHalf` fallback `4`,
`outlineHalf = Math.max(16, jambHalf+8, gateDepth+8)`, `hitHalf =
Math.max(20, ...+10, ...+12)` — совпадает с заявленными «4-12 units»;
- `src/wall-thickness.ts:31-43` (`HATCH_REFERENCE_CELL_CM = 5`,
`HATCH_BASE_STEP_UNITS = 8`) — арифметика `8 × (5/5) × (шаг сетки)` даёт
заявленные 9.6 см из #230 и AC5 — проверено вычислением, не с чужих слов;
- `src/iso-projection.ts:29-30` — `ISO_WALL_HEIGHT = 64`,
`ISO_FLOOR_EDGE_HEIGHT = 10` — заявленные «user units» константы
существуют и не параметризованы `cell_cm`, совпадает с §3.2.5;
- `src/houseplan-card.ts:5241,5178-5254` — `_isoGeometryCache.get(source.key)`
подтверждает риск «тёплый remount вернёт старую высоту», если `source.key`
не будет включать масштаб — корректно учтено в таблице рисков §16, а не
пропущено;
- `src/space-geometry.ts:442-478` (`iconUnit`, `iconCqw`) — числитель и
знаменатель оба выведены из содержимого плана в тех же plan units, поэтому
при физически эквивалентном плане (`k = 5/cell_cm`) множитель `k`
сокращается сам — заявление §3.1 «эти пути уже верны, не домножать»
подтверждено алгеброй, а не принято на веру;
- `docs/USER-GUIDE.ru.md:1092,1145` — термины «дюйм»/«фут» для физических
размеров уже существуют в продукте, значит предлагаемая подпись «1 дюйм на
клетку» не изобретает новую терминологию.
5. Сверены §5 (входит/не входит) с §6 (классификация размеров) и §8
(контракт по поверхностям) на отсутствие противоречий — реестр
`Physical/Screen/Plan-relative/Visual unit/Grid` покрывает все пункты §3.2
без пропусков и без двойного назначения одного размера в два класса.
6. Сопоставлены критерии приёмки §13 (AC1–AC16) с планом автотестов §14 —
не как формальность, а по прямому указанию для этого этапа: «указание
способа доказательства». Для сверки взяты два прецедента из этого же
репозитория: `docs/specs/230-hatch-density-normalization.md` §13 (план
тестов поимённо покрывает AC1–AC12) и `docs/specs/238-opening-inner-distances.md`
§15 (таблица AC с отдельной графой «Доказательство» для всех 15 AC, включая
мета-критерии производительности и документации — AC14 «unit со
счётчиком/ревью кода», AC15 «provenance + ревью кода»). См. находку ниже.
7. Проверен гейт `node scripts/check-docs.mjs` — единственный применимый на
docs-only диффе класса C. `npx tsc --noEmit` / `npm test` / `npm run build`
не запускались: диапазон изменений не содержит ни одного файла класса A/B
(`git diff origin/dev...HEAD --stat` — только `docs/specs/239-*.md` и
строка в `docs/specs/README.md`), эти гейты ничего не проверяют на чистой
документации и были бы тратой времени без предмета (см. «Объём гейтов
соразмерен задаче»).
## Находки
### Medium (в скоупе задачи — исправляется в этом ТЗ)
**M1. §13 Acceptance criteria не указывает способ доказательства для AC13,
AC14, AC16, и это не восстанавливается однозначно ни из §14 «План
автотестов», ни из таблицы рисков §16.**
- AC13 (инвариант `cell_cm: 5` = pre-#239 контракт) частично закрыт: в самом
тексте AC упомянут «current golden», так что проверяемость угадывается, но
§14.3 говорит лишь «Golden baseline разработчик не принимает» — не называет
golden прямым доказательством AC13.
- AC14 (touch/gesture-инвариант: pan/pinch/pointercancel не получают новых
действий, opening action area не меньше эталонной) не назван ни в одном
пункте §14, а из таблицы рисков (§16, строка «Touch target станет physical»)
восстанавливается только половина критерия — про размер hit-area, но не про
«никакой новый жест ничего не запускает».
- AC16 (EN/RU user guide и оба changelog объясняют новый контракт) не назван
ни в §14, ни в release-артефактах §17 как проверяемый пункт, хотя оба
changelog там перечислены как артефакт.
Это не придирка к форме: у этой же задачи есть два прямых прецедента в этом
репозитории, оба прошли ревью зелёными на этом самом требовании процесса.
`docs/specs/230-hatch-density-normalization.md` §13 поимённо связывает каждый
AC1–AC12 с тестом. `docs/specs/238-opening-inner-distances.md` §15 — тот же
issue-поток, тот же автор, документ на день раньше #239 — держит отдельную
графу «Доказательство» и заполняет её даже для мета-критериев вроде
производительности (AC14: «unit со счётчиком/ревью кода») и документации
(AC15: «provenance + ревью кода»). #239 явно откатывается от этой практики без
объяснения, и без неё код-ревью не имеет явного контракта: чем именно должна
быть закрыта строка «pan/pinch не получают новых действий» — тестом или
утверждением автора. Учитывая объём и риск задачи (сложность 8/10, риск 8/10 в
собственной оценке автора), для «not by accident» touch-инварианта и для
одного из главных anti-regression AC (AC13) это не косметика.
**Как починить:** добавить в §13 колонку «Доказательство» (как в #238 §15)
либо явно перечислить AC13/AC14/AC16 в §14 с указанием конкретного смока,
unit-теста или «ревью кода» — по аналогии с уже принятым для этой же задачи
подходом «AC15 — только ревью кода, потому что perf-гейт до беты» (эта фраза
в документе уже есть для AC15, её не хватает для соседних AC).
### Low (снимается с записью, не блокирует)
**L1. §10 «Данные, i18n...» не называет конкретный текст/ключ новой imperial-
подписи внутри самого i18n-раздела** — формулировка «новые EN/RU строки нужны
для imperial unit label» не самодостаточна. Текст фактически дан двумя
разделами ниже, в §9.2 («дюйм на клетку» / `in per cell`), так что
неоднозначности для реализации нет — но это расходится с конвенцией
остальных 239 спеков репозитория (например, #157: «`opening.passage`,
`opening.passage_binding_warning`»; #167: «label «Plan only»»; #094:
«`tap.toggle`: «Переключить состояние»»), где i18n-раздел сам перечисляет
строки, а не отсылает к UX-разделу. Снимаю как Low: содержательно текст уже
в документе, вопрос чисто в консолидации. Автор может перенести/продублировать
строку в §10 попутно с фиксом M1 либо оставить как есть.
## Что проверено и корректно
- **Продуктовая рамка.** Сценарий и «что человек увидит» — обе продуктовые
секции §7.1 присутствуют первыми, отвечают на «какая персона / какая
поверхность / какой момент» и не используют термины реализации. Задача
закрывает J4/J6 из `docs/SCOPE.md`, конфликтов с out-of-scope нет.
- **Ни одной догадки, выданной за факт.** Каждое диагностическое утверждение
§3 («что уже масштабируется правильно» / «где инвариант нарушен») проверено
построчно по коду (см. «Как проверялось», п.4) и совпадает буквально, а не
на уровне пересказа. Технические решения (§19) явно помечены как «assumed,
change freely» и не выданы за продуктовое решение.
- **Продуктовых вопросов владельцу нет, и это оправдано.** Единственный
потенциально продуктовый вопрос — «должен ли визуально измениться уже
существующий сохранённый план с `cell_cm ≠ 5` после фикса» — уже
разрешён самим текстом issue («Нужно проверить и при необходимости
нормализовать весь визуальный контракт»): это и есть цель бага, не
побочный эффект, и AC13/§17 changelog корректно фиксируют это как
осознанное, объявленное в changelog поведение, а не тихую перерисовку.
- **Скоуп/не-скоуп непротиворечивы.** §5.2 явно запрещает менять
`GRID_N`/`GRID_PITCH`/snap-алгоритм и migration существующих `cell_cm` —
это ровно то, что держит задачу в границах «визуальный слой», не
«координатная модель», и совпадает с диапазоном 0.1…1000, подтверждённым в
коде.
- **Классификация размеров (§6) внутренне непротиворечива** и предотвращает
ровно ту ошибку, которую называет главным риском сам автор (общий CSS/SVG
transform повторно умножил бы уже верные physical/plan-relative пути) —
проверено алгеброй на `iconCqw`/`iconUnit` (см. «Как проверялось», п.4).
- **AC1–AC12 однозначны и алгебраически проверяемы** без реализации:
например AC1 (`gridVisualScale(5) === 1`, `5/cellCm` для остальных, `1` для
невалидных), AC5 (physical-контроль не должен домножаться второй раз — уже
сформулирован как мутационный тест, а не как описание), AC9 (canonical
`2.54` для imperial — точное значение 1 дюйма, не приближение).
- **Откат (§18) и release-артефакты (§17) полны**: один revert коммита,
явное заявление «данные не мигрируют и остаются валидными и после отката»,
оба changelog, оба USER-GUIDE, CANVAS.md, ARCHITECTURE.md, TESTING.md и
пересъёмка public docs screenshot — ничего из обязательного набора не
забыто.
- **Риски (§16) содержательны, не общие слова**: каждая строка называет
конкретный механизм провала (например, «px/unitless `calc()` в SVG
вычислится не в те units») и конкретную защиту, без карго-культных «будем
осторожны».
- **Гейт docs зелёный**: `node scripts/check-docs.mjs` → `Documentation
checks passed (7 files, 10 external links)`.
## Чего не проверял
- `npx tsc --noEmit`, `npm test`, `npm run build` — не запускал: диапазон
диффа не содержит файлов класса A/B (только `docs/specs/239-*.md` и строка
реестра), эти гейты не имеют предмета проверки на этом этапе и их прогон на
чистой документации был бы потерей времени, а не тщательностью.
- Реализацию классов Physical/Screen/Plan-relative/Visual/Grid — кода ещё
нет, оценивалась только логическая непротиворечивость классификации, не её
будущее исполнение.
- Полный текст остальных ~470 существующих ТЗ репозитория — выбраны только
#230 (единственный прямой предок контракта) и #238 (непосредственно
предыдущая задача в очереди, тот же автор) как контрольные образцы
конвенции «AC → доказательство»; это не полная статистика по всем спекам,
а два целевых прецедента, достаточных для находки M1.
- Golden/browser smoke/performance — не запускались и не должны: реализации
нет, эти гейты относятся к S6/S7 и предрелизному прогону (PROCESS.md §8),
не к ревью ТЗ.