Files
houseplan-card/docs/reviews/SPEC-REVIEW-462-r3.md
T
2026-09-05 16:00:54 +03:00

237 lines
21 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-462-r3
- **Issue:** https://github.com/Matysh/houseplan-card/issues/462
- **Спецификация:** `docs/specs/462-card-resource-registration.md`, коммит `b45c3464`
(ветка `issue/462-card-resource-registration`, HEAD на момент ревью — тот же SHA)
- **Трек:** полный
- **Заход:** r3 · блокирующих циклов израсходовано 2 из 4 (лимит на полном треке — 4;
зелёный вердикт этого раунда бюджет не тратит, #227)
## Материал раунда r2 (для дельты)
- Предыдущий вердикт: красный · заход r2 · High: 1 · Medium: 0 → в задаче
(комментарий issue, 2026-09-05T11:22:44Z), документ
`docs/reviews/SPEC-REVIEW-462-r2.md`.
- SHA материала r2 назван в документе дважды (шапка и «Материал раунда»):
`e9e3d6bd7d8e61a0e7d561da76b19cedb157f32b`. Резолвится и сейчас как предок
`HEAD` (`git merge-base --is-ancestor e9e3d6bd HEAD` → успех) — ребейза не
было, восстановление по дереву/блобу не требовалось.
- Дельта этого раунда: `git diff e9e3d6bd..HEAD -- docs/specs/462-card-resource-registration.md`,
два коммита: `c56afbd3` («docs: cite Home Assistant resource mode contract» —
ответ на High r2) и `b45c3464` («docs: cover native more-info reload guard» —
самостоятельно найденный автором до кода дополнительный unsafe-path в §11.3).
40 изменённых строк, пять точек правки: §3 (новое обоснование версионной
границы), §11.3 (расширение guard-условия про native more-info), §12 (те же
ссылки в тексте документации), AC8 (§15) и план тестов (§16).
Дельта не вводит новый терминологический контракт и не задевает подсистему,
которую не разбирал r2: она (а) подкрепляет источниками уже существующее в r2
разделение storage/YAML-resource-mode и (б) уточняет один из guard-предикатов
kiosk-перезагрузки, не меняя ни матрицу §11.1, ни список § заявленных outcome,
ни какой-либо другой AC. Оба места делта касается напрямую (AC1/§12 через
источники; AC8/§11.3/§16 через native more-info) — только эти два узла и
проверялись заново по существу; остальное наследуется из r2.
## Скоуп ревью
Прочитано перед разбором: `docs/SCOPE.md` (Core user jobs, J4), `AGENTS.md`,
`PROCESS.md` §1–10 и §2.10 (объём по дельте), тело issue #462 и все 13
комментариев (включая комментарии автора о закрытии High через PR HA и о новом
guard native more-info), документы и находки `SPEC-REVIEW-462-r1.md` и
`SPEC-REVIEW-462-r2.md`, `docs/UX-MODES.md` (Kiosk mode), `docs/TOUCH-SUPPORT.md`.
Из самого ТЗ перечитаны §3, §11.3, §12, AC8, §16 целиком; остальные разделы —
по диффу и по унаследованному выводу r1/r2 (см. «Унаследовано из r2»).
## Как проверялось
В этой сессии, в отличие от r1 и r2, `WebFetch` (по прямым URL) отказал тем же
`permission denied`, но `gh api` (через `Bash`) сработал без ограничений и дал
прямой доступ к GitHub REST API `home-assistant/core` — то есть найденный в r1/r2
пробел («не подтверждено — WebSearch/WebFetch недоступны») в этом раунде закрыт
не доверием к цитате автора, а независимой проверкой того же источника другим
инструментом.
1. **Проверка PR, закрывшего High r2.**
`gh api repos/home-assistant/core/pulls/161816` — существует, `merged: true`,
`merge_commit_sha: 190fe10eed42d6ed5122bf873e61986851bc0b15` (совпадает с
сокращённым `190fe10` из ТЗ), `milestone.title: "2026.2.0"` (совпадает с
заявленным в ТЗ), заголовок «Allow lovelace path for dashboard in yaml and fix
yaml dashboard migration», описание автора PR дословно: «Introduce a new key :
`resource_mode` to replace `mode` so it's clear that it's only for resources.» —
то есть первоисточник подтверждает именно то утверждение, которое ТЗ
приписывает ему, а не общую формулировку не по делу.
2. **Проверка исходника на теге, где утверждается контракт.**
`gh api repos/home-assistant/core/contents/homeassistant/components/lovelace/__init__.py?ref=2026.2.0`
(сохранён локально, прочитан полностью). Подтверждено дословно:
- `CONFIG_SCHEMA` домена `lovelace` содержит `vol.Optional(CONF_MODE, default=MODE_STORAGE)`
**и отдельно** `vol.Optional(CONF_RESOURCE_MODE)` — два независимых
необязательных ключа, не один "mode" как было в 2025.1.4 (r2 проверял ровно
эту версию и не нашёл там `resource_mode` — версии до и после границы
действительно различаются, как и утверждает ТЗ);
- `resource_mode = config[DOMAIN].get(CONF_RESOURCE_MODE, mode)` — при
отсутствии `resource_mode` поведение полностью откатывается к старому
единому `mode` (обратная совместимость сохранена, ТЗ этого не утверждает
явно, но и не противоречит);
- `if resource_mode == MODE_YAML: resource_collection = await create_yaml_resource_col(...)`
— YAML-ресурсы загружаются по значению `resource_mode`, **независимо** от
того, что находится в `mode` (то есть независимо от режима dashboard) — это
дословно тот функциональный контракт, который ТЗ описывает в §3/§5/§12/AC1;
- ветка `else` (когда `resource_mode != yaml`, т.е. storage) сохраняет старый
`_LOGGER.warning("Lovelace is running in storage mode. Define resources via
user interface")` при непустых `yaml_resources` — то есть для HA без
`resource_mode: yaml` (в том числе для версий до 2026.2, где ключа вообще
нет) исходная посылка issue («в storage-режиме `lovelace.resources` из YAML
игнорируется целиком») остаётся верной, и ТЗ её не отменяет — оно вводит
доп. путь **только** для новых версий, что и обещает.
3. **Проверка риска "чужой ключ ломает валидацию"**, которым r2 обосновывал
серьёзность High. Внутренний `vol.Schema({...})` для домена `lovelace` не
объявляет `extra=vol.ALLOW_EXTRA` (только внешний `CONFIG_SCHEMA` — это
касается ключей вне `lovelace:`, не внутри него) — значит на версиях **до**
2026.2, где `resource_mode` не входит в схему, этот ключ до сих пор обвалил бы
валидацию `lovelace:`. Проверил формулировку ТЗ (§12, «Legacy HA
2024.6–2026.1»): современный snippet с `resource_mode` предлагается **только**
для «HA 2026.2+», отдельная explicitly помеченная legacy-секция без этого
ключа — для старых версий. Риск, которым r2 блокировал находку, ТЗ
структурно исключает версионным разделением, а не только цитатой.
4. **Проверка дельты по native more-info guard (§11.3, AC8).** Прочитан
`src/houseplan-card.ts`: `_openMoreInfo` (строка 5565) — единственная точка
вызова `fireEvent(this, 'hass-more-info', …)` в файле; все places, открывающие
more-info (right-click `view`-стейджа `:5618`, action handler `:5746-5747`,
кнопки info-card `:12402`, `:13334`, `:13376`), проходят через неё. `grep -n
_cyclePausedUntil` показывает единственную точку записи паузы —
`_stagePointerDown` (`:6691`), и только при `this._kiosk`, только на
pointerdown по сцене. Кнопки info-card на `:12402` делают
`e.stopPropagation()` перед вызовом `_openMoreInfo`, то есть их клик не
обязан долетать до обработчика сцены; клавиатурный и «внутренний
программный» пути тем более не проходят через `pointerdown`. Значит
формулировка ТЗ «одной паузы из stage pointerdown недостаточно» и «единая
точка открытия more-info обязана продлить `_cyclePausedUntil`» — не догадка,
а точное описание существующей архитектуры и корректно найденная дыра:
единственная уже существующая точка сведения (`_openMoreInfo`) —
реалистичное место для реализации, что соответствует §17 («точные helper
names остаются за реализацией»).
5. **Согласованность правки по всем пяти точкам делты** (§3, §11.3, §12, AC8,
§16 «Browser smoke/golden») — не осталось места, где старая формулировка
(«открытие уже ставит общую interaction pause») продолжала бы жить рядом с
новой; численно проверено, что заново введённая фраза одна и та же по
смыслу во всех пяти местах.
6. Не прогонялись `typecheck`/`test`/`build` — этап spec, кода нет; они
применимы к код-ревью (§2.7 `PROCESS.md`), не к этому этапу.
## Находки
Ни одной High- или Medium-находки в делте `e9e3d6bd..HEAD` не обнаружено.
## Закрытие раунда r2
| Находка r2 | Чем закрыта | Где это видно |
|---|---|---|
| High: контракт `lovelace.resource_mode: yaml` для «HA 2026.2+» не подтверждён, противоречит доступной версии HA (2025.1.4) и собственной посылке issue; риск сломать `lovelace:` валидацией на старых HA | Первичные источники добавлены в ТЗ (PR, merge-коммит, тег, офиц. документация) и **независимо перепроверены в этом раунде напрямую через `gh api`** против реального исходника `homeassistant/components/lovelace/__init__.py` на теге `2026.2.0`: `CONF_RESOURCE_MODE` — реальный, отдельный от `CONF_MODE` optional-ключ схемы; `resource_mode == MODE_YAML` грузит YAML-ресурсы независимо от `mode` дашборда; версии до 2026.2 (без ключа) по-прежнему получают только legacy-snippet — риск валидации закрыт версионным разделением, а не игнорированием | `docs/specs/462-card-resource-registration.md` §3 (строки 75–93), §12 (398–436), AC1 (468–475); коммит `c56afbd3`; см. «Как проверялось» пп.1–3 этого документа |
Единственная находка r2 закрыта по существу и подтверждена независимо, не на
слово автора. Новых находок делта не внесла.
## Унаследовано из r2 (без повторной проверки в этом раунде)
Следующее не тронуто дельтой `e9e3d6bd..HEAD` за пределами описанного выше и
принимается по документам `docs/reviews/SPEC-REVIEW-462-r1.md` (SHA материала
`12ddd107a9cdd9b12ea8411360760f684f5293c1`) и
`docs/reviews/SPEC-REVIEW-462-r2.md` (SHA материала `e9e3d6bd7d8e61a0e7d561da76b19cedb157f32b`):
- Наличие и содержание обязательных разделов §7.1 `PROCESS.md` (сценарий, что
человек увидит, причина, скоуп/не-скоуп, контракт §8–11, данные/миграция,
i18n, AC1…AC13, план тестов, риски, откат, release-артефакты, §22).
- Закрытие обеих находок r1 (баннер vs terminal-тост #353; touch-target 44×44
как локальный, не канонный порог) — таблица «Закрытие раунда r1» в
`SPEC-REVIEW-462-r2.md`; дельта этого раунда их не касалась.
- Существование и смысл символов кода, проверенных в r1/r2:
`_register_lovelace_resource`/`_lovelace_resources`
(`custom_components/houseplan/__init__.py:336-378`), `manifest.json`
(dependencies без `lovelace`), `system_health_info`, `CARD_VERSION`
(`src/houseplan-card.ts:402`), `integration_version` в `config/get`,
`_preflightVersionsDiffer`, `persistent_notification.async_create` как
синхронный `@callback`, card-level overlay в трёх ранних render-ветках
(`src/houseplan-card.ts:11282-11320`).
- Подтверждённая по реальным wheel `homeassistant==2024.6.0` и `==2025.1.4`
цепочка зависимостей `houseplan → frontend → lovelace` и отсутствие
`remove_extra_js_url` в 2024.6 (§8.2, п.9 и абзац после).
- Пригодность `homeassistant.helpers.start.async_at_started` в связке с
`entry.async_on_unload` (§22 п.3).
- Полный список из 4 файлов документации с исходным невалидным плоским
`resources:` (README.md, README.ru.md, docs/USER-GUIDE.md,
docs/USER-GUIDE.ru.md) и обоснование выбора `persistent_notification` вместо
Repairs (§22 п.1).
- Соответствие `docs/SCOPE.md`: задача закрывает J4, out-of-scope не задет.
- AC2–AC7, AC9–AC13 — формулировки не менялись делтой `e9e3d6bd..HEAD` (сверено
построчно диффом); принимаю их однозначность и доказуемость как установленную
в r1/r2. AC1 и AC8 менялись — перепроверены заново в этом раунде (см. «Как
проверялось» пп.1–4 и «Закрытие раунда r2»).
## Что проверено и корректно (в этом раунде)
- Источники для `lovelace.resource_mode` — реальны, дают именно то
утверждение, которое им приписано, и независимо подтверждены против
исходного кода HA на названном теге, а не только против цитаты автора.
- Версионное разделение snippet'ов (2026.2+ / legacy 2024.6–2026.1) структурно
исключает риск, из-за которого High r2 был серьёзным (обвал валидации
`lovelace:` на старых HA неизвестным ключом): новый ключ предлагается только
там, где он существует в схеме.
- Новое guard-условие native more-info (§11.3/AC8) описывает существующую,
проверенную по коду архитектуру (`_openMoreInfo` как единая точка сведения,
`_cyclePausedUntil` как единственная точка паузы только из
`_stagePointerDown`), а не гипотетический символ; реализуемо без новой
неоднозначности в AC8 и плане тестов.
- Правка внесена согласованно во всех пяти местах, которые она логически
затрагивает (§3, §11.3, §12, AC8, §16) — старая и новая формулировка нигде не
остались одновременно.
- AC1 и AC8 после делты остаются однозначными и называют способ доказательства
(`unit`/docs contract для AC1; `unit + browser + mutation` для AC8).
## Чего не проверял
- Не проверял версии HA новее `2026.2.0` — не требуется: делта именно
зафиксировала версионную границу на этом теге, дальнейшие версии вне спора.
- Не проверял официальную документацию `home-assistant.io/dashboards/dashboards/`
напрямую (WebFetch по прямому URL отказал тем же `permission denied`, что и в
r1/r2) — не потребовалось: первичный источник (PR + исходник тега) сильнее
вторичного (документация) и уже даёт исчерпывающее прямое подтверждение.
- Не прогонял `typecheck`/`test`/`build` — этап spec, кода ещё нет; гейты
применимы к код-ревью, не к этому этапу.
- Не переоткрывал AC2–AC7, AC9–AC13, §8–10, §13–15, §17–22 по существу — делта
их не касалась (см. «Унаследовано из r2»).
- Не оценивал landing page/дистрибуцию — вне репозитория, зона Claude по
`AGENTS.md`, не часть ТЗ.
## Вывод
Единственная блокирующая находка r2 закрыта по существу и подтверждена
независимой проверкой первичного источника, а не принята на слово. Делта
сверх этого (native more-info reload guard) — корректное, реализуемое и
согласованное уточнение существующего требования, без новых High/Medium.
Задача готова к разработке.
## Материал раунда
- Ветка: `issue/462-card-resource-registration`
- SHA материала: `b45c3464ca1847b5f9041bdc277a0c01de49eb19`
- Дерево материала: `d8d3c0f0dd8cfabad9e9cb583142bda2a8f50ddb`
- Файл ТЗ: `docs/specs/462-card-resource-registration.md`, блоб
`d3620eab48950255aa91240cd70ec3ed1edfd756`
- SHA r2 (для дельты): `e9e3d6bd7d8e61a0e7d561da76b19cedb157f32b`
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/462-card-resource-registration`, коммит `b45c3464ca18` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `d8d3c0f0dd8cfabad9e9cb583142bda2a8f50ddb`
```
git log --all --format='%H %T' | grep d8d3c0f0dd8c
```
- ТЗ `docs/specs/462-card-resource-registration.md`, блоб `d3620eab48950255aa91240cd70ec3ed1edfd756`
```
git log --all --find-object=d3620eab48950255aa91240cd70ec3ed1edfd756 -- docs/specs/462-card-resource-registration.md
```