diff --git a/docs/reviews/SPEC-REVIEW-598-r2.md b/docs/reviews/SPEC-REVIEW-598-r2.md new file mode 100644 index 00000000..6d4a4883 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-598-r2.md @@ -0,0 +1,193 @@ +# 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