Files
houseplan-card/docs/reviews/CODE-REVIEW-586-r1.md
T
2026-09-16 19:23:02 +00:00

14 KiB
Raw Blame History

CODE-REVIEW-586-r1

Issue: #586 — Validate: строгий режим свежести скриншотов документации не включается (сравнение с многострочным выводом CLI). Трек: инфраструктурный (§1 PROCESS.md) — ни один файл класса A не тронут, ТЗ/S1–S6 не требуются. Материал: 21923c344de012c8849083b68200a6a205a53649 (ветка issue/586-screenshots-strict-mode), два коммита от 303c0843 (dev на момент ветвления):

  • 245b7f98 — фикс разбора вывода classify-changes.mjs в validate.yml;
  • 21923c34 — пересъёмка и приёмка 4 кадров документации (снятый долг #479).

Заход r1, блокирующих циклов израсходовано 0 из 4.

Скоуп диффа

.github/workflows/validate.yml  | 10 ++++++----
docs/images/01-view-desktop.png | Bin
docs/images/02-view-touch.png   | Bin
docs/images/08-room-card.png    | Bin
docs/images/09-device-info.png  | Bin
docs/images/screenshots.json    | 40 ++++++++++++++++----------------
scripts/classify-changes.mjs    | 29 +++++++++++++++++++++++++++-
scripts/mutation-registry.mjs   | 15 +++++++++++++++
test/classify-changes.test.mjs  | 30 +++++++++++++++++++++++++++++

Все файлы — класс B (гейты/инструменты) и C (документация/скриншоты). Продуктовый код (src/**, custom_components/**) не тронут — классификация «инфраструктурная» верна.

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

Читал по порядку: docs/SCOPE.md, PROCESS.md (§1–§10), тело issue #586 и хендофф-комментарий автора, сам диффа. Продуктового поведения нет, поэтому docs/USER-GUIDE.ru.md и канонические документы подсистем нерелевантны.

Гейты — что прогнано и почему

Гейт Статус Комментарий
Validate на 21923c34 не перезапускал Подтверждён вводной задачи как success: https://github.com/Matysh/houseplan-card/actions/runs/35139056409. Дешёвые гейты (tsc, npm test, build+сверка бандла) считаю закрытыми этим прогоном
node --test --test-name-pattern="#586" test/classify-changes.test.mjs прогнан, зелёный 2/2 pass — оба новых теста-свидетеля
Мутация вручную (снял защиту так же, как патч в mutation-registry.mjs, прогнал тест) прогнан тест #586: режим гейта скриншотов… покраснел: expected 'strict', actual 'warn'. Working tree восстановлен после проверки (git status чист)
node scripts/mutation-gate.mjs --id=screenshot-freshness-never-strict прогнан, зелёный ok screenshot-freshness-never-strict: заявленный тест покраснел на мутанте · поймано 1 из 1 — независимое подтверждение официальным гейтом, не только ручной правкой
node scripts/mutation-gate.mjs --check прогнан, зелёный структурная проверка реестра, включая новый id — без FAIL
node scripts/check-docs.mjs --external --screenshots=strict прогнан, зелёный Documentation checks passed (7 files, 12 external links) — подтверждает, что отпечаток кадров действительно синхронизирован после второго коммита
node scripts/no-new-any.mjs --base origin/dev --head HEAD прогнан 0 добавленных строк в src/**/*.ts — неприменимо, но чисто
node scripts/smoke-select.mjs --base origin/dev --head HEAD прогнан «Исполняемого frontend-диффа нет» — смоки не выбираются, выбирать нечего
node scripts/process-gate.mjs прогнан, зелёный 2 коммита, диапазон origin/dev..HEAD, предупреждений 0
npx tsc --noEmit, полный npm test, npm run build+cmp бандла не прогонял отдельно покрыты зелёным Validate на этом SHA (см. выше); diff не трогает src/**, риска для бандла/тайпчека нет
npm run golden:verify не прогонял diff не меняет рендер карточки — визуального продуктового результата нет, только скриншоты документации
python -m pytest tests_backend не прогонял custom_components/**/*.py не тронут
performance-профили не прогонял не названы в AC, чувствительные пути (src/iso-*, src/live-*, src/render-*) не тронуты

Находки

Блокирующих (High) находок нет. Medium в скоупе или вне скоупа — нет.

Low, снятая с записью: комментарий во validate.yml («#586: CLI отдаёт ОДИН ответ…») и в classify-changes.mjs дублируют друг друга почти дословно — не мешает читаемости и не влияет на корректность, менять не требую.

AC — построчно

Приёмка из issue:

  1. «Свидетель краснеет на текущем коде: при heavy=true в выводе из двух строк выбранный режим обязан быть strict» — доказано автотестом test/classify-changes.test.mjs. Таблица «чем краснеет»:

    AC Чем доказан Чем краснеет
    AC1 (защитный: старая логика никогда не давала strict) node --test --test-name-pattern="#586" test/classify-changes.test.mjs Патч из mutation-registry.mjs id screenshot-freshness-never-strict (возвращает strict только для несуществующего события) — тест падает expected 'strict', actual 'warn'; дополнительно подтверждено mutation-gate.mjs --id=screenshot-freshness-never-strict → «поймано 1 из 1»

    Дополнительно тест assert.notEqual(run(['--heavy'], candidate), 'heavy=true', …) напрямую фиксирует регрессионный факт (вывод --heavy многострочный), из-за которого баг вообще возник — не дал ему тихо вернуться.

  2. «Кандидат с устаревшим отпечатком скриншотов краснеет в Validate» — проверено чтением, не исполнением полного Validate: validate.yml теперь вызывает node scripts/check-docs.mjs --external --screenshots=$mode, где $mode считает screenshotsGateMode() (класс strict при трейлере Release:, PR, schedule, workflow_dispatch full=true). Поведение check-docs.mjs --screenshots=strict на устаревшем отпечатке (падение с ERROR) — уже установленный факт из #479 и подтверждён самим текстом issue («Локально… check-docs.mjs --strict падает с ERROR»); в этом диффе check-docs.mjs не менялся. Комбинация «правильное значение считается» (доказано тестом п.1) + «правильное значение используется» (доказано вторым тестом «preflight спрашивает режим одним значением…», который проверяет содержимое validate.yml через regex) закрывает AC2 без необходимости гонять весь Validate.

  3. «Обычный push остаётся в warn» — доказано юнит-тестом: screenshotsGateMode({ eventName: 'push', headMessage: 'fix: x…' }) → 'warn'.

  4. «Заодно проверить остальные места, где вывод classify-changes.mjs разбирается сравнением строки целиком» — проверено чтением: grep по всем .github/workflows/*.yml и scripts/** на использования classify-changes.mjs показывает единственный паттерн вида [ "$var" = "…" ] — тот, что был пофикшен. Остальные вызовы (строки 265, 305, 329, 339, 347 в validate.yml) пишут вывод в $GITHUB_OUTPUT построчно (tee -a/>>) — это штатный формат ключ=значение, а не сравнение целой строки, и он не задет багом.

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

  • Второй коммит (пересъёмка скриншотов) — не самостоятельная работа над гейтом «раз уж я здесь»: он прямо мотивирован тем, что включение strict-режима без актуального отпечатка тут же покрасило бы кандидата, а «долг» скопился именно потому, что защита не работала. Это укладывается в рамку задачи, а не расширяет её.
  • Приёмка скриншотов — легитимным путём: docs-accept.mjs поддерживает --expect-change, и declared в screenshots.json (view-desktop, view-touch, room-card, device-info) совпадает с реально изменившимися 4 файлами из диффа. JSON не редактировался руками мимо инструмента.
  • Автор задокументировал двойной прогон капчура (11/11 байт-в-байт совпадение) как довод против шума раннера, и визуально сверил 4 разошедшихся кадра (таблица differences ≤0.17%, maxΔ≤17) — качество доказательства выше типового для подобной правки.
  • Трейлеры обоих коммитов: Issue: #586, User-Visible: no — корректно (изменение не меняет наблюдаемое поведение продукта; экран пользователя не задет, правится только CI и скриншоты документации).
  • mutation-registry.mjs: новый id screenshot-freshness-never-strict синтаксически и структурно корректен (--check проходит), а guard (--test-name-pattern="#586") точечно нацелен на добавленные тесты, не гоняя весь файл.
  • Working copy после моих проверок чиста (git status --porcelain пуст) — я не оставил артефактов в репозитории.

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

  • Полный npm test / npx tsc --noEmit / npm run build+cmp — не гонял отдельно, положился на зелёный Validate этого точного SHA (см. таблицу гейтов).
  • golden:verify, pytest tests_backend, performance-профили — не применимы к диффу (не трогает рендер/бэкенд/perf-пути).
  • Реальный сценарий «Validate на кандидате со специально устаревшим отпечатком краснеет end-to-end» не воспроизводился запуском workflow — закрыт комбинацией юнит-теста + чтения кода (см. AC2 выше), это осознанное решение, а не пропуск.

Материал раунда

  • SHA материала: 21923c344de012c8849083b68200a6a205a53649
  • Дерево: см. git show 21923c34^{tree}
  • Первый раунд — раздел «Унаследовано из r0» не требуется.

Вердикт

Зелёный. AC1–AC4 доказаны (автотест + мутационный гейт + чтение кода), защитный AC имеет полноценную таблицу «чем краснеет» с подтверждением через mutation-gate.mjs, скоуп не расширен, трейлеры корректны, инфраструктурный трек применён правомерно.


Материал раунда

  • Ветка: issue/586-screenshots-strict-mode, коммит 21923c344de0 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: ebc8db699ad297494bc533175728e18947afef32
    git log --all --format='%H %T' | grep ebc8db699ad2
    
  • Тело issue: 18937affcac4a9658ede2b880f86cf8f3c1a1e78f4b87f6823d451e5ed313c42
  • Вердикт конвейера: green · High 0