# SPEC-REVIEW-598-r2 **Issue:** https://github.com/Matysh/houseplan-card/issues/598 **Заголовок:** Три оставшихся диалога настроек на общий набор контролов: Пространство, Общие, Устройство (шаг 3 эпика #591) **Этап:** ревью ТЗ (PROCESS.md §2.4) **Заход:** r2 · блокирующих циклов израсходовано 1 из 4 **Материал:** тело issue #598, раздел `## ТЗ`, снято `gh issue view 598 --json body` на момент чтения 2026-09-19 (редакция текста — r3 по собственной нумерации автора; раунд ревью — r2, так как редакции r2→r3 прошли под `blocked` без нового ревью). Комментариев к issue — 4: вердикт конвейера r1, ответ автора на M1 (с открытым вопросом), ответ владельца, финальный ответ автора со снятием `blocked` и заявкой `S4-spec-review`. ## Скоуп ревью r2 По PROCESS.md §2.10 предмет повторного раунда — дельта, а не задача целиком. Прежний вердикт (SPEC-REVIEW-598-r1.md, `docs/reviews/`) — единственная находка M1, Medium, в скоупе. Материал того раунда объявлен в его блоке «Материал раунда»: ветка `dev`@`958ab36652eb`, дерево `232dd22abb22fc04ea01c124ac4332a6060a4fc8`, блоб тела issue `aee54691f0fa3651f53c32dcb2934b5d0ebb983714e8c297783696b476976331`. Код по issue не писался (веток `issue/598-*` нет, `git branch -a | grep 598` — пусто, HEAD на `ac1d25a0` — это как раз коммит публикации `SPEC-REVIEW-598-r1.md`, `git status` чист), поэтому дельта — только текст тела issue между r1 и текущей редакцией. Готового постраничного диффа тела issue GitHub не отдаёт (правки — не отдельные ревизии API, а редактирование одного и того же issue body), поэтому дельта восстановлена по авторской записи: комментарий «Ответ на жёлтый вердикт r1» построчно называет, что изменилось (М1, вторая находка про словарь `gs.hint`, пересчёт счётчиков), а текущее тело issue сверено с этим перечнем построчно — см. «Как проверялось» ниже. Это тот же метод доказательства, что использует сам конвейер для git-дельты: не гадание, а сверка заявленного изменения с итоговым текстом. Разбор остаётся по дельте: рёбейз/смена контракта/новая подсистема неприменимы к текстовому ТЗ, объём дельты (одна находка + одно сопутствующее наблюдение автора) несравним с объёмом всего ТЗ. Проверке заново подлежат ровно те разделы, которых касается дельта: К4, К5, УX-таблицы «Общие» и «Устройство», раздел i18n со счётчиками, риск №4, AC4, AC5. Остальные разделы (§7.1 полностью, К1–К3/К6–К8, AC1–AC3/AC6–AC11, скоуп/не-скоуп, классы риска §2.6, план автотестов, откат, release-артефакты) дельта не трогает — они наследуются из r1 (раздел ниже). ## Как проверялось 1. Прочитано текущее тело issue #598 целиком (`gh issue view 598 --json body`, 312 строк) и все 4 комментария (`gh issue view 598 --json comments`). 2. Открыт архивный `docs/reviews/SPEC-REVIEW-598-r1.md` — проверено, что находка M1 сформулирована именно так, как её описывает текущий раздел «Закрытие раунда r1» ниже (сверка дословная). 3. Построчно сверены места, где дельта обязана была появиться, с текущим текстом ТЗ: - К5 (новый контракт) присутствует и называет оба сообщения о состоянии поимённо со ссылкой на решение владельца; - УX-таблица «Общие настройки», строка «Солнце», явно оставляет `gs.sun_missing` callout'ом; УX «Устройство на плане» явно называет карточку («Действие по нажатию»), где остаётся callout'ом `marker.run_target_gone`, — то есть седьмое пояснение из r1-находки больше не висит без места; - раздел i18n предметно: «Итого 24 ключа, 96 строк» вместо прежних 26/104; таблица разбивки 12+12 ключей × 4 языка = 48+48 строк сходится арифметически (проверено умножением, не переписано на слово); - список «переиспользуются и не удаляются» в i18n явно включает и `gs.sun_missing`, и `marker.run_target_gone` — они не попадают ни в один список переносимых ключей; - AC5 добавлен, доказательство — блок в смоках «Общих» и «Устройства» с фикстурой без `sun.sun` и черновиком с несуществующей целью, страж — новый мутант `state-callout-hidden-under-help`; - риск №4 переписан под этот же риск явно («соблазн спрятать предупреждение заодно... запрещено К5 и сторожится мутантом»); - нумерация AC — пересчитана таблица ТЗ: AC1…AC11, встроен новый AC5, дальше сдвинуто на один без пропусков и дублей (сверено построчно, не на слово автора). 4. Отдельно проверено новое наблюдение автора, не бывшее находкой r1: перенос `gs.hint` из `src/i18n/support/*.json` в основной каталог как `gs.card_fills.help`. `grep -rn "gs.hint\|gs\\.hint" src/` и чтение `src/editors/general-settings-dialog.ts` / `src/i18n/support/*.json` подтверждают заявленное — ключ реально живёт в `support/*.json` и читается через `supportT(...)`, а `_help()` действительно резолвит `${string}.help` через основной `_t`. Значит перенос текста между словарями, который называет ТЗ, — не выдумка, а реальное расхождение путей чтения; `test/i18n-dead-keys.test.mjs` реально существует и действительно сливает оба словаря (файл присутствует в дереве), так что заявление «оставленная копия покраснит его» проверяемо, а не голословно. 5. Терминология «карточки-группы» — уже используется `docs/USER-GUIDE.ru.md:572` для диалога комнаты (#594); r1 это подтвердил, дельта термин не меняет — унаследовано. 6. `git branch -a`, `git log --oneline -3`, `git status` — подтверждают отсутствие кода и то, что HEAD стоит ровно на коммите публикации r1-документа; материал для код-ревью по этому issue не существует, гейты (typecheck/test/build) неприменимы на этапе ревью ТЗ, как и в r1. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | M1 (Medium) — судьба `marker.run_target_gone` не решена; счётчик i18n «7 пояснений» расходится с 6 явно поимёнными парами «карточка → текст» | Автор задал продуктовый вопрос владельцу (обязателен, т.к. решение 18.09 «под «?» уезжает всё» — продуктовое, распространять ли его на предупреждения — тоже продуктовый вопрос, а не только технический выбор варианта а/б, которые оба предложил r1); владелец ответил в комментарии: оба сообщения остаются видимыми callout'ами. ТЗ переписано: добавлен К5, AC5, УX-таблицы обеих форм явно называют карточку/место, где остаётся callout, риск №4 и мутант `state-callout-hidden-under-help` добавлены, счётчик i18n пересчитан на 24 ключа/96 строк | К5 (строка «Сообщения о состоянии остаются на виду»); AC5 в таблице критериев; УX-таблица «Общие настройки» строка «Солнце» и «Устройство на плане» строка «Действие по нажатию»; раздел i18n «Итого 24 ключа, 96 строк»; раздел «Риски» пункт 4 | | Сопутствующее наблюдение автора (не было находкой r1, вскрыто при разборе M1) — `gs.hint` читается из `support/*.json`, а не из основного словаря; перенос под «?» — это перенос ключа между словарями, а не только разметки | Явно описано отдельным разделом «`gs.hint` лежит в другом словаре»; в i18n-разделе прямо сказано: переезжает как `gs.card_fills.help`, копия в support удаляется во всех четырёх локалях, `test/i18n-dead-keys.test.mjs` покраснит при забытой копии | Раздел «`gs.hint` лежит в другом словаре» в «Аналитике»; раздел i18n, абзац «Отдельно: `gs.hint` переезжает...» | Оба пункта закрыты текстом ТЗ, а не заявлением «сделано»: место, счётчик и страж названы конкретно и проверяемы по самому тексту, без необходимости угадывать поведение реализации (ровно то, чего не хватало для AC4 в r1). ## Унаследовано из r1 Без повторной проверки приняты выводы `docs/reviews/SPEC-REVIEW-598-r1.md` (материал: `dev`@ `958ab36652eb`, дерево `232dd22abb22fc04ea01c124ac4332a6060a4fc8`), поскольку дельта r1→r2 их не затрагивает: - полнота обязательных разделов §7.1 (сценарий, что человек увидит, проблема, скоуп/не-скоуп, контракт поведения, УX, модель данных и миграция, i18n, AC, план автотестов, риски, откат, release-артефакты) — дельта не удаляла и не переименовывала ни один раздел, только правила К4, добавила К5 и AC5, пересчитала i18n; - К1–К3, К6–К8 — запись в те же ключи, поведение сохранения/отмены, `hp-color-opacity`, классы-опоры `.srcrow`/`.dispsection`/пр., клавиатура/44px, граф загрузки — не упомянуты дельтой, текст не менялся; - AC1–AC3, AC6–AC11 (нумерация до AC5 не сдвигается, после — сдвигается на один без потери содержания, проверено построчно в п.3 «Как проверялось») — доказательства и способы покраснения идентичны r1; - скоуп/не-скоуп — идентичен, сверен построчно на этапе r1 с архивным `SPEC.md` эпика #591 и с #594 — дельта эту границу не трогает; - 11 golden-сцен AC8 (было AC7 в r1) — тот же список имён, дельта не добавляла и не убирала сцены; - классы риска §2.6 — не переписывались; - терминология «карточки-группы» (USER-GUIDE.ru.md:572) и минимум 44 px (TOUCH-SUPPORT.md) — не переписывались; - существование примитивов набора (`form-kit.ts`: `formCard`, `formRow`, `segmented`, `colorRow`) и факт, что `hpf-switch` в коде не существует и правильно помечен «принято предположительно», — дельта эти утверждения не касается. ## Находки Не найдено. Дельта закрывает M1 полностью и по существу (не декларативно): продуктовая часть вопроса ушла владельцу и получила прямой ответ, техническая часть (счётчики, место словаря) решена автором самостоятельно и корректно, с проверяемыми доказательствами. Новых догадок, выданных за факт, дельта не вносит — оба новых утверждения (перенос словаря, арифметика счётчиков) проверены по коду и по самому тексту таблиц, а не приняты на слово. ## Что проверено и признано корректным - М1 закрыт: `marker.run_target_gone` явно назван callout'ом карточки «Действие по нажатию», `gs.sun_missing` — callout'ом карточки «Солнце»; ни один не участвует в переносе под «?»; AC4 («абзацев-пояснений... не осталось») теперь непротиворечив относительно AC5 («сообщения о состоянии остались видимыми»), поскольку ТЗ явно развело термины «пояснение» и «сообщение о состоянии» и использует слово «callout» только для второго — код-ревьюеру не придётся гадать, что считается нарушением AC4. - Продуктовый вопрос действительно был продуктовым (что человек видит — предупреждение или скрытая подсказка) и действительно ушёл владельцу, а не решён автором за него; технический побочный вопрос (словарь) автор решил сам, как и предписывает §7.1 — граница между двумя видами вопросов соблюдена корректно. - Счётчики i18n пересчитаны верно и согласуются с комментарием автора «было 26/104, стало 24/96» — проверено умножением по таблице, а не переписано на слово. - `gs.hint`/`supportT`/`_help()`/`test/i18n-dead-keys.test.mjs` — реальные пути в коде, наблюдение автора не является отвлечённым утверждением. - Материал раунда r1 (SHA `958ab36652eb`, дерево, блоб тела issue) не осиротел: `dev` не двигался вперёд относительно r1 непредвиденным образом, HEAD рабочей копии — коммит публикации того же документа; отдельная реставрация по дереву/блобу не потребовалась. ## Чего не проверял - Не проверял `fr`-локаль на реальные значения строк (унаследовано из r1 — там же не проверялось предметно, только совпадение счётчика «4 языка» с существующими словарями `en/ru/de/fr`). - Не сверял архивный `SPEC.md` эпика #591 построчно — он не входит в это репо (не найден ни по `find . -iname "SPEC.md"`, ни в файловой системе с иным путём), это внешний присланный документ; ТЗ и r1, и текущая редакция сознательно берут из него только раскладку, отклонённые части (смена поведения, компас, инлайн-цвет и т.д.) не входят в скоуп и не требуют сверки. - Не запускал `npm test`/`npm run typecheck`/`npm run build` — кода по issue нет (ветки `issue/ 598-*` не существует), гейты неприменимы на этапе ревью ТЗ; они станут предметом код-ревью. - Не оценивал будущую формулировку `docs/USER-GUIDE.ru.md` для новой раскладки трёх диалогов — этот документ обновляется на этапе кода (Release-артефакты ТЗ это явно называют), сейчас сверялась только терминологическая преемственность («карточки-группы»), не текст будущей правки. ## Вердикт Единственная находка предыдущего раунда закрыта по существу: продуктовый вопрос решён владельцем, техническое наблюдение — автором, оба с явными, проверяемыми следами в тексте ТЗ (К5, AC5, УX-таблицы, счётчики i18n, риск №4, новый мутант). Дельта не вносит новых High или Medium находок и не разрушает ни один вывод r1: наследуемые разделы не затронуты, а точки соприкосновения с дельтой (AC4/AC5, i18n-таблица) проверены заново и непротиворечивы. ТЗ готово к разработке. `Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0` --- ## Материал раунда - Ветка: `dev`, тот же коммит, что и в r1 — `958ab36652eb` (кода по issue #598 не появлялось между раундами); рабочая копия ревью — `ac1d25a08cb6da1e4154e857df761504a7f79d1d` (коммит публикации `SPEC-REVIEW-598-r1.md`). - Тело issue снято `gh issue view 598 --repo Matysh/houseplan-card --json body` 2026-09-19, 312 строк — предмет этого раунда; официальный sha256 нормализованного тела для якоря пишет конвейер публикации, как и в r1 (issue #414), самостоятельный расчёт здесь не приводится во избежание расхождения метода нормализации с конвейером. - Комментарии issue на момент ревью: 4 (`gh issue view 598 --json comments` → `length: 4`) — вердикт r1, ответ автора (M1 + открытый вопрос), ответ владельца, финальный ответ автора. --- ## Материал раунда - Ветка: `dev`, коммит `ac1d25a08cb6` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `d80ad22ec697829c20bd27b1a0cd5bda4d600e55` ``` git log --all --format='%H %T' | grep d80ad22ec697 ``` - Тело issue: `a81e7c46b347ae3eb2a0de35e30606c335c901733d52a04e1019a937a6d737fb` - Вердикт конвейера: `green` · High 0