mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 13:18:58 +00:00
109 lines
14 KiB
Markdown
109 lines
14 KiB
Markdown
# 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`, скоуп не расширен, трейлеры корректны, инфраструктурный трек применён правомерно.
|
||
|
||
---
|
||
|
||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/586-screenshots-strict-mode`, коммит `21923c344de0` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||
- Дерево материала: `ebc8db699ad297494bc533175728e18947afef32`
|
||
```
|
||
git log --all --format='%H %T' | grep ebc8db699ad2
|
||
```
|
||
- Тело issue: `18937affcac4a9658ede2b880f86cf8f3c1a1e78f4b87f6823d451e5ed313c42`
|
||
- Вердикт конвейера: `green` · High 0
|