mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 04:09:17 +00:00
@@ -0,0 +1,289 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user