Files
houseplan-card/docs/reviews/SPEC-REVIEW-210-r2.md
T
2026-08-19 22:26:20 +00:00

14 KiB

Ревью ТЗ — 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