docs: review document for #462

Issue: #462
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-05 16:00:54 +03:00
committed by Matysh
parent 248c926a17
commit a4fe939f63
+236
View File
@@ -0,0 +1,236 @@
# 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
```