mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 13:48:57 +00:00
149 lines
13 KiB
Markdown
149 lines
13 KiB
Markdown
# SPEC-REVIEW-378-r1
|
||
|
||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/378
|
||
- **Этап:** ТЗ на ревью (`S4-spec-review`), заход r1, блокирующих циклов израсходовано 0/4
|
||
- **Артефакт ТЗ:** `docs/specs/378-value-face-source.md`
|
||
- **SHA материала:** `7d5d006cccb5eeb36f2ecc92dabd124b7d137e32` (ветка `issue/378-value-face-source`, HEAD на момент ревью)
|
||
- **Ревьюер:** Claude (роль «ревьюер ТЗ»), сессия без контекста автора
|
||
- **Первый раунд** — раздел «Унаследовано из r<N-1>» неприменим, разбор полный.
|
||
|
||
## Скоуп ревью
|
||
|
||
Оценивался только этап ТЗ: тело issue #378, аналитический комментарий владельца
|
||
(2026-08-29T18:37:48Z) и файл `docs/specs/378-value-face-source.md` на коммите
|
||
`7d5d006c`. Продуктовый код не менялся и не оценивался как реализация — только
|
||
как источник фактов для проверки утверждений ТЗ (существуют ли названные
|
||
функции/поля, действительно ли устроено так, как описано).
|
||
|
||
## Как проверялось
|
||
|
||
Читал в порядке, предписанном процессом:
|
||
|
||
1. `docs/SCOPE.md` — соответствие Core user jobs. Задача расширяет уже
|
||
закрытый J1/J5-механизм («лицо» устройства на плане), не создаёт новую
|
||
персону и не входит в out-of-scope список. Класс изменения (A, полный
|
||
трек) назван и обоснован верно: конфиг marker получает новое публичное
|
||
поле, задета миграция/reference seam, несколько renderer, touch-контракт —
|
||
именно тот критерий `small`, который называет сам автор как нарушенный.
|
||
2. `PROCESS.md` §2.3–2.5, §7.1, §5 — проверка обязательных разделов ТЗ и
|
||
выбора трека.
|
||
3. Тело issue #378 и оба комментария (аналитика, затем «ТЗ готово»).
|
||
4. `docs/USER-GUIDE.ru.md` (раздел «Четыре варианта „Отображение“» и «Бейдж
|
||
со значением рядом с устройством», строки ~1047–1113) — сверка
|
||
терминологии и уже задокументированного поведения.
|
||
5. `docs/DEVICE-PRESENTATION.md` — таблица решений F09–F12, S01–S15,
|
||
владение модулями (`device-presentation.ts`, `device-presentation-policy.ts`,
|
||
`device-face.ts`).
|
||
6. Код на SHA `7d5d006c` — не как реализация (её нет), а как проверка того,
|
||
что ТЗ не выдаёт догадку за факт:
|
||
- `src/device-value-badge.ts` целиком — `VALUE_BADGE_ATTRIBUTES`,
|
||
`valueBadgeCandidates()`, `valueBadgeSourceKey/FromKey()`, `resolveSource()`,
|
||
unavailable-контракт, кэш кандидатов;
|
||
- `src/device-presentation.ts:449-547` — `validStateValue()`, `resolveValue()`,
|
||
climate/power-gate/ambiguity эвристика, `signatureOf()`,
|
||
`presentationSourceSignature()`;
|
||
- `src/houseplan-editor-runtime.ts` (грепом) — существующий selector value
|
||
badge в диалоге (строки ~12586-12633), `innerValueSourceKey` (12122,
|
||
сравнение с `badgeSourceKey` для duplicate-hint, 12611), `_valueBadgeForBinding`
|
||
(11904-11922, сброс/рекомендация источника при смене binding);
|
||
- `custom_components/houseplan/validation.py:805-865` —
|
||
`validate_marker_value_badges`, доступные коды ошибок;
|
||
- `custom_components/houseplan/import_export.py:1090-1111, 1344-1391` —
|
||
rebind и space-transfer для `value_badge.source.ref`;
|
||
- `src/devices.ts:868-889` — `rewriteMarkerControlReferences()`, уже
|
||
переписывающий `value_badge.source.ref` при смене marker id;
|
||
- `docs/specs/README.md:116` — ссылка issue ↔ ТЗ на месте;
|
||
- четыре файла `src/i18n/*.json` — паритет локалей, на которые претендует ТЗ.
|
||
|
||
Гейты (typecheck/test/build/golden и т.д.) на этом этапе не запускались:
|
||
кода нет, реализации нет, оценивать нечего. Это ожидаемо для этапа spec-review
|
||
и не является пропуском гейта.
|
||
|
||
## Находки
|
||
|
||
Блокирующих (High) и Medium-находок нет.
|
||
|
||
**Low — не блокирует, снимаю с записью.** Контракт п.1.6 («При явной смене
|
||
HA binding в диалоге старый `value_source` сбрасывается в auto») асимметричен
|
||
уже существующему поведению соседнего селектора: `_valueBadgeForBinding()`
|
||
(`src/houseplan-editor-runtime.ts:11904-11922`) при смене binding не просто
|
||
сбрасывает `value_badge.source` в null, а вызывает `recommendedValueBadgeSource()`
|
||
и подставляет новую рекомендацию для только что привязанного устройства. ТЗ же
|
||
предписывает для `value_source` только сброс в auto, без аналогичной
|
||
рекомендации. Это может привести к тому, что при рebind марказы на другой cover
|
||
бейдж рядом получит разумную рекомендацию (`current_position`), а лицо значения
|
||
откатится к слову состояния — ровно к той проблеме, которую issue закрывает.
|
||
Решение не ошибочно (объяснение «перенос источника прежнего устройства был бы
|
||
ложной настройкой» состоятельно и последовательно с общим принципом «явный
|
||
источник не заменяется автоматически»), и это техническое, не продуктовое
|
||
решение (`PROCESS.md` §7.1: где строится дефолт после rebind — не то, что
|
||
спрашивают владельца). Оставляю как решение автора, но фиксирую для внимания
|
||
на код-ревью AC8: если реализация всё же скопирует `recommendedValueBadgeSource`-
|
||
паттерн для симметрии, это не должно расцениваться как отход от ТЗ.
|
||
|
||
## Что проверено и корректно
|
||
|
||
- **Полнота обязательных разделов §7.1**: сценарий, что человек увидит до/после,
|
||
проблема, скоуп/не-скоуп, контракт поведения, UX, модель данных и миграция,
|
||
i18n, AC1–AC10 с доказательством, план автотестов, риски, откат,
|
||
release-артефакты — присутствуют все, в этом порядке.
|
||
- **Продуктовые разделы отвечают на два обязательных вопроса**: персона
|
||
(администратор дома, desktop, диалог устройства), поверхность и момент
|
||
встречи, а также «что человек увидит до/после» одной фразой без терминов
|
||
реализации (`Open` → `42 %`) — до раздела с реализацией, как требует канон.
|
||
- **Выбор полного трека обоснован явно** названным нарушенным критерием
|
||
(публичное поле конфига + reference seam + несколько renderer + touch),
|
||
а не общим «сложно».
|
||
- **Ни одного факта не оказалось выдумкой.** Каждое техническое утверждение
|
||
ТЗ проверено против реального кода на SHA `7d5d006c` и подтвердилось:
|
||
- `VALUE_BADGE_ATTRIBUTES`, `valueBadgeCandidates()`, unavailable-контракт —
|
||
существуют буквально как описаны;
|
||
- общий resolver/formatter для лица и бейджа — реалистичная цель: уже есть
|
||
частичное сближение (`innerValueSourceKey` в редакторе уже сравнивается
|
||
с ключом бейджа для duplicate-hint, т.е. инфраструктура для п.2.8 контракта
|
||
существует, а не изобретается с нуля);
|
||
- reference seam для `derived_marker_state` (`rewriteMarkerControlReferences`,
|
||
backend `import_export.py` rebind/space-transfer) для `value_badge.source.ref`
|
||
уже реализован тем самым паттерном, который ТЗ предлагает распространить на
|
||
`value_source.ref` — это не новый механизм, а его вторая точка подключения;
|
||
- `resolveDevicePresentation()` действительно используется и основным планом,
|
||
и `space-card.ts`/`space-render.ts` — утверждение AC2 «полный план и static
|
||
space card показывают одно и то же» технически обосновано общим вызовом;
|
||
- `d.virtual` gate в `resolveValue()` срабатывает раньше любого другого
|
||
условия — контракт п.2.6 («virtual имеет приоритет над сохранённым
|
||
источником») соответствует реальному порядку проверок, изменений в этой
|
||
ветке не требуется;
|
||
- F09–F12 и связанные ID из `docs/DEVICE-PRESENTATION.md` использованы точно,
|
||
без придумывания несуществующих строк таблицы;
|
||
- i18n-паритет заявлен для ровно тех четырёх локалей, что есть в дереве
|
||
(`en/ru/de/fr`), лишних/недостающих не названо.
|
||
- **Не-скоуп совпадает 1:1** с разделом «Не входит» тела issue: новый display
|
||
mode, изменение самого списка атрибутов, изменение бейджа, перенацеливание
|
||
tap-действия — везде явно исключено и не просачивается в контракт.
|
||
- **Каждый AC называет способ доказательства** (unit/backend/smoke/golden/
|
||
docs gate/commands), ни одного голого «проверено».
|
||
- **Откат безопасен и объяснён на уровне данных**: старый frontend/backend с
|
||
`ALLOW_EXTRA` пропустит поле, данные не теряются — соответствует модели
|
||
`docs/CONFIG-COMPATIBILITY.md` (lossless / untouched future literal).
|
||
- **Открытых продуктовых вопросов нет**, и это обоснованно: сценарий, объём
|
||
видимого изменения и границы деградации (unavailable → `—`, virtual →
|
||
икона) зафиксированы однозначно самим ТЗ, а не додуманы — раздел «Принято
|
||
предположительно» содержит только техническое (имя поля, извлечение модуля,
|
||
структура smoke-теста), что и требуется §7.1.
|
||
|
||
## Чего не проверял
|
||
|
||
- Не запускал `npx tsc --noEmit` / `npm test` / `npm run build` — на этапе
|
||
spec-review код ещё не написан, гейты неприменимы.
|
||
- Не проверял `MARKER_SCHEMA` (`validation.py:1730`) построчно на предмет того,
|
||
как именно будет описан `value_source` в схеме — это код-ревью реализации,
|
||
не предмет ТЗ.
|
||
- Не оценивал производительность нового обхода `valueBadgeCandidates()` в
|
||
реальном большом доме — раздел «Производительность» в ТЗ ссылается на
|
||
существующий кэш и budget-гейт, этого достаточно для этапа spec.
|
||
- Не проверял golden/скриншоты — они не существуют до реализации.
|
||
|
||
## Вердикт
|
||
|
||
Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0 → в задаче
|