# SPEC-REVIEW-592-r2 — «Вынести четыре диалога настроек из houseplan-editor-runtime.ts в модули (шаг 0 эпика #591)» Issue: [#592](https://github.com/Matysh/houseplan-card/issues/592) Этап: spec Заход: r2 (блокирующих циклов израсходовано 1 из 4 — израсходован жёлтым вердиктом r1; зелёный вердикт цикла не образует, §4) ## Вердикт **Зелёный.** High: 0. Medium: 0. ## Скоуп разбора — по дельте (§2.10) Предыдущий раунд (r1) — жёлтый, две находки: M1 (в скоупе) и L1 (снята с записью). Документ: `docs/reviews/SPEC-REVIEW-592-r1.md`. Материал r1 объявлен его блоком «Материал раунда»: ветка `dev`, коммит `01fe7350791e`, дерево материала `0054c6622b79256b64b455ed786b46270fec9208`. SHA живой (это предок текущего `HEAD` — `01fe7350` виден в истории репозитория), ре-резолвить по якорям не потребовалось. Дельта этапа spec — не код, а тело issue #592 (ревизия r2 против r1). По правке автора в комментарии видно, что изменения локальны: заголовочная строка «Редакция r2: правки по ревью ТЗ r1», таблица «Мера храповика/Запас» (4→3), формулировка «трёх строках» вместо «нескольких», список эталонов AC2 (добавлен `device-ripple-color-popover-mobile-ru`), новый абзац «Почему именно этот список эталонов (правка по M1)» с таблицей покрытия по диалогам, и список смоков AC1 (добавлены `smoke_gear_tabs`, `smoke_esc_dialogs`, `smoke_dialog_footer_width`). Остальные разделы ТЗ (Проблема кроме числа, Контракт К1–К5, Скоуп/не-скоуп, AC3–AC7, Риски, Откат, Release-артефакты, «Принято предположительно») дельту не задевают — это подтверждают обе реплики автора («обе находки закрыты», список правок исчерпывающий) и сверка структуры документа r2 с r1: разделы совпадают дословно за пределами перечисленных мест. Дельта локальна: новый субъект не появился (та же подсистема — редакторский граф диалогов), контракт поведения (К1–К5) не менялся, объём правки (несколько строк и одна таблица) несопоставим с объёмом исходной задачи. Условия «разбор остаётся полным» (ребейз, смена контракта, новая подсистема, сопоставимый объём) не выполнены — сокращённый разбор оправдан. Поскольку код ещё не менялся (шаг не начат, `User-Visible: no`, задача готовит почву), дешёвые гейты (`typecheck`/`test`/`build`) в этом раунде не гоняю — они относятся к материалу code-review, а не к телу issue на этапе spec; это не пропуск, а неприменимость предмета. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | **M1** (Medium, в скоупе) — AC2 не называет эталон `device-ripple-color-popover-mobile-ru`, хотя он единственный доказывает ветку `display === 'icon_ripple'` в переносимом `_renderMarkerDialog` | Эталон добавлен в список AC2 (выделен жирным в теле issue) | Тело issue, строка AC2: «…**`device-ripple-color-popover-mobile-ru`**…»; проверено независимо: `demo/golden/matrix.mjs:1084` регистрирует сценарий, `demo/golden/harness.mjs:1994-2013` (ветка `dialog === 'device-ripple-color'`) открывает `_openMarkerDialog`, ставит `display: 'icon_ripple'` и раскрывает `hp-color-opacity` с ярлыком `marker.activity_color`; baseline `demo/golden/baselines/device-ripple-color-popover-mobile-ru.png` существует | | **L1** (Low, снята с записью) — шапка называла запас ядер «4», фактически 3 (`split('\n').length`, не `wc -l`) | Число исправлено на «3» в таблице «Мера храповика/Запас» и в тексте «Проблема» | Тело issue: таблица показывает `Запас: 3 / 3`; текст «Оба ядра стоят в трёх строках от жёсткого потолка»; проверено пересчётом: `src/houseplan-editor-runtime.ts` — 14096 строк по `wc -l`/`awk 'END{print NR}'`, `split('\n').length` даёт 14097 против потолка 14100 в `test/core-file-budget.test.mjs:36` → запас 3; `src/houseplan-card.ts` — 13728 строк, `split.length` = 13729 против потолка 13732 (`:32`) → запас 3 | Обе находки закрыты фактом текста/кода, а не заявлением автора — проверено независимо, не только пересказано. ## Что проверено в этом раунде (дельта) 1. **Новый абзац «Почему именно этот список эталонов»** — таблица покрытия по диалогам (Устройство 7 / Комната 2 / Общие настройки 3 / Пространство 1) пересчитана и совпадает с фактическим списком AC2: 3 `device-dialog-*` + 1 `device-help-popover-*` + 1 `device-ripple-color-*` + 2 `toggle-entity-dialog-*` = 7 для устройства; 2 `room-temperature-dialog-*` для комнаты; `general-color-*` + 2 `settings-help-zoom-200-*` = 3 для общих настроек; 1 `space-room-color-popover-desktop-ru` для пространства. Итого 13 эталонов в AC2 (12 из r1 + добавленный ripple) — сходится с телом issue. 2. **Три новых смока в AC1**, заявленные как компенсация единственного golden-кадра пространства. Проверено чтением каждого файла: - `demo/smoke_gear_tabs.mjs` — открывает `_spaceDialog` (строки 23-49), проверяет режим `create` (`_spaceDialog.mode === 'create'`) — реально задействует диалог пространства; - `demo/smoke_esc_dialogs.mjs:67-68` — вызывает `c._openSpaceDialog('edit', c._space)` и проверяет закрытие по Esc; - `demo/smoke_dialog_footer_width.mjs` — вызывает `_openSpaceDialog('edit', …)` дважды (desktop и narrow), измеряет `buttons`, `oneRow`, `noHorizontalOverflow`, `insideViewport` (строки 87, 139, 201-217) — реально проверяет структуру диалога пространства (число кнопок, отсутствие переполнения), а не просто факт открытия. Все три файла существуют, все три действительно открывают/используют `_spaceDialog`/`_openSpaceDialog`, заявление автора подтверждено чтением кода, а не принято на слово. Важная оговорка (не находка, а точность формулировки): эти три смока — функциональные/структурные проверки (число кнопок, переполнение, режим), не побайтовые сравнения кадра. Автор сам явно не относит их к AC2 (побайтовое совпадение), а держит в AC1 — формулировка в issue корректна и не выдаёт смок за замену golden-сравнения. ## Унаследовано из r1 Без повторной проверки в этом раунде, со ссылкой на `docs/reviews/SPEC-REVIEW-592-r1.md` (материал: ветка `dev`, коммит `01fe7350791e`, дерево `0054c6622b79256b64b455ed786b46270fec9208`): - Границы всех четырёх методов (`_renderMarkerDialog` 12884–13610, `_renderSpaceDialog` 13611–13907, `_renderSettingsDialog` 10337–10502, `_renderRoomDialog` 13984–14097) — дельта их не меняет, номера строк в теле issue r2 идентичны r1. - Прецедент `radar-section.ts`/`vacuum-maps-section.ts` (форма экспорта, отсутствие `@state()`, кто импортирует) — К2 в r2 не менялся. - Число мутационных патчей (33) с `file: 'src/houseplan-editor-runtime.ts'` и направление `relocateEditorPatch` — К5 в r2 не менялся. - Реальность доказательств AC5 (`bundle:budget`, `bundle-assets.test.mjs` разбирают фактический вывод сборки) и AC6 (`mutation-gate.test.mjs` — уникальность якоря) — тексты AC3–AC7 в r2 не менялись. - Полнота обязательных разделов §7.1, отсутствие незаявленных допущений (раздел «Принято предположительно» ограничен техническими решениями) — структура документа вне перечисленных мест дельты идентична r1. - Продуктовая рамка: задача — шаг 0 эпика #591, видимых изменений нет (К1), сценарий и «что человек увидит» корректно формулируют нулевой видимый эффект как факт, а не как отсутствие анализа. - Trailers: `User-Visible: no` корректно, changelog не требуется — задача не меняет ничего наблюдаемого пользователем. ## Чего не проверял - Не повторял построчный аудит всех ≈1300 строк четырёх методов на предмет других скрытых веток по образцу ripple, кроме уже закрытой M1 — дельта r2 не расширяла зону риска (не добавляла новых диалоговых веток), только меняла список эталонов/смоков; повторный полный аудит не относится к предмету раунда. - Не проходил построчно содержимое `smoke_general_settings.mjs`/ `smoke_room_settings.mjs`/`smoke_color_picker_consumers.mjs`/ `smoke_static_icon.mjs`/`smoke_device_inbox.mjs`/`smoke_room_temperature_thresholds.mjs`/ `smoke_device_preview_parity.mjs` — эти семь смоков дельту не задели, их разбор унаследован из r1 неявно (r1 их не проверял построчно и сам отметил это в «Чего не проверял»; предмет этого раунда — не они). - Не гонял `npx tsc --noEmit`, `npm test`, `npm run build`, `golden:verify`, `bundle:budget` — этап spec, продуктовый код ещё не менялся под эту задачу; единственная числовая проверка (`split('\n').length` против потолков `test/core-file-budget.test.mjs`) сделана вручную пересчётом, сам тестовый файл не запускался (не нужен: код диалогов ещё на месте, ratchet не может измениться). - Не читал повторно эпик #591 целиком — К1 подготовки к нему в r1 уже подтверждён, дельта эпика не касается. ## Материал раунда - Issue #592, тело на момент разбора (`gh issue view 592 --json body`), ревизия r2 (после комментария автора от 2026-09-18T16:11:07Z). - Заход r2, циклов ревью ТЗ израсходовано 1 из 4 (израсходован жёлтым вердиктом r1; текущий зелёный вердикт цикла не расходует, §4). - Предыдущий раунд: `docs/reviews/SPEC-REVIEW-592-r1.md`, материал — коммит `01fe7350791e`, дерево `0054c6622b79256b64b455ed786b46270fec9208`. - Код на момент разбора — рабочая копия на `HEAD` = `8ec85ac0f6f62e72f8ac43c95786080e02b3a8bf` (продуктовый код задачи ещё не менялся; ссылки на строки/потолки относятся к этому состоянию репозитория). --- ## Материал раунда - Ветка: `dev`, коммит `8ec85ac0f6f6` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `d7c95364e09b77282237c26d3917fc86f3a025bd` ``` git log --all --format='%H %T' | grep d7c95364e09b ``` - Тело issue: `b105f7b3eb3f70575ee5751d1a917940da949981400e342e9cb133153057f673` - Вердикт конвейера: `green` · High 0