docs: #44 spec revision 3 per SPEC-REVIEW-44-r1

User-Visible: no
Issue: #44
This commit is contained in:
Codex
2026-08-30 13:39:23 +03:00
parent df1dbccfdb
commit ba56876361
+34 -16
View File
@@ -3,8 +3,8 @@
- Issue: https://github.com/Matysh/houseplan-card/issues/44
- Приоритет: P2, tech-debt; полный трек (discovery/UI/settings/i18n/tests —
решение аналитики 2026-08-15)
- Ревизия: 2 (2026-08-30) — policy v1 принята владельцем (вход в работу без
возражений к записанной политике); конкретизация под инбокс v1.69
- Ревизия: 3 (2026-08-30) — по SPEC-REVIEW-44-r1 (H1, H2); policy v1 принята
владельцем, конкретизация под инбокс v1.69
- Связано: device inbox #29, field registry/манифест #33 (паспорта
`allow-extra` обоих ключей уже выданы), персист-паттерн #377
@@ -20,10 +20,11 @@
## Что человек увидит до и после
**До**: группировка света и исключение интеграций происходят молча; понять
«почему этого устройства нет в списке» нельзя. **После**: обе настройки видны
и объяснимы; у кандидата, скрытого фильтром, в инбоксе появляется причина
«Исключена интеграция X»; ничего не меняется само — только по Сохранить.
**До**: группировка света и исключение интеграций происходят молча; причина
`excluded_integration` в инбоксе уже существует (#29), но безлична
(«Интеграция исключена фильтрами»). **После**: обе настройки видны и
управляемы; текст причины называет интеграцию по имени («Исключена
интеграция {integration}»); ничего не меняется само — только по Сохранить.
## Проблема
@@ -66,13 +67,15 @@ discovery `devices.ts:1491`).
Отмена — no-op. Undo — штатный именованный снапшот конфига (паттерн
существующих правок настроек).
### 2. Причина «Исключена интеграция X» в инбоксе
### 2. Причина с именем интеграции (H1 r1: факт исправлен)
Кандидат, отфильтрованный исключением, при показе на вкладке «Скрытые»
несёт `DeviceInboxReason` нового значения `excluded_integration` с текстом
«Исключена интеграция {integration}». (Сегодня такие устройства не попадают
в инбокс вовсе — они появляются на «Скрытых» именно с этой причиной;
объём выдачи ограничен существующей механикой вкладки.)
Причина `excluded_integration` УЖЕ существует и рендерится на вкладке
«Доступны» (#29, `houseplan-editor-runtime.ts:7637`,
`device_inbox.reason_excluded_integration`) — но текст обобщённый. Скоуп
этой задачи: тексту добавляется плейсхолдер `{integration}` с фактическим
platform-именем кандидата; вкладка НЕ меняется (перенос категории — отдельный
продуктовый вопрос, здесь не открывается). Значение reason и его механика не
трогаются.
### 3. Registry/доки
@@ -93,6 +96,13 @@ discovery `devices.ts:1491`).
1. Конфиг без ключей → поведение байт-в-байт сегодняшнее (default-путь не
меняется ни в одном потребителе).
1a. (H2 r1) `roomClimateMap` (`devices.ts:1491`) — единственное место в
`src/**`, хардкодящее `EXCLUDED_DOMAINS` мимо настройки, — переводится на
тот же резолвер действующего набора исключений, что и discovery: климат
комнаты следует за настройкой пользователя, а не за старым жёстким
списком (иначе UI создаёт новый «третий вариант»). Явный climate-opt-in
(галочка «этот датчик меряет воздух комнаты») по-прежнему сильнее
исключения — существующая ветка `optClimate` не меняется.
2. Explicit-маркер (живой или tombstone) НИКОГДА не исчезает из-за смены
фильтров — фильтры влияют только на автоматических кандидатов и seed.
3. Оба значения читаются из одного источника (`_settings`) всеми
@@ -125,8 +135,13 @@ issue требует видимости и policy, не пере-схемиза
список; «Вернуть рекомендуемые» + Сохранить → ключа нет.
- **AC3** (смок): у явного маркера с binding исключённой интеграции ничего
не меняется (живой остаётся на плане, tombstone не трогается).
- **AC4** (смок): скрытый фильтром кандидат виден на «Скрытых» с причиной
«Исключена интеграция X».
- **AC4** (смок): кандидат исключённой интеграции виден на «Доступны» с
причиной, называющей интеграцию по имени («Исключена интеграция demo_x»);
вкладка и значение reason прежние (регресс-ветка).
- **AC4b** (юнит, H2): `roomClimateMap` с настроенным `exclude_integrations`
фильтрует по НЕМУ (интеграция вне списка даёт климат; продуктовая, вручную
включённая в список — не даёт); без ключа — байт-в-байт сегодняшний
результат; явный climate-opt-in побеждает исключение (регресс-ветка).
- **AC5** (юнит): резолвер действующего набора: unset→продуктовый список,
[]→пусто (валидная «ничего не исключать»), список→список; тумблер:
unset→true.
@@ -139,8 +154,8 @@ issue требует видимости и policy, не пере-схемиза
## План автотестов
- Юниты: резолверы (AC5), превью-контракт (AC6), регресс дефолта (AC7) —
test/devices.test.mjs + test/device-inbox.test.mjs.
- Юниты: резолверы (AC5), превью-контракт (AC6), регресс дефолта (AC7),
климат-фильтр (AC4b) — test/devices.test.mjs + test/device-inbox.test.mjs.
- Смок `demo/smoke_discovery_filters.mjs` (новый): AC1–AC4 на демо-стенде
с моковым config/set (паттерн #377-смока).
- Мутанты: м1 — превью-счётчики от копии логики (подмена вызова общего
@@ -155,6 +170,9 @@ issue требует видимости и policy, не пере-схемиза
- Реason-вкладка «Скрытые» получает новый класс записей — объём ограничен
существующим механизмом вкладки; смок проверяет отсутствие дублей.
- Гонка вкладок — штатный conflict #340.
- Перевод roomClimateMap на резолвер (H2) меняет климат ТОЛЬКО при заданном
пользователем ключе — default-путь передаёт тот же продуктовый список
(AC4b регресс-ветка + существующие климат-юниты #317 не слабеют).
## Откат