Files
houseplan-card/docs/reviews/SPEC-REVIEW-44-r2.md
T
2026-08-30 10:45:00 +00:00

19 KiB

SPEC-REVIEW-44-r2

  • Issue: https://github.com/Matysh/houseplan-card/issues/44
  • Этап: spec (ревью ТЗ, PROCESS.md §2.4)
  • Заход: r2 · блокирующих циклов израсходовано 1/4 (потрачен на r1, красный)
  • ТЗ: docs/specs/044-filter-grouping-policy.md, ревизия 3, зафиксирована коммитом ba568763 («docs: #44 spec revision 3 per SPEC-REVIEW-44-r1», единственный файл, 34 добавлено / 16 удалено — git show --stat ba568763)
  • Предыдущий раунд: docs/reviews/SPEC-REVIEW-44-r1.md, вердикт красный, получен на ревизии 2, коммит 3d4c5090
  • Вердикт: жёлтый

Скоуп ревью

Разбор по дельте (PROCESS.md §2.9): дельта локальна — один файл ТЗ, изменения сосредоточены ровно вокруг двух High из r1 (H1, H2), новых подсистем не задето, продуктовый код не менялся ни разу с r1 (только докс-коммиты df1dbccf, ba568763). Полный повторный разбор всего документа не требуется; передельчиваю только то, что задевает дельта, и то, что дельта могла сломать по соседству (см. «Унаследовано» ниже за границей проверки).

Дельта: git diff 3d4c5090..ba568763 -- docs/specs/044-filter-grouping-policy.md — 34/16 строк, три места: заголовок ревизии, «Что человек увидит»/раздел «Скоуп → 2» (H1), Контракт п.1a + AC4b + План автотестов + Риски (H2).

Закрытие раунда r1

Находка r1 Чем закрыта в ревизии 3 Где это видно
H1 — сценарий/AC4 утверждали, что причина excluded_integration сегодня не существует и появляется на «Скрытых»; факт неверен, категория hidden структурно недостижима для такого кандидата Раздел «Что человек увидит» и «Скоуп → 2» переписаны: причина уже существует и рендерится на «Доступны» (#29); скоуп задачи сведён к добавлению плейсхолдера {integration} в текст, вкладка/значение reason не меняются, перенос категории явно объявлен нерешаемым здесь вопросом. AC4 переписан под факт docs/specs/044-filter-grouping-policy.md:23-27, 70-78, 138-140; факты перепроверены чтением кода — совпадают (houseplan-editor-runtime.ts:7637, src/i18n/ru.json:484, row.integration уже в структуре строки, houseplan-editor-runtime.ts:11842)
H2 — roomClimateMap хардкодит EXCLUDED_DOMAINS в обход настраиваемого резолвера; Контракт №3 («один источник для всех потребителей») был неверен Добавлен пункт контракта 1a: roomClimateMap переводится на тот же резолвер, что и discovery; явный climate-opt-in (optClimate) остаётся сильнее — ветка не трогается; добавлен AC4b (юнит) и регресс-риск docs/specs/044-filter-grouping-policy.md:99-105, 141-144, 173-175; проверено чтением: optClimate действительно существующая ветка (devices.ts:1488-1492), вызовы roomClimateMap в трёх местах (devices.ts:1546, houseplan-card.ts:11870, space-render.ts:301) и существующие тесты #317 (test/devices.test.mjs:1304-1426) зовут функцию без нового параметра — расширение сигнатуры опциональным параметром не сломает эти вызовы, что подтверждает реалистичность «default-путь байт-в-байт» из AC4b
L1 — термин «Доступные» вместо канонического «Доступны» (Low, правится при следующей правке текста, отдельного цикла не требовал) Не исправлено единообразно См. ниже, «Находки → L1 (сохраняется)»

H1 и H2 закрыты по существу: факты исправлены, новый контракт/AC технически реализуем (проверено чтением кода, а не на слово автора). Но правка H1 задела не весь документ — два места вне тронутого диапазона остались буквально с прежним (уже опровергнутым) утверждением. Это отдельная находка этого раунда, см. ниже.

Находки

M1 — Ревизия 3 не убрала два места, где документ прямо утверждает то, что H1 уже опроверг: причина будто появляется на «Скрытых»

Файл: docs/specs/044-filter-grouping-policy.md, «Риски» (строка 170) и «Принятые предположения» (строка 201).

Что в документе сейчас:

  • Строка 170 (не менялась при правке H1): «Реason-вкладка «Скрытые» получает новый класс записей — объём ограничен существующим механизмом вкладки; смок проверяет отсутствие дублей.»
  • Строка 201 (не менялась при правке H1): «Причина показывается на «Скрытых», НЕ в отдельной новой вкладке.»

Почему это противоречие, а не стилистика: ровно эта пара утверждений — причина оседает на «Скрытых» — и была фактической ошибкой H1 в r1 (строка 183 ревизии 2, процитирована в SPEC-REVIEW-44-r1.md как то, что «маскирует» техническое противоречие). Ревизия 3 переписала «Что человек увидит», «Скоуп → 2» и AC4 так, что причина теперь однозначно остаётся на «Доступны» (строки 72-78, 138-140: «вкладка НЕ меняется … перенос категории … здесь не открывается»). Но правка не дотянулась до «Риски» и «Принятые предположения» — там буквально сохранился текст, отрицающий собственное исправление документа. Это не гипотетический риск: раздел «Риски» ссылается на несуществующий по факту «новый класс записей» на «Скрытых» и требует от смока проверки «отсутствия дублей» там, где по исправленному контракту вообще ничего нового не появляется; раздел «Принятые предположения» формулирует как «предположение» то, что теперь прямо противоречит принятому (и проверенному чтением) решению.

Кому это мешает: реализатор или автор смока, читающий «Риски»/ «Предположения» отдельно от «Скоупа» (частый паттерн — эти разделы короче и их пробегают первыми), получит указание строить или проверять поведение вкладки «Скрытые», которого сама задача явно не создаёт. Ложное срабатывание дублей на «Скрытых» либо реализация лишней ветки категоризации — именно тот класс расхождения, который сделал r1 красным.

Почему Medium, не High: сами AC (AC4, AC4b) и нормативный «Контракт поведения» однозначны и не зависят от этих двух абзацев — блокирующей невыполнимости AC нет, в отличие от r1, где AC4 была буквально невыполнима как написана. Дефект — во внутренней непротиворечивости документа, задача которого — заново подтверждённые факты (H1). Это находка в скоупе ревизии, которую делает именно этот раунд (H1-правка), поэтому чинится тем же циклом, без отдельного issue (PROCESS.md, решение 2026-08-19, #202).

Что нужно поправить: строку 170 переписать/удалить (никакого нового класса записей на «Скрытых» нет — риск, который стоит сформулировать вместо неё, если он есть: неверно подставленный {integration} при отсутствующем platform-имени, откат на обобщённый текст); строку 201 удалить из «Принятых предположений» (это больше не предположение, а опровергнутый факт, раздел уже содержит корректную версию в теле ТЗ).

L1 (сохраняется, Low, не блокирует) — Термин вкладки всё ещё «Доступные» в трёх местах, «Доступны» — в одном

Файл: строки 13, 49, 196 — «Доступные»; строка 73 (новый текст ревизии 3, часть правки H1) и строка 138 (AC4) — «Доступны», канонический термин docs/USER-GUIDE.ru.md:785 / device_inbox.tab_available.

Ревизия 3 внесла корректный термин именно там, где переписывала текст под H1, но не выровняла остальные вхождения — документ сейчас использует оба варианта параллельно. Как и в r1: не блокирует, правится автором в рабочем порядке при следующей правке текста; отдельного цикла не требует.

Унаследовано из r1

Без повторной проверки приняты (проверялись в SPEC-REVIEW-44-r1.md на коммите 3d4c5090, продуктовый код с тех пор не менялся — только докс- коммиты df1dbccf, ba568763, git log --oneline 3d4c5090..HEAD -- src пуст):

  • SCOPE.md: задача закрывает пункт J6/тезис issue («третий вариант» запрещён), конфликта со SCOPE нет.
  • Трек — полный (не small), файл ТЗ обязателен и существует; все обязательные разделы §7.1 присутствуют.
  • Резолвер group_lights (devices.ts:1033/1098) и exclude_integrations (houseplan-card.ts:3924-3925, space-render.ts:245-246) описаны точно, включая replace-семантику и [] как валидное «ничего не исключать».
  • EXCLUDED_DOMAINS (rules.ts:12, 13 доменов) совпадает с описанием.
  • scripts/config-field-registry.mjs:42-70 — статусы decision-required, паспорта allow-extra обоих ключей, план перевода в current технически корректен и имеет прецедент (6 других полей).
  • expected_rev/транзакционный паттерн Сохранить, прецедент удаления ключа при возврате к дефолту (settings.weather_entity) — реальны.
  • buildDevices(ctx) — чистая функция, план AC6 (общий вход превью/боевого пути без копии логики) реализуем технически.

Эти пункты дельтой ревизии 3 не задеты (правка ограничена ровно тремя абзацами, перечисленными в «Скоуп ревью» выше), поэтому переносятся без повторной сверки кода.

Как проверялось

  1. Восстановлен SHA r1 (3d4c5090) и вердикт из docs/reviews/SPEC-REVIEW-44-r1.md (документ называет SHA явно — не находка).
  2. git diff 3d4c5090..ba568763 -- docs/specs/044-filter-grouping-policy.md — единственный источник дельты этого раунда.
  3. git log --oneline 3d4c5090..HEAD -- src — пусто, продуктовый код не менялся; гейты (tsc, test, build, check-docs.mjs) неприменимы, как и в r1 (чистый докс-коммит, git show --stat ba568763 подтверждает единственный изменённый файл).
  4. Каждый факт из H1/H2-правок перепроверен чтением текущего кода (не на слово автора): houseplan-editor-runtime.ts:7629-7642 (reasonByBinding, integrationByBinding уже существуют в одной структуре — плейсхолдер {integration} дёшев в реализации), :11838-11851 (row.integration уже используется в соседней строке шаблона), src/i18n/ru.json:484 / en.json:484 (текущий обобщённый текст), devices.ts:1435-1533 (roomClimateMap, ветка optClimate:1488-1492, вызовы EXCLUDED_DOMAINS — единственное прямое вхождение в src/**, перепроверено grep -rn "EXCLUDED_DOMAINS\.has("), три вызова roomClimateMap (devices.ts:1546, houseplan-card.ts:11870, space-render.ts:301), тесты #317 (test/devices.test.mjs:1304-1426, 2157-2166) — сигнатура везде трёхаргументная, совместима с опциональным 4-м параметром.
  5. Полнотекстовый grep документа по «Доступны»/«Доступные»/«Скрыты» — выявил M1 и подтвердил L1.

Что проверено и корректно

  • Оба High из r1 закрыты по существу, не косметически: факты исправлены, новый контракт (1a) и AC4b реализуемы без структурных противоречий с существующим кодом (подтверждено чтением, включая совместимость с уже существующими тестами #317).
  • AC4 в новой формулировке однозначен и проверяем как написан (вкладка, текст причины, регресс-ветка на «значение reason не меняется»).
  • AC4b однозначен: три случая (интеграция вне действующего набора → климат участвует; интеграция в действующем наборе → не участвует; явный climate-opt-in побеждает) — тестируемы юнитом без реализации.
  • Ссылки на строки кода в новых абзацах (7637, 1491) точны, перепроверены заново на текущем дереве, а не унаследованы с доверием.

Чего не проверял

  • Полный повторный разбор документа вне дельты — по правилу «дельта локальна», см. «Унаследовано из r1».
  • Гейты tsc/test/build/check-docs.mjs/смоки/golden/backend/perf — неприменимо: с r1 не менялось ничего в src/**, test/**, demo/** (проверено git log --oneline 3d4c5090..HEAD -- src test demo — пусто).
  • Не проверялась реализуемость точного способа проброса {integration} в _t() (нужен ли новый параметр вызова на строке 11849) — это вопрос кода на этапе реализации, не ТЗ; сам факт наличия данных (row.integration, integrationByBinding) подтверждён, механизм интерполяции _t(key, params) подтверждён существующими вызовами в другом месте того же файла.

Итог

Вердикт: жёлтый. High-находок нет — оба High из r1 закрыты по существу и подтверждены чтением кода, а не на слово автора. Один Medium в скоупе (M1): ревизия 3 не убрала два места («Риски», «Принятые предположения»), которые буквально утверждают опровергнутый H1 факт («причина показывается на «Скрытых»») — внутреннее противоречие документа ровно на той оси, из-за которой r1 стал красным. Чинится без нового цикла (правка двух абзацев). Low (L1, термин вкладки) — сохраняется с r1, не блокирует.