Files
houseplan-card/docs/reviews/SPEC-REVIEW-374-r1.md
T
2026-08-29 11:14:50 +00:00

187 lines
16 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-374-r1
Issue: [#374](https://github.com/Matysh/houseplan-card/issues/374) — Feature request: optional light pools and wall shadows in `houseplan-space-card`
ТЗ: [`docs/specs/374-space-card-light-pools.md`](https://github.com/Matysh/houseplan-card/blob/issue/374-space-card-light-pools/docs/specs/374-space-card-light-pools.md)
Ветка: `issue/374-space-card-light-pools`, SHA материала ревью: `c4f37986`
Заход: r1 · блокирующих циклов израсходовано 0 из 4
## Скоуп ревью
Дифф ветки против `origin/dev`:
```
docs/specs/374-space-card-light-pools.md | 434 +++++++++++++++++++++++++++++++
1 file changed, 434 insertions(+)
```
Только новый файл ТЗ, продуктовый код не тронут. Трек — полный (не `small`,
не `trivial`): лейблы issue — `P2`, `feature`, `S4-spec-review`, признака
лёгкого трека нет; аналитика в issue явно называет причину («появляется новый
публичный конфигурационный контракт, затрагиваются производительность и
touch/View, а каноническую световую модель надо разделить между двумя
рендерами»), что корректно по критериям §5 PROCESS.md (сложность/риск 8/10 —
далеко за порогом ≤3). Файл ТЗ в `docs/specs/374-*.md` — верное место.
## Как проверялось
1. Прочитан `docs/SCOPE.md` — сверена персона и job (J1), закрытая задача.
2. Прочитаны `AGENTS.md`, `PROCESS.md` целиком (жизненный цикл, §5 критерии
трека, §7.1 обязательные разделы ТЗ).
3. Прочитано тело issue #374, все комментарии (аналитика + «ТЗ готово»), и
связанный закрытый документационный issue #370 с ответом владельца —
источник контекста, откуда взялся запрос.
4. Прочитан `docs/LIGHT.md` целиком — канон световой модели, включая раздел
«Which surfaces render pools», прямо описывающий текущее ограничение
static card, которое ТЗ снимает.
5. Прочитан `docs/USER-GUIDE.ru.md` (раздел про `live_states`) для сверки
терминологии.
6. Прочитан сам ТЗ целиком (435 строк).
7. Технические утверждения ТЗ о **текущем** поведении кода сверены с
исходниками, а не приняты на слово:
- `resolvedLightSources`, `selectSpatialGlowSource`, `resolveGlowAppearance`,
`glowAlpha`, `GLOW_FALLOFF` — существуют в `src/devices.ts`,
`src/logic.ts`, `src/houseplan-card.ts` ровно с той сигнатурой
использования, что описана в контракте (раздел «Источники и видимость»).
- Независимость Glow от `live_states` (`_renderGlowLayer` не читает
`live_states`, `live_states === false` гасит только
`_syncActivityRuntime`/`_activityRuntime`, т.е. иконки/пульс, но не сам
слой pool) — проверено чтением `src/houseplan-card.ts:10500-10589` и
`src/space-card.ts:363-467`. Совпадает с заявлением ТЗ п.2.5.
- Порядок слоёв («room/tunnel fills → Glow base/tunnels → radial pools →
стены/openings → labels/devices») — сверен с фактическим порядком
`svg` в `_renderPlanBody`/аналоге (`src/houseplan-card.ts:11290-11317`):
room fills → opening tunnel fills → Glow base → decor → `_renderGlowLayer`
→ sun rays → zero walls → wall bodies. Совпадает.
- Кеш-ключи (`_glowClipCache` = `space.id|fingerprint|pos|R`, barrier по
geometry fingerprint + квантованной сигнатуре проёмов) — сверены с
`src/houseplan-card.ts:10586-10588` и с разделом «Caching» `docs/LIGHT.md`.
Совпадает дословно.
- Утверждение «static renderer получает marker positions в plan
coordinates до процентного преобразования devlayer» — сверено:
`src/space-geometry.ts` строит модели в `NORM_W`×`NORM_W` (план-
координаты), а `src/space-render.ts:399,435` конвертирует их в
`left:%`/`top:%` только в момент рендера HTML-оверлея маркеров. Значит
план-координаты действительно доступны раньше процентного шага —
утверждение точное, не догадка.
- Поведение неизвестного `space` («сохраняет текущую error card», AC1) —
сверено с `src/space-card.ts:812` (`_errorCard(t(..., 'space_card.not_found'...))`).
Существует уже сейчас.
- Все файлы, упомянутые в «Затронутые файлы» и «План автотестов»
(`demo/smoke_space_card.mjs`, `demo/performance/budgets-large-house-glow-overlay.json`,
`src/i18n/{en,ru,de,fr}.json` с параллельными ключами `editor.live_states`
как образец конвенции) — существуют.
8. Проверено отсутствие открытых продуктовых вопросов и незакрытых меток
неопределённости (`?`, «уточнить», TBD) в тексте ТЗ — не найдено ни одной
вне markdown-разметки типа `light_pools?: boolean`.
## Гейты
Стадия — ревью ТЗ, продуктовый код в диффе отсутствует. `typecheck`/`test`/
`build`/`check-docs`/смоки/golden/performance/invariants — гейты код-ревью
(§8 PROCESS.md), к спек-ревью неприменимы и не запускались. Единственная
проверка этой стадии — чтение и сверка ТЗ с кодом и канонами, что сделано в
разделе выше.
## Находки
Ни одной блокирующей (High) или Medium-находки, в скоупе или вне скоупа, не
обнаружено.
Рассмотренные, но не подтвердившиеся кандидаты (для прозрачности разбора):
- **AC3 явно не называет «room draft» как отдельный оккludер**, хотя раздел
«Скоуп» и канон `docs/LIGHT.md` перечисляют draft отдельно. Проверка:
`docs/LIGHT.md` группирует draft вместе с partition как один класс
«independent bodies», геометрически объединяемых в одно тело перед
visibility sweep. AC3 и план автотестов покрывают «partition», которое по
канону механически идентично draft. Не находка — терминологическое
сокращение, а не пробел контракта.
- **Термин «semantic pixel witness»** в доказательствах AC2–AC4 не определён
в `docs/TESTING.md` как канонический термин проекта — он введён и уже
использовался автором в предыдущем спеке (`372-space-card-empty-title.md`).
В этом же документе термин раскрыт операционально («План автотестов» п.5–6:
конкретные точки сэмплинга пикселей, свет/тёмная тема, DPR/ширины). Не
находка — определён внутри документа, а не оставлен голой ссылкой на
неписаный процесс.
- **Дублирование места переключателя в UI** («UX и доступность»: «рядом с
live-state visual settings» вместо точного «рядом с `live_states`»,
уточнённого только в блоке «Принято предположительно») — не противоречие,
второе уточняет первое, оба текста согласуются.
## Что проверено и корректно
- Оба обязательных продуктовых раздела (§7.1) — «Сценарий» и «Что человек
увидит до и после» — называют персону из `docs/SCOPE.md` (домочадец/гость-
киоск), поверхность (`houseplan-space-card` как основной визуальный элемент
дашборда) и однозначно формулируют видимое изменение без терминов
реализации.
- Задача укладывается в J1 `docs/SCOPE.md` — прямое усиление «Show the whole
home and what's happening right now» для отдельной карточки. Не пересекает
«Out of scope» (не sunlight/window wedges — прямо исключено в «Не-скоуп» со
ссылкой на независимость `docs/SUN.md`; не 3D/интерьер; не automations).
- Все 13 обязательных разделов ТЗ по §7.1 присутствуют: сценарий · что
увидит · проблема · скоуп/не-скоуп · контракт поведения · UX · модель
данных и миграция · i18n · AC1…AC10 с доказательством · план автотестов ·
риски · откат · release-артефакты. Плюс необязательные, но полезные:
архитектурный контракт, перф/bundle, touch/темы/размеры, затронутые файлы,
блок принятых предположений.
- Каждый AC (AC1…AC10) сформулирован как проверяемое утверждение и несёт
явный способ доказательства (`unit`/`browser smoke`/`golden`/`performance
artifact`/`CI checks`/«ревью кода» через ссылку на п.5 архитектурного
контракта). Ни одного AC без названного способа проверки.
- Продуктовых вопросов владельцу не осталось — комментарий аналитики прямо
фиксирует это, и текст ТЗ подтверждает: единственная развилка
(`light_pools` vs `show_light_shadows`) закрыта решением в пользу одного
канонического ключа, без alias.
- Технические решения, не наблюдаемые пользователем (имена shared-модулей,
форма runtime API, конкретный smoke-фикстур, точные raster tolerances,
место переключателя в форме редактора), явно вынесены в блок «Принято
предположительно, поменять свободно» — именно туда, где им место по §7.1,
а не разбросаны по тексту как скрытые решения.
- Не-скоуп корректно отсекает соседние риски: миграцию существующих карточек,
визуальные изменения полной карточки, деградацию/лимиты источников
(запрещены явно — важно, т.к. issue упоминал «возможно с warning», ТЗ не
поддалось соблазну добавить скрытую деградацию), редакторы геометрии,
повышение бюджетов без отдельного решения.
- Архитектурный контракт формулирует пять проверяемых инвариантов извлечения
общего модуля (нет DOM/hass/приватных полей в shared-модуле, no module-level
unbounded cache, full-card output regression-equivalent, единый источник
истины по aperture classification/visibility/source guard/falloff/SVG field)
— ровно то, что нужно ревьюеру кода на код-ревью, без диктовки конкретных
имён файлов (эти имена сознательно оставлены реализации).
- i18n-раздел соответствует существующей конвенции проекта (`editor.<key>` во
всех четырёх словарях, без английского fallback для de/fr).
- Откат описан на двух уровнях (пользовательский — снять флаг/false; кодовый
— раздельный откат static-адаптера и shared-extraction) с явным критерием,
когда откатывается всё целиком.
- Риски (8 штук) названы конкретно и с указанием, чем каждый снижается в
самом контракте, а не общими словами.
## Чего не проверял
- Реализуемость перформанс-цели (AC7) на практике — на этой стадии нет кода,
профиль `large-space-card-glow-overlay-v1` кандидатный и явно помечен как
подлежащий уточнению в реализации; численные бюджеты по правилу процесса
утверждаются только полным Linux performance artifact, не на спек-ревью.
- Точные имена shared-модулей и границы API — сознательно оставлены
реализации согласно блоку «Принято предположительно», ревью не оспаривает.
- Гейты код-ревью (`typecheck`, `test`, `build`, `check-docs`, смоки, golden,
performance, `model-invariants`) — неприменимы на этой стадии, кода нет.
## Унаследовано из r0
Не применимо — это первый заход (r1), предыдущего цикла нет.
## Вердикт
Полный, детально проработанный ТЗ без блокирующих или Medium-находок.
Технические утверждения о текущем поведении кода выборочно, но
представительно сверены с исходниками и каноном (`docs/LIGHT.md`) и во всех
проверенных случаях подтвердились — включая нетривиальные детали вроде
точных ключей кэша и независимости Glow от `live_states`. Продуктовые
разделы отвечают на оба обязательных вопроса §7.1, открытых вопросов
владельцу не осталось, технические развилки корректно вынесены в блок
предположений вместо того, чтобы быть выданными за решённые факты.
**Вердикт: зелёный.**