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

290 lines
25 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-r2
- **Issue:** https://github.com/Matysh/houseplan-card/issues/462
- **Спецификация:** `docs/specs/462-card-resource-registration.md`, коммит `e9e3d6bd`
(ветка `issue/462-card-resource-registration`, HEAD на момент ревью — тот же SHA)
- **Трек:** полный
- **Заход:** r2 · блокирующих циклов израсходовано 1 из 4 (лимит на полном треке — 4)
## Материал раунда r1 (для дельты)
- Предыдущий вердикт: жёлтый · заход r1 · High: 0 · Medium: 1 → в задаче
(комментарий issue, 2026-09-05T11:03:23Z).
- **SHA в самом вердикте (комментарии issue) не назван** — это находка процесса
фиксации, отдельно от содержательного разбора (см. «Находки», Low-2). SHA
восстановлен из документа `docs/reviews/SPEC-REVIEW-462-r1.md`, где он назван
явно: `12ddd107a9cdd9b12ea8411360760f684f5293c1`, и подтверждён порядком коммитов
(`910eedb4 docs: review document for #462` — публикация документа r1 —
следует сразу за `12ddd107`, до `8973431f`, который отвечает на находку r1).
- Дельта этого раунда: `git diff 12ddd107..e9e3d6bd -- docs/specs/462-card-resource-registration.md`
(151 изменённая строка), двумя коммитами:
`8973431f` («Замечание SPEC-REVIEW r1 исправлено» + новый YAML resource_mode
контракт + источник touch-target) и `e9e3d6bd` («устранены технические
двусмысленности» — retry timing/cancellation, нормализация версии, translations
category, card-level overlay в ранних render-states, kiosk-guard дополнения).
Дельта не локальна: она вводит новый терминологический контракт поведения HA
(«YAML resources mode» vs «Legacy full-YAML dashboard mode», см. ниже) и меняет
формулировку четырёх AC (AC1, AC3, AC12, AC13) плюс план тестов и риски. По
правилу «смена контракта поведения → полный разбор» я перечитал документ
целиком (§1–22) и проверил новые технические утверждения по коду HA, а не
только территорию, формально задетую диффом.
## Скоуп ревью
Прочитано перед разбором: `docs/SCOPE.md` (Core user jobs, J4), `AGENTS.md`,
`PROCESS.md` §1–10, тело issue #462 и все 8 комментариев (включая решение
владельца о тихой kiosk-перезагрузке и аналитику 2026-09-05), документ и
находки `SPEC-REVIEW-462-r1.md`, `docs/UX-MODES.md` (Kiosk mode),
`docs/TOUCH-SUPPORT.md`. Сам документ ТЗ — целиком, §1–22.
## Как проверялось
Ревью состязательное: issue + ТЗ + код `dev` + прямая проверка технических
утверждений о платформе HA, без доверия формулировке «по официальному контракту
HA» на слово (именно так был подписан коммит `8973431f`).
1. **Материал среды.** `pip index versions homeassistant` в этом окружении
зеркалирует PyPI и обрывается на `2025.1.4` (последняя доступная версия) —
то же ограничение уже зафиксировал r1 («не проверялся HA 2026.8 напрямую»).
Скачан `homeassistant==2025.1.4` (`pip download --no-deps`) и прочитан исходник
компонента `lovelace`.
2. **Ключевая новая находка.** `homeassistant/components/lovelace/__init__.py`
(2025.1.4) содержит единственный `CONFIG_SCHEMA` для домена `lovelace`:
```python
vol.Optional(CONF_MODE, default=MODE_STORAGE): vol.All(vol.Lower, vol.In([MODE_YAML, MODE_STORAGE])),
vol.Optional(CONF_DASHBOARDS): ...,
vol.Optional(CONF_RESOURCES): [RESOURCE_SCHEMA],
```
Один `mode` одновременно решает: (а) в YAML ли dashboard, (б) читаются ли
`resources:` из YAML. В ветке `else` (mode != yaml, т.е. storage — умолчание)
код явно делает `_LOGGER.warning("Lovelace is running in storage mode. Define
resources via user interface")`, если `yaml_resources is not None` — то есть
**подтверждает**, а не опровергает исходную посылку issue («в storage-режиме
`lovelace.resources` из YAML игнорируется целиком»). Отдельного ключа
`resource_mode`, который расцеплял бы «источник ресурсов» и «режим dashboard»,
в схеме нет. `grep -r resource_mode` по всему распакованному пакету (`.py`,
все файлы) — ноль совпадений. `grep -rn resource_mode` по всему репозиторию
houseplan-card вне `docs/specs/462-*.md` — тоже ноль: концепция не
встречается больше нигде в кодовой базе, документации или прежних раундах.
3. **Попытка проверить внешний источник.** `WebSearch`/`WebFetch` недоступны в
этой сессии («haven't granted it yet» на оба вызова, дважды). Верификация
ограничена локальным кодом платформы и внутренней согласованностью
документа — этого достаточно, чтобы поднять находку, но не достаточно, чтобы
на 100% исключить более поздний upstream-релиз (ниже, «Чего не проверял»).
4. **Проверка синхронности `persistent_notification.async_create`.** §9 теперь
утверждает: флаг «уведомление создано» пишется «сразу после того, как
синхронный callback `persistent_notification.async_create` вернулся без
исключения». Подтверждено в исходнике 2025.1.4:
`homeassistant/components/persistent_notification/__init__.py` — функция
помечена `@callback` (не `async def`, не корутина), тело — синхронная запись
в dict + `async_dispatcher_send`, реальных исключений в штатном пути нет.
Формулировка точна.
5. **Проверка card-level overlay в ранних render-состояниях (§11.2).**
`src/houseplan-card.ts:11282-11320` — реальность подтверждена: `fixed.kind
=== 'pending'`, `'invalid'` и `!model.length` действительно возвращают три
независимых `<ha-card>...</ha-card>` шаблона до основной сцены. Требование
«отдельные копии разметки в render-ветках не становятся независимыми
состояниями» — не выдумка, а корректный ответ на реальную структуру кода;
без него легко забыть плашку в одной из трёх веток.
6. **Проверка закрытия Medium-находки r1** (баннер против тоста #353) — по
тексту нового §11.2 и обновлённых AC12/§16 (ниже, «Закрытие раунда r1»).
7. **Проверка закрытия Low-находки r1** (число 44×44 без источника) — §22 п.8.
## Находки
### High — новый контракт `lovelace.resource_mode: yaml` не подтверждён и
### противоречит и коду HA, и собственной посылке issue
Введено в этом раунде (`8973431f`, отсутствовало в редакции r1). §5, §12, AC1 и
план тестов (§16 «Docs/mutation») теперь утверждают, что в «HA 2026.2+»
`lovelace.resource_mode: yaml` — независимый от `lovelace.mode` ключ, который
подключает YAML-ресурсы к storage-managed dashboard. Формулировка прямая,
без оговорки «предположение»: «Нельзя утверждать, что `lovelace.resources`
всегда игнорируется storage-managed dashboard: в HA 2026.2+ именно
`resource_mode: yaml` поддерживает YAML-ресурсы независимо от режима
dashboard» (docs/specs/462-card-resource-registration.md:405-408). Коммит
`8973431f` в issue подписан «по официальному контракту HA» — то есть подан как
проверенный факт, не как гипотеза.
Что не сходится:
- В единственной доступной для проверки версии HA (`2025.1.4`, актуальная в
индексе pip этого окружения) ключа `resource_mode` не существует нигде —
ни в `lovelace`, ни где-либо ещё в пакете. Единственный `mode` управляет и
dashboard, и тем, читаются ли `resources:` из YAML.
- Это прямо противоречит тому, что сама схема делает сегодня: `resources:` под
`lovelace:` **действительно игнорируется** (с explicit warning) при
`mode: storage` — то есть исходная посылка issue («в storage-режиме
`lovelace.resources` из YAML игнорируется целиком») **подтверждена кодом**, а
не опровергнута, как теперь пишет ТЗ.
- Это противоречит собственному телу issue (написано владельцем в тот же день):
«в storage-режиме дашбордов (умолчание HA) `lovelace.resources` из YAML
игнорируется целиком» — заявлено как подтверждённый факт полевого разбора, не
снято ни одним последующим комментарием.
- `vol.Schema({...})` для домена `lovelace` в 2025.1.4 не имеет
`extra=vol.ALLOW_EXTRA` (в отличие от внешней обёртки `CONFIG_SCHEMA`) —
значит непредусмотренный ключ верхнего уровня внутри `lovelace:` обычно
**валится валидацией конфигурации HA**, а не тихо игнорируется. Если
`resource_mode` не появится в реальном будущем релизе именно так, как описано,
рекомендованный «современный» sniplet из §12 может не просто не сработать
(старая проблема #462), а **сломать загрузку `lovelace:` целиком** — то есть
задача рискует внедрить дефект хуже исходного, ровно в том месте, которое
должно чинить документацию.
- Утверждение не помечено в §22 («Принятые технические предположения — можно
менять на ревью») как гипотеза, хотя структурно — это ровно тот раздел, где
ему место, если бы оно было гипотезой, а не фактом.
- Утверждение приводит к automated docs-contract тесту (AC1, §16) и
формулировке AC1 «мутант: … современный `mode: yaml` вместо `resource_mode:
yaml` … делает docs contract красным» — то есть контракт будет насильно
удерживать в README/User Guide формулировку, которую нечем подтвердить, и
снимать её будет уже не ревью, а очередной пользовательский репорт, как #462.
**Возврат автору.** Нужно либо (а) привести проверяемый источник для
`lovelace.resource_mode` (номер release note/PR HA, который это ввёл — тогда
формулировка остаётся, но с явной ссылкой), либо (б) убрать разделение на
современный/legacy YAML-режим и вернуться к единственному подтверждённому
контракту: `lovelace.mode: yaml` меняет и dashboard, и разрешает
`resources:`, а storage-режим требует UI Resources — то есть к тому описанию,
которое уже было верно на r1 и не вызывало вопросов. Пока источник не назван —
AC1, §5, §12 и соответствующий пункт §16 остаются недоказанным техническим
угадыванием, оформленным как решённый факт. High, блокирует.
### Low-1 (наследуется, закрыта) — числовой touch-target 44×44
Была Low в r1 без источника в каноне. §22 п.8 теперь явно помечает число как
«локальный accessibility-порог», а не заимствование канона. Формулировка
r1 предлагала ровно это — принимаю как закрытую, отдельного действия не
требуется.
### Low-2 — SHA не назван в вердикт-комментарии r1
Комментарий-вердикт r1 в issue (`Вердикт: жёлтый · заход r1 …`) не называет SHA
материала, на котором получен вердикт — притом что сам документ
`SPEC-REVIEW-462-r1.md` SHA называет дважды (в шапке и в «Материале раунда»).
Практических последствий не имело: SHA восстановлен однозначно по порядку
коммитов. Не блокирует, но стоит отметить в шаблоне хендофф-комментария на
будущее — вердикт-комментарий в issue должен либо называть SHA сам, либо явно
отсылать к документу как единственному источнику SHA.
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где видно |
|---|---|---|
| Medium: новый version-controller баннер и terminal-тост #353 просят reload независимо и могут показаться одновременно с разными формулировками | Явное правило: terminal-тост #353 подавляется только пока видна version-mismatch плашка; network/non-terminal тост и terminal-тост без плашки не подавляются; одновременно два сообщения с одной просьбой не показываются | `docs/specs/462-card-resource-registration.md:312-318` (§11.2), AC12 (`:526-528`), план тестов §16 «Browser smoke/golden» (`:560-562`), риск §19 п.11 (`:629-631`) |
| Low: touch-target 44×44 без источника в каноне | Помечено как локальное (не канонное) accessibility-решение | §22 п.8 (`:681-683`) |
Обе находки r1 закрыты по существу, без новых открытых вопросов по ним самим.
Однако правка Medium-находки (коммит `8973431f`) в том же дыхании внесла
новую, более серьёзную находку — см. «Находки», High — что и есть причина,
по которой предупреждение §2.9/#102 в этой задаче не абстрактно: починка одного
AC создала риск в другом месте того же коммита.
## Унаследовано из r1 (без повторной проверки в этом раунде)
Следующее не тронуто дельтой `12ddd107..e9e3d6bd` за пределами описанного выше
и принимается по документу `docs/reviews/SPEC-REVIEW-462-r1.md`
(SHA материала `12ddd107a9cdd9b12ea8411360760f684f5293c1`):
- Наличие и содержание обязательных разделов §7.1 PROCESS.md (сценарий, что
человек увидит, причина, скоуп/не-скоуп, контракт §8–11, данные/миграция,
i18n, AC1…AC13, план тестов, риски, откат, release-артефакты, §22).
- Существование и смысл символов кода, проверенных в r1:
`_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` (`houseplan-editor-runtime.ts:9610`),
`_cyclePausedUntil`/`_zoom`/`_editing`/`_pendingPhysicalWrites`/
`_writesPending`/`_vacFit`.
- Подтверждённая по реальным wheel `homeassistant==2024.6.0` и `==2025.1.4`
цепочка зависимостей `houseplan → frontend → lovelace` (§3) и отсутствие
`remove_extra_js_url` в 2024.6 (обосновывает `lovelace_resource_with_
session_fallback`, §8.2 абзац после п.9). Эти два факта дельта не
затрагивала и я их не переоткрывал.
- Пригодность `homeassistant.helpers.start.async_at_started` как
`CALLBACK_TYPE`, совместимого с `entry.async_on_unload` (§22 п.3, базовая
часть механизма; новым в этом раунде является только добавка secondary
cancellable delay поверх него — это я перепроверил заново, см. «Как
проверялось», п.2 относится к другому вопросу, а сам `async_at_started`
переподтверждать не стал).
- Полный список из 4 файлов документации с невалидным плоским `resources:`
(README.md:101, README.ru.md:105, docs/USER-GUIDE.md:94,
docs/USER-GUIDE.ru.md:93) — дельта не меняла состав списка, только
требуемое содержание правки.
- Обоснование выбора `persistent_notification` вместо Repairs (§22 п.1) через
прецедент `custom_components/houseplan/repairs.py`.
- Соответствие SCOPE.md: задача закрывает J4, out-of-scope не задет.
- AC2, AC5, AC6, AC7, AC8, AC9, AC10, AC11 — формулировки не менялись в дельте
(проверено `git diff` по разделам, см. «Материал раунда r1»); принимаю их
однозначность и доказуемость как установленную в r1.
## Что проверено и корректно (в этом раунде)
- Раздел «Закрытие раунда r1» — обе находки закрыты по существу, без
формальных отписок.
- §9 (флаг notification после синхронного `async_create`) — техническая
точность подтверждена по исходнику HA 2025.1.4.
- §11.2 card-level overlay в ранних `fixed-floor`/пустых состояниях —
требование обосновано реальной структурой `render()` в
`src/houseplan-card.ts` (три отдельных `<ha-card>`-ветки), не выдумка.
- §11.3 новые guard-пункты (нативный HA more-info не участвует по
ненадёжному полю; pending layout debounce/грязные позиции устройств
блокируют auto-reload) описаны как требования к реализации без ссылки на
несуществующие внутренние символы — не нарушают правило «не выдавать догадку
за решение», это раздел, где §17 явно оставляет точные имена за
реализацией.
- §22 п.8 (touch-target) и §11.2/AC12/§16/§19 (закрытие toast/banner
конфликта) корректно закрывают находки r1 — см. таблицу выше.
- Нумерация и внутренняя согласованность AC1–AC13 не нарушены дельтой: каждый
из четырёх изменённых AC (AC1, AC3, AC12, AC13) по-прежнему однозначен и
называет способ доказательства; проблема не в форме, а в фактической
достоверности содержания AC1.
## Чего не проверял
- Не проверял HA-версии новее `2025.1.4` напрямую — недоступны в индексе pip
этого окружения (то же ограничение, что и в r1). Это ровно тот пробел,
из-за которого High-находка выше сформулирована как «не подтверждено», а не
как «стопроцентно неверно»: теоретически upstream мог ввести такой ключ
именно так, как описано, в версии между `2025.1.4` и «2026.2+», но ни одного
подтверждения этому нет ни в коде, ни в вебе (недоступен), ни в остальной
части репозитория.
- `WebSearch`/`WebFetch` недоступны в сессии (permission denied на оба вызова)
— не смог свериться с release notes/документацией HA напрямую; вся
верификация High-находки — по исходному коду ближайшей доступной версии и
внутренней логике самого ТЗ.
- Не прогонял `typecheck`/`test`/`build` — этап spec, кода нет; гейты
применимы к код-ревью (§2.7), не к этому этапу.
- Не проверял AC2, AC5–AC11 заново по существу — формулировки не изменились
дельтой, приняты из r1 (см. «Унаследовано из r1»).
- Не оценивал разделы §13/§14/§17/§18/§20/§21 заново — дельта их не касалась
(кроме упомянутых точечных правок в §21 про «три golden» и «десять кадров»,
которые являются числовым уточнением существующего пункта r1, а не новым
контрактом, и не меняют вывод r1 по этим разделам).
## Материал раунда
- Ветка: `issue/462-card-resource-registration`
- SHA материала: `e9e3d6bd7d8e61a0e7d561da76b19cedb157f32b`
- Дерево: `7bea3d2ffb5216214a550abe845e67d7b4680797`
- ТЗ `docs/specs/462-card-resource-registration.md`, блоб
`c507644bf4fb0f5e902ddc4628497e329afeba3c`
- SHA r1 (для дельты): `12ddd107a9cdd9b12ea8411360760f684f5293c1`
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/462-card-resource-registration`, коммит `e9e3d6bd7d8e` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `7bea3d2ffb5216214a550abe845e67d7b4680797`
```
git log --all --format='%H %T' | grep 7bea3d2ffb52
```
- ТЗ `docs/specs/462-card-resource-registration.md`, блоб `c507644bf4fb0f5e902ddc4628497e329afeba3c`
```
git log --all --find-object=c507644bf4fb0f5e902ddc4628497e329afeba3c -- docs/specs/462-card-resource-registration.md
```