# SPEC-REVIEW #33 — r2 - Issue: https://github.com/Matysh/houseplan-card/issues/33 - Этап: ревью ТЗ (PROCESS.md §2.4) - ТЗ: `docs/specs/033-config-schema-lifecycle.md`, ревизия 3 - SHA материала: `1c1f9d8ea4a3e2f32c5cd6150dde5e08bdfc280b` - Заход: r2 · блокирующих циклов израсходовано 1 из 4 (r1 — жёлтый, потратил цикл; зелёный вердикт этого раунда цикла не образует, #227) - Вердикт: **зелёный** ## Скоуп Ревизия 3 — точечный ответ на три Medium-находки и два Low прошлого раунда (SPEC-REVIEW-33-r1, SHA `8335191b`), ничего сверх этого. Продуктовая рамка, класс задачи (A, schema-потребители), трек (полный) и оценка аналитики не пересматриваются — они не были предметом находок r1 и делта их не задевает. Дельта — ровно один файл, `docs/specs/033-config-schema-lifecycle.md`, 53 изменённые строки (34 добавлено / 19 удалено), подтверждено `git diff 8335191b..1c1f9d8e --stat`. Рядом в дереве `src/**` и `custom_components/**` не менялись с базового SHA r1 (`git diff --stat 8335191b..HEAD -- src/ custom_components/ package.json` пуст) — значит, ничего из подтверждённого чтением кода в r1 не могло протухнуть за это время. Делта локальна по критерию §2.10: не ребейз (HEAD ровно на один docs-коммит впереди материала r1), не смена контракта поведения (контракт поведения — «ничего в рантайме не меняется» — не тронут), не задета новая подсистема, объём сопоставим с тремя находками, которые он закрывает. Полный повторный разбор не требуется; ниже — разбор по дельте плюс целевая перепроверка того, до чего дельта дотягивается. ## Как проверялось 1. Найден вердикт r1 и SHA материала — оба названы в шапке `docs/reviews/SPEC-REVIEW-33-r1.md` (`8335191b`), спорить не пришлось. 2. Объявлена дельта: `git diff 8335191b..1c1f9d8e -- docs/specs/033-config-schema-lifecycle.md` (полный текст сверен построчно). 3. По каждой находке r1 (M1, M2, M3, L1, L2) — отдельная проверка чтением текущего дерева, что предложенное в ревизии 3 решение не просто иначе сформулировано, а технически состоятельно (не только «текст ТЗ звучит иначе», а «то, что он теперь обещает, совместимо с кодом, которого касается»): - `scripts/config-field-registry.mjs` — прочитан целиком; подтверждено, что поля `enforcedBy` и `schema: 'allow-extra'` в текущей форме записей нет (свободны для добавления), что `CONFIG_FIELD_STATUSES` остался тем же перечислением из 6 значений (ревизия 3 их не трогает — точно то, что требовало M1), что записей сейчас 27 (`node --input-type=module` импорт и `.length`), и что ни одна из четырёх «новых» ID-записей (`decor_default_style`, `furniture`, `host=partition`) ещё не существует в registry — делта не дублирует; - `src/logic.ts:898-919` — `SPACE_FILL_MODES`/`ROOM_FILL_MODES`/ `DISPLAY_MODES`/`TAP_ACTIONS` существуют и экспортируются, как и утверждает исправленный текст (только 3 из 7 пар); - `src/types.ts:212`, `src/plan-optimizer.ts:285`, `src/zero-walls.ts`/`houseplan-editor-runtime.ts`/ `houseplan-onboarding-runtime.ts` (литералы `'dashed'`/`'solid'`), `custom_components/houseplan/validation.py:1254` (`_BG_MODE`) — подтверждено, что для `opening.type`, `vacuum.trail_mode`, `zero_wall_style`, `bg_mode` сегодня нет ни одной именованной экспортируемой frontend-константы (ровно то, что M2 требовал признать явно) и что предложенные имена `OPENING_TYPES`, `VACUUM_TRAIL_MODES`, `ZERO_WALL_STYLES`, `BG_MODES` не заняты (`grep` по `src/*.ts` — пусто), то есть план не столкнётся с коллизией имён при реализации; - `scripts/config-audit.mjs` (всё, 103 строки) — подтверждено, что `process.exitCode` сегодня выставляется только на ветках ошибок разбора (74/79/102), никогда от найденных полей; что код `1` нигде не занят и не проверяется ни одним потребителем (`grep` по `package.json` и `.github/workflows/*.yml` на `audit:config`/`config-audit` — только объявление npm-скрипта, никакого CI-потребления exit-кода), то есть новый контракт `0/3/2` ревизии 3 не конфликтует с существующим поведением или скрытым потребителем; заодно перечитан сам факт: `2` сегодня — единственный ненулевой код, значит выбор `3` для нового смысла действительно не сталкивается с ним, как и заявляет текст; - `package.json:19` — `"audit:config": "node scripts/config-audit.mjs"`, текст ТЗ теперь везде цитирует именно эту команду (L1 закрыт); - весь файл ТЗ ревизии 3 — построчный поиск «24» не находит ни одного оставшегося упоминания старого числа, все вхождения — «27» (L2 закрыт полностью, не только в шапке). 4. Перечитаны критерии приёмки AC1–AC7 целиком — ни один не ссылается на статус `implemented` или на старый контракт exit-кода: правка текста Блока 2/3 синхронизирована с разделом AC, который сам по себе делту не зафиксировал (он и не должен был — AC6 уже был сформулирован достаточно абстрактно, «различает exit-codes», и лишь уточнён числами 0/3/2). 5. Перечитан документ ревью r1 целиком (не только раздел «Находки») — сверено, что раздел «Что проверено и корректно» r1 не полагался ни на один факт, который ревизия 3 меняет (архитектура манифеста, `docs/SCOPE.md`, §7.1-разделы, «уже реализовано жизнью» для show_all/weather_entity/ aspect/segments — ревизия 3 их не трогает, только меняет способ их фиксации в registry с несуществующего статуса на поле `enforcedBy`). 6. Дешёвые гейты: `npx tsc --noEmit` на HEAD (`1c1f9d8e`) — чисто, 0 ошибок. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где видно | |---|---|---| | **M1** — статус `implemented` не существует ни в перечислении, ни в `CONFIG_COMPATIBILITY.md` | Статусная модель не меняется; вместо нового статуса — опциональное поле `enforcedBy` (ссылка на кодовую точку/тест), новые паспорта получают существующий статус `current` | `docs/specs/033-config-schema-lifecycle.md:80-90` (Блок 2, абзац «Актуализация registry как данных»); подтверждено чтением, что `CONFIG_FIELD_STATUSES` (`scripts/config-field-registry.mjs:362-369`) и поле `enforcedBy` совместимы (поля с таким именем в записях сегодня нет) | | **M2** — 4 из 7 frontend-констант для parity-теста не существуют | Текст явно называет 3 готовые пары отдельно от 4 отсутствующих и описывает их как новую работу этого issue: экспортируемые `as const`-массивы `OPENING_TYPES`/`VACUUM_TRAIL_MODES`/`ZERO_WALL_STYLES`/`BG_MODES`, из которых выводятся существующие union-типы | `docs/specs/033-config-schema-lifecycle.md:59-68` (Блок 2, первый абзац); подтверждено, что ни одно из четырёх имён не занято в `src/*.ts` | | **M3** — AC6/Блок 3 описывали несуществующее поведение `config-audit.mjs` как «расширение теста» | Явно зафиксирован новый контракт exit-кодов: `0` clean, `3` migration available, `2` invalid (без изменений), с обоснованием выбора `3`, чтобы не конфликтовать с текущим `2`; AC6 переформулирован под тот же контракт | `docs/specs/033-config-schema-lifecycle.md:101-107` (Блок 3, последний абзац) и `:147-148` (AC6); подтверждено чтением `scripts/config-audit.mjs`, что код `1` сегодня свободен и не потребляется CI | | **L1** — команда в «Что человек увидит» названа неверно (`config:audit` вместо `audit:config`) | Текст исправлен на точное имя npm-скрипта | `docs/specs/033-config-schema-lifecycle.md:22`; сверено с `package.json:19` | | **L2** — «registry = 24 записи» разошлось с фактическим размером массива (27) | Все вхождения числа в шапке и в разделе «Проблема» заменены на 27 | `docs/specs/033-config-schema-lifecycle.md:8,27`; сверено подсчётом `CONFIG_FIELD_REGISTRY.length` на HEAD — 27; поиском по файлу подтверждено отсутствие оставшихся «24» | Все пять находок закрыты правкой текста в пределах ревизии 3, без изменения архитектуры Блока 1 и без обращения к владельцу — как и предполагал вердикт r1. ## Унаследовано из r1 Следующее принято без повторной проверки — делта его не задевает, и ничего в дереве (src/**, custom_components/**) с SHA `8335191b` не изменилось: - соответствие `docs/SCOPE.md` (задача — защитная инфраструктура под J6, не претендует на пользовательскую ценность) — `SPEC-REVIEW-33-r1.md`, раздел «Скоуп», SHA `8335191b`; - присутствие и полнота всех обязательных разделов §7.1 (сценарий, что человек увидит, проблема, скоуп/не-скоуп, контракт, UX/i18n, модель данных, AC, план тестов, риски, откат, release-артефакты, принятые предположения) — `SPEC-REVIEW-33-r1.md`, раздел «Что проверено и корректно», SHA `8335191b`; ревизия 3 не удалила и не переименовала ни один раздел (структура файла сверена при чтении ревизии 3 целиком в этом раунде, см. «Как проверялось» п.4–5, поэтому это скорее переподтверждено, чем чисто унаследовано); - архитектурное решение Блока 1 (манифест схемы генерируется из Voluptuous, а не пишется руками на 212 путей) — не тронуто делтой, корректность подтверждена в r1 чтением `scripts/config-field-registry.mjs` и `custom_components/houseplan/validation.py` на SHA `8335191b`; - факты «show_all/weather_entity/aspect/segments уже реализованы кодом» — подтверждены в r1 чтением `houseplan-card.ts:3895`, `houseplan-editor-runtime.ts:9618`, `validation.py:1590,1670` на SHA `8335191b`; ревизия 3 меняет только способ их фиксации в данных (`enforcedBy` вместо статуса), не сам факт; - необходимость заглушек родительских пакетов для импорта `validation.py` без `homeassistant` — воспроизведено в r1 (`ModuleNotFoundError`), делта этого блока не касается; - формулировка не-скоупа (`group_lights`/`exclude_integrations` → #44) — не менялась. ## Что проверено и корректно - Все пять находок r1 закрыты по существу, а не только по формулировке: каждое новое обещание текста (поле `enforcedBy`, четыре новых экспорта, контракт exit-кодов 0/3/2, точное имя npm-скрипта, число 27) сверено с текущим деревом и не встречает коллизии имён, полей или уже занятого значения. - AC1–AC7 остаются согласованными с телом ТЗ после правки: ни один AC не ссылается на отменённый статус `implemented` или на прежний недоопределённый контракт exit-кода; AC6 обновлён в паре с текстом Блока 3. - Числа в шапке (`212`, `2`, `27`) внутренне согласованы по всему файлу — расхождений вида «одно число, два значения» не осталось (единственная такая находка прошлого раунда, L2, закрыта полностью). - Делта не расширяет скоуп: три новых абзаца — это уточнение уже анонсированных в ревизии 2 блоков, а не новая работа сверх исходного ТЗ. ## Чего не проверял - Не проверял по существу архитектурные решения, не затронутые находками r1 (Блок 1 целиком, фикстуры Блока 3 кроме контракта exit-кода, раздел «Риски») — они не входят в дельту и не изменились со SHA `8335191b`; доверие — по разделу «Унаследовано из r1» выше, не молчаливое. - Не гонял `npm test`, `npm run build`, `node scripts/check-docs.mjs` — diff раунда docs-only (подтверждено `git diff --stat`), `src/**` не тронут, эти гейты не сказали бы ничего нового о ревизии ТЗ (то же основание, что в r1). - Не гонял смоки, golden, инварианты модели, performance — этап ТЗ, кода ещё нет, ни один из этих гейтов не применим. - Не проверял заново оценки сложности/риска и продуктовую ценность из комментариев аналитики — не предмет ревью ТЗ, не изменились в этом раунде. ## Гейты — сводка | Гейт | Прогнан | Результат | |---|---|---| | `npx tsc --noEmit` | да, на SHA `1c1f9d8e` | чисто, 0 ошибок | | `npm test` | нет | diff docs-only, src/** не тронут | | `npm run build` | нет | то же | | `node scripts/check-docs.mjs` | нет | diff не касается `src/**` | | смоки / golden / инварианты / perf | нет | этап ТЗ, продукт не меняется | ## Итог 0 High, 0 Medium, 0 Low. Все пять находок r1 закрыты состоятельно — проверено не только чтение нового текста, но и совместимость каждого нового обещания (поле `enforcedBy`, четыре новых frontend-константы, контракт exit-кодов, точные имена команды и числа) с текущим деревом. Новых находок делта не создала. Вердикт зелёный, ТЗ готово к разработке.