diff --git a/docs/reviews/SPEC-REVIEW-210-r2.md b/docs/reviews/SPEC-REVIEW-210-r2.md new file mode 100644 index 00000000..5c05fb47 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-210-r2.md @@ -0,0 +1,168 @@ +# Ревью ТЗ — issue #210, цикл r2 + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/210 +- **ТЗ:** `docs/specs/210-fixed-floor-card.md` (коммит `6c3d376`, ветка + `issue/210-fixed-floor`) +- **Предыдущий цикл:** `docs/reviews/SPEC-REVIEW-210-r1.md` — жёлтый, + High: 0, Medium: 1 (M1, в скоупе), Low: 1 (L1) +- **Трек:** обычный (аналитика явно исключила `small`/`trivial`; сложность + 6/10, риск 6/10, метка `small` на issue отсутствует) — лимит циклов 4 +- **Вердикт:** зелёный · цикл r2/4 · High: 0 · Medium: 0 + +## Скоуп ревью + +Проверялось ТЗ `docs/specs/210-fixed-floor-card.md` в редакции коммита +`6c3d376` как артефакт этапа `S4-spec-review`, второй цикл. Задача — не +переповторить r1 целиком, а (1) убедиться, что правки по M1 и L1 сделаны так, +как обещано в комментарии автора, (2) не подтвердить это на слово, а +независимо перечитать документ целиком и код, к которому апеллирует новый +текст, и (3) заново пройти обязательные разделы §7.1 PROCESS.md и AC1…AC12 +свежим взглядом, а не только смотреть на диф — чтобы не унаследовать слепое +пятно предыдущего цикла. Продуктового кода по #210 всё ещё не существует +(диапазон коммитов ветки — только документация), поэтому гейты §8 PROCESS.md +к этому циклу неприменимы, как и в r1. + +## Как проверялось + +1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` целиком (в этой + сессии, без переноса контекста из r1 — ревью ТЗ и код-ревью обязаны идти + в разных сессиях по §6 PROCESS.md; здесь то же требование соблюдено для + независимости повторного цикла ТЗ). +2. Прочитано тело issue #210 и все шесть комментариев, включая аналитику, + вердикт r1 и комментарий автора о правках (`6c3d376`, «M1: … L1: …»). +3. Получен точный диф правки: `git diff 0bd6094 6c3d376 -- + docs/specs/210-fixed-floor-card.md` — 12 добавленных строк, ни одной + удалённой; сверен построчно с тем, что обещано в комментарии автора и с + тем, что требовало M1/L1. +4. Прочитан файл ТЗ `210-fixed-floor-card.md` целиком заново, в текущей + редакции (337 строк), а не только изменённые фрагменты. +5. Перечитаны `docs/CONFIG-COMPATIBILITY.md` целиком и профильные разделы + `docs/UX-MODES.md` (header/tabs, строка 81: «Header in View: space tabs, + device count, zoom cluster. Nothing else.») и `docs/TOUCH-SUPPORT.md` + (canonical-tag правило) — те же документы, что и в r1, чтобы проверить, + не создал ли новый текст противоречие, которого не было в r1. +6. Прочитан `docs/USER-GUIDE.ru.md` в местах, которые ТЗ обязуется + обновить в release-артефактах: карточка card-options (строка 54, 125 — + `default_floor` уже называется «Стартовое пространство» в действующем + guide, то есть новый термин ТЗ **Initial space / Стартовое пространство** + не изобретён, а взят из текущего интерфейса) и раздел «Несколько карточек + и стартовые пространства» (строки 1327-1341) — подтверждает, что именно + это описание (`default_floor` как единственная защита) ТЗ обязано + уточнить в §15, и это уже учтено (не новая находка, ещё в r1). +7. Проверено соответствие нового текста коду: + - `src/editor.ts` (`_valueChanged`, объединение через `{ ...this._config, + ...ev.detail.value }`) — подтверждён паттерн, на который ссылается M1 + r1 («никакого существующего прецедента явной зачистки пустого поля + нет»). Новый текст §6.2 закрывает именно этот пробел явным требованием + «удалить собственный ключ `floor` из выдаваемого card config целиком», + а не полагаться на то, что пустая строка сама попадёт в уже описанный + invalid-путь. + - Существующие смоки `demo/smoke_nav_persist.mjs`, `demo/smoke_kiosk.mjs` + присутствуют в репозитории; `demo/smoke_fixed_floor.mjs` — нет, как и + ожидается по плану тестирования (§13): новый смок создаётся в + реализации, а не сейчас. +8. Проверено, что новый §9.1 «i18n» не дублирует и не противоречит + существующим требованиям §9 (invalid-state accessibility) и §6.2 (GUI + labels) — он их резюмирует под отдельным заголовком, не вводя новых + требований к продукту. +9. Код не запускался, гейты не прогонялись — на этапе ревью ТЗ продуктового + кода нет, как и в r1 (PROCESS.md §8 относит гейты к код-ревью). + +## Проверка правок по M1 и L1 + +### M1 (было Medium, в скоупе) — закрыто + +Ровно в заявленном месте (§6.2, после абзаца про dropdown/textbox fallback) +добавлено: + +> Выбор «не закреплять» обязан удалить собственный ключ `floor` из выдаваемого +> card config целиком. GUI не записывает `floor: ''` или `floor: null`: эти +> значения намеренно остаются невалидными для явно заданного YAML, а +> отсутствие свойства возвращает legacy navigation согласно §6.1. + +Это прямо снимает найденное в r1 противоречие: пустой выбор GUI теперь +однозначно ведёт к обычной (legacy) навигации, а не к невалидному `floor: ''` +и, как следствие, к error state. Зеркальный пункт добавлен и в §16 +(«Принятые предположения», п.8) той же формулировкой, как и просил r1. Оба +места непротиворечивы друг другу и §6.1 п.3 (`''`/`null` остаются невалидными +для **явного** YAML, что осталось неизменным и корректным — M1 просил +разграничить «GUI никогда не пишет эти значения» и «явный YAML с этими +значениями всё ещё невалиден», а не менять §6.1). AC8 («GUI … умеет очистить +`floor`») теперь однозначно проверяем: тест должен убедиться, что после +очистки ключ отсутствует в объекте конфигурации, а не равен `''`/`null`. + +Находка полностью устранена, без побочных противоречий с остальным текстом. + +### L1 (было Low, на решение автора) — закрыто + +Добавлен отдельный подраздел **§9.1. i18n** сразу после §9 (invalid-state +контракт), формально выполняющий требование §7.1 PROCESS.md об отдельном +разделе i18n. Содержание не вводит новых по существу требований — оно +называет два конкретных файла (`en.json`/`ru.json`), явно запрещает зашитые +рантайм-строки и привязывает термин к уже принятому «space / пространство» — +то есть закрывает найденный формальный пробел, не расширяя скоуп. + +## Заново проверенные разделы (не только диф) + +- **Обязательные разделы §7.1 PROCESS.md** — по-прежнему все присутствуют, с + добавлением отдельного i18n-подраздела структура стала полнее, чем в r1. +- **AC1…AC12** — перечитаны заново; ни один не изменился текстом со времени + r1, добавление в §6.2/§16 делает AC8 более однозначным (см. M1 выше), не + ослабляя и не меняя ни один AC. +- **Непротиворечивость нового текста остальному документу** — §6.1 (типы и + invalid-список), §9 (когда рендерится error state), §10 (fixed instance не + очищает и не мигрирует nav record) и §16 п.6 (invalid `floor` не + откатывается к `default_floor`) не пересекаются и не конфликтуют с новым + текстом об удалении ключа при очистке GUI: очистка через GUI — это + отсутствие `floor`, а не невалидное значение, поэтому она не проходит через + invalid-ветку вовсе, что и было целью правки. +- **SCOPE.md** — правка не меняет ни персону, ни поверхность, ни объём + видимого изменения по сравнению с r1; продуктовая часть контракта + по-прежнему исходит от владельца («Технические детали решаются свободно на + ревью»), а не является догадкой автора, выданной за факт. +- **Открытые продуктовые вопросы** — отсутствуют, как и в r1; оба + устранённых пункта были техническими уточнениями формулировки, не + продуктовыми развилками, и не требовали обращения к владельцу. + +Новых находок — ни High, ни Medium, ни Low — при независимом перечтении +целиком не появилось. + +## Проверено и признано корректным + +Всё, что было проверено и признано корректным в r1 (причинная цепочка §3/§7, +структура документа, соответствие `docs/UX-MODES.md`, `docs/TOUCH-SUPPORT.md`, +`docs/CONFIG-COMPATIBILITY.md`, AC1…AC12, план тестирования, риски и откат), +остаётся в силе — правка `6c3d376` его не затронула ни одной строкой (диф +только добавляет текст, ничего не удаляет и не переформулирует). Дополнительно +в этом цикле подтверждено: + +- точное соответствие внесённых правок тому, что было обещано в комментарии + автора (ничего не забыто, ничего лишнего не изменено походя); +- отсутствие побочных противоречий между новым текстом и остальными + разделами документа; +- термин **Initial space / Стартовое пространство** не изобретён — совпадает + с уже используемым в `docs/USER-GUIDE.ru.md:54,125` названием поля + `default_floor`. + +## Чего не проверял + +- **Фактическая работоспособность будущей реализации** — на этапе ревью ТЗ + продуктового кода не существует. +- **`ha-form`/`ha-selector` поведение при очистке optional select** — + по-прежнему не тестировалось эмпирически; текст ТЗ теперь однозначен + («удалить ключ целиком»), но соответствие итоговой реализации этому + контракту — предмет код-ревью, не этого цикла. +- **`demo/smoke_fixed_floor.mjs`, `smoke_nav_persist.mjs`, `smoke_kiosk.mjs`** + не запускались — нового смока ещё нет, существующие два не относятся к + этому циклу (нет кода, который они могли бы проверить). +- Гейты `typecheck`/`test`/`build`/smoke/golden не прогонялись — раздел + неприменим на этапе ревью ТЗ (PROCESS.md §8 относит их к код-ревью). + +## Итог + +Оба цикла r1 (M1, L1) закрыты именно так, как было обещано, без побочных +противоречий и без сужения/расширения скоупа. Независимое повторное чтение +всего документа не выявило новых блокирующих или Medium-находок. Открытых +продуктовых вопросов нет. ТЗ готово к «Готово к разработке». + +**Вердикт: зелёный · цикл r2/4 · High: 0 · Medium: 0**