Files
houseplan-card/docs/reviews/CODE-REVIEW-248-r1.md
T
2026-08-22 22:37:51 +00:00

22 KiB
Raw Blame History

CODE-REVIEW-248-r2

  • Issue: #248 — «Оптимизировать» не идемпотентна после записи и reload
  • Ветка: issue/248-optimize-idempotence
  • ТЗ: docs/specs/248-optimize-idempotence.md (SPEC-REVIEW-248-r1, зелёный)
  • Этап: code (PROCESS.md §2.7), заход r2, трек обычный
  • Вердикт: зелёный

Расхождение с переданным заголовком задачи

Заголовок этого запуска называл «заход r1 · блокирующих циклов 0/4». Это не соответствует действующему состоянию issue и было исправлено по факту, а не принято на слово:

  • в комментариях #248 уже есть код-ревью с вердиктом красный · заход r1 · блокирующих циклов 1/4 · High: 1 (находка H1 — устаревший docs-отпечаток);
  • документ того раунда уже закоммичен в дерево задачи — docs/reviews/CODE-REVIEW-248-r1.md, коммит e468414 (текущий HEAD);
  • автор закрыл H1 и владелец явно написал «Возвращаю задачу в code review для захода r2».

Соответственно этот документ — -r2, а не -r1; публикация в docs/reviews/ не должна затирать уже существующий CODE-REVIEW-248-r1.md. Бюджет циклов после этого зелёного вердикта остаётся 1/4 (зелёный вердикт цикл не тратит, #227).

Отдельно к самому раунду r1: его вердикт не называл SHA, на котором получен — только производная находка H1 упоминает HEAD (cbdd5a7) внутри своего текста. Формально это ровно то расхождение, которое делает раунд 2 менее воспроизводимым, чем должен быть; в этот раз SHA раунда явно зафиксирован (см. ниже), чтобы для потенциального r3 не потребовалось той же реконструкции.

SHA раунда r1 (материал предыдущего вердикта): cbdd5a7e73b2b7afd1e05bee453154d3e75cd60b (реконструирован из текста находки H1 — там же единственная явная привязка). SHA этого раунда (r2, текущий HEAD): e468414c241900f2175923cfb0901a73e86e9767.

Скоуп ревью

Предмет раунда — дельта git diff cbdd5a7..HEAD, а не задача целиком (PROCESS.md §2.10): три коммита сверх cbdd5a7 —

  • 5ad4280 test: complete Optimize storage fixtures (User-Visible: no)
  • 120b491 test: require schema-valid Optimize fixture (User-Visible: no)
  • e468414 docs: review document for #248 (User-Visible: no, только docs/reviews/CODE-REVIEW-248-r1.md, класс C, не участвует в проверке статуса issue по process-gate)

Дельта касается ровно 4 файлов кроме самого review-документа:

docs/images/05-plan-context-tray.png              | Bin 345560 -> 345559 bytes
docs/images/screenshots.json                      |  24 +--
test/fixtures/optimize-storage-roundtrip.json     |   4 +
tests_backend/test_coordinate_canonicalization.py |   2 +

src/** и custom_components/houseplan/**/*.py в дельте не тронуты — продуктовый фикс идемпотентности (src/plan-optimizer.ts) целиком лежит в cbdd5a7 и уже был разобран в r1. Дельта — это (а) закрытие H1 (docs fingerprint) и (b) самостоятельная находка автора по итогам первого CI: общая fixture не проходила backend-схему без view_box.

Разбор оставлен полным по чувствительным точкам, а не «только H1»: раунд не локален формально (дельта пересекает docs-гейт и backend-fixture-контракт AC4), поэтому ниже переисполнены typecheck/test/build/check-docs/process-gate и целевой AC5-смок, а не только прочитан diff.

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

Команда Результат
npx tsc --noEmit green, без вывода
npm test 1116/1116 pass, 0 fail, 0 skip
npm run build green
cmp dist/… custom_components/…/frontend/… && cmp dist/… demo/srv/assets/… все три бандла идентичны байт-в-байт
git status --porcelain после build пусто — закоммиченный бандл уже актуален
node scripts/check-docs.mjs green — «Documentation checks passed (7 files, 10 external links)» — H1 закрыт
node scripts/smoke-select.mjs --base cbdd5a7 --head HEAD «Исполняемого frontend-диффа нет (src/**/*.ts не тронут)» — дельта раунда не выбирает ни один смок
node scripts/smoke-select.mjs --base origin/dev --head HEAD воспроизведено идентично отчёту r1: 1 файл src, 5 символов (AlignReport, PLAN_MODEL_VERSION, SpaceReferenceReport, canonicalizeConfigGeometry, canonicalizeLayoutGeometry), НЕОПРЕДЕЛЁННОСТЬ
node demo/smoke_optimize_coordinate_canonicalization.mjs green, все 16 полей true (включая serverEventReloadIsExactNoOp, coldReloadIsExactNoOp) — перепрогнан лично на текущем HEAD, не только унаследован
node scripts/mutation-gate.mjs --check green, все мутанты валидны статически, включая три из #248 (optimize-storage-boundary-removed, optimize-config-storage-half-raw, optimize-layout-storage-half-raw)
node scripts/process-gate.mjs green, диапазон origin/dev..HEAD, 6 коммитов, 0 предупреждений
node scripts/process-gate.mjs --issues green (issue #248 в S7-code-review)
python3 -c "import pytest" / import voluptuous оба ModuleNotFoundError — окружение этой ревью-сессии не содержит backend-харнесс, как и в r1
gh run view 32602363169 --json status,conclusion,headSha,jobs исполнено, не унаследовано на слово: conclusion: success, headSha: 120b491…, job backend — success (pip install pytest … pytest-homeassistant-custom-component … + python -m pytest tests_backend/ -q — success), job docs — success, process-gate — success
git diff 120b491..HEAD --stat подтверждает, что HEAD (e468414) = проверенное CI-дерево 120b491 + только docs/reviews/CODE-REVIEW-248-r1.md — CI-результат применим к текущему HEAD без экстраполяции
gh run view 32602050492 --json conclusion,workflowName,headBranch success, Docs screenshots, ветка issue/248-optimize-idempotence — легитимная пересъёмка, не самодельный PNG

Дешёвые гейты прогнаны заново, как требует §2.10 (код изменился, они стоят минуты). Тяжёлые — по дельте: golden/полный smoke-набор/performance не прогонялись (обоснование в «Чего не проверял»), backend pytest не исполнялся локально, но подтверждён предъявленным CI-прогоном на точном дереве текущего HEAD, а не восстановлен из слов автора.

Находки

Нет находок High или Medium в этом раунде.

Low (на усмотрение, не блокирует): заголовок задачи, полученный этой сессией, называл неверный заход/бюджет (r1 · 0/4 вместо фактических r2 · 1/4). Указано выше в отдельном разделе, не дублируется здесь как отдельная находка кода — это дефект оркестрации запуска ревью, а не диффа #248, и не входит в скоуп автора. Если он повторяется на других issue, стоит завести process-issue отдельно от #248.

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

Находка r1 Чем закрыта Где это видно
H1 (High) — check-docs.mjs красный на cbdd5a7: docs/images/screenshots.json хранит устаревший sourceFingerprint, src/plan-optimizer.ts из диффа делает его неактуальным Пересъёмка через каноническую джобу Docs screenshots (workflow_dispatch) на Chromium 151.0.7922.34, принята npm run docs:accept -- --reviewed; sourceFingerprint/sourceSha256 в docs/images/screenshots.json обновлены на 3f3237694ccc9aabd77df6204c598797dc528c2f7a23520915083486666be740 во всех 10 сценариях Коммит 5ad4280; лично перепрогнан node scripts/check-docs.mjs на HEAD e468414 → green; gh run view 32602050492 → success на ветке задачи; из 10 PNG изменился только 05-plan-context-tray.png (345560→345559 байт, один imageSha256 из десяти), остальные девять байт-в-байт прежние — подтверждено git diff cbdd5a7..HEAD -- docs/images/screenshots.json | grep imageSha256
L1 (Low) — хендофф говорил «1115 pass, 1 skip», факт — «1116 pass, 0 skip» Объяснено, не правкой: разные среды (Windows platform-skip у автора, Linux 0 skip у ревьюера r1), оба числа корректны для своей среды Комментарий владельца «Закрытие CODE-REVIEW-248-r1»; лично перепрогнан npm test на HEAD e468414 → 1116 pass, 0 skip, совпадает с числом, которое r1 сессия получила на Linux — outcome: no_change_needed

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

Принято без повторной проверки в этом раунде, поскольку дельта cbdd5a7..HEAD не касается ни одного из перечисленных файлов (подтверждено git diff cbdd5a7..HEAD --stat, см. «Скоуп ревью»). Источник — docs/reviews/CODE-REVIEW-248-r1.md, коммит e468414 (уже в дереве), проверено на SHA cbdd5a7:

  • AC1 — test/plan-optimizer.test.mjs, тест «issue 248 Optimize stays a no-op across the nine-decimal storage round-trip»; мутационный guard optimize-storage-boundary-removed подтверждён исполнением в r1 (тест краснеет без правки границы в src/plan-optimizer.ts). Не тронуто дельтой.
  • AC2 — параметризованный тест по cell_cm 1/3/5/1000 в test/plan-optimizer.test.mjs; существующий ULP-тест «six-room» подтверждает, что реальный шум по-прежнему даёт changed:true, счётчики не занулены безусловно. Не тронуто дельтой.
  • AC3 — custom_components/houseplan/store.py:150-229, websocket_api.py:1554-1666: обе половины записи (optimize_pending и live store) канонизируются независимо; backend production-код не менялся (§13.5 ТЗ). В r1 это было «проверено чтением, не исполнением» из-за отсутствия pytest в сессии; в этом раунде дополнительно подтверждено исполнением через gh run view 32602363169 — job backend success на 120b491, то есть на дереве, идентичном текущему HEAD за вычетом самого review-документа.
  • AC4 (частично) — общая fixture test/fixtures/optimize-storage-roundtrip.json и её потребители в Node/Python. Наследуется факт «одна fixture, оба runtime» из r1; не наследуется схемная валидность фикстуры на момент cbdd5a7 — это ровно то, что дельта раунда исправляет (см. «Как проверялось» и разбор view_box ниже), поэтому по AC4 сделана дополнительная самостоятельная проверка, а не чистое наследование.
  • AC5 — demo/smoke_optimize_coordinate_canonicalization.mjs, расширенный под serverEventReloadIsExactNoOp/coldReloadIsExactNoOp. Файл не тронут дельтой; тем не менее смок лично перепрогнан на текущем HEAD (см. таблицу гейтов) — совпадает с r1: все 16 полей true.
  • AC6 (документация) — docs/CANVAS.md §9.5, docs/CONFIG-COMPATIBILITY.md, docs/USER-GUIDE.ru.md, docs/TESTING.md, оба changelog: содержание не тронуто дельтой, разобрано и принято в r1.
  • Трейлеры cbdd5a7 (Issue: #248, User-Visible: yes, оба changelog в том же коммите) — унаследовано, не перепроверялось повторно (коммит не входит в дельту раунда).
  • Смок-выборка полного диффа origin/dev...HEAD (5 символов, НЕОПРЕДЕЛЁННОСТЬ, три целевых смока с прямой/тематической связью запущены и зелёные в r1: optimize_coordinate_canonicalization, optimize_geometry_preflight, optimize_micro_interval) — воспроизведено идентично инструментом в этом раунде (см. таблицу), сами смоки _preflight и _micro_interval не перезапускались повторно, т.к. их файлы не в дельте.
  • golden:verify и полный набор 167/169 браузерных смоков — не прогонялись ни в r1, ни здесь; обоснование (diff не меняет геометрию/рендер, golden — предрелизный гейт по AC6 и §12 ТЗ) наследуется без изменений, т.к. дельта этого раунда тем более не касается рендера (только docs-манифест, фикстура, один assert).
  • Performance — не названо в AC, чувствительный путь (O(n) проход) не тронут дельтой; наследуется из r1.

Проверено и корректно (сверх наследования)

  • Закрытие H1 — не принято на слово: check-docs.mjs лично перепрогнан зелёным на HEAD e468414, git status после npm run build чист, а сравнение imageSha256 по всем 10 сценариям показывает ровно один изменённый файл (plan-context-tray, 1 байт), как и утверждал автор — никакой другой скриншот не «съехал» тайком.
  • Самостоятельная находка автора (view_box) — до фикса test/fixtures/optimize-storage-roundtrip.json не содержал обязательное поле vol.Required("view_box") (custom_components/houseplan/validation.py:984); без него CONFIG_SCHEMA(source["config"]) в test_optimize_roundtrip_fixture_has_one_backend_canonical_target не могла бы пройти. Значение [0, 0, 1, 1] проверено против _view_box() (validation.py:566-570): x=0, y=0 в допуске _GEOM, w=1, h=1 в допуске _EXTENT (0.001..CANVAS_LIMIT) — валидно и не вырождено (не повторяет баг HP-1502-01 про [0,0,0,0]). Значение идентично в input и expected для обеих geometry (fine, regular) — канонизация не должна его менять, что и подтверждает зелёный backend-прогон в CI.
  • AC3/AC4 backend-доказательство теперь подтверждено исполнением, а не только чтением — gh run view 32602363169 называет точный SHA (120b491) и job (backend) с командой python -m pytest tests_backend/ -q, success; git diff 120b491..HEAD --stat показывает, что единственная разница между проверенным CI-деревом и текущим HEAD — файл docs/reviews/CODE-REVIEW-248-r1.md (класс C, не участвует в тестах). Это устраняет ограничение «проверено чтением, не исполнением», отмеченное в r1 для AC3.
  • Docs screenshots run легитимен — gh run view 32602050492 подтверждает workflow Docs screenshots на ветке задачи с успешным результатом, то есть принятие через docs:accept --reviewed соответствует правилу §8 PROCESS.md (не самодельный локальный кадр).
  • Трейлеры дельты — 5ad4280, 120b491, e468414 все несут Issue: #248; User-Visible: no корректен для всех трёх (тесты/fixture/ docs-манифест и сам review-документ не меняют пользовательское поведение сверх того, что уже задокументировано в cbdd5a7).
  • process-gate зелёный на полном диапазоне origin/dev..HEAD (6 коммитов, 0 предупреждений) и с --issues (статус issue #248 соответствует S7-code-review).

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

  • Полную матрицу 167/169 браузерных смоков — не унаследовано слепо: лично подтверждено, что smoke-select даёт тот же результат («НЕОПРЕДЕЛЁННОСТЬ», 5 символов) на полном диффе origin/dev...HEAD, и что дельта этого раунда (cbdd5a7..HEAD) вообще не выбирает смоков (src/**/*.ts не тронут). Целевой AC5-смок перепрогнан лично; _preflight и _micro_interval — нет (не в дельте, зелёные в r1).
  • npm run golden:verify — diff (полный и дельта) не меняет геометрию, стили или слои рендера; предрелизный гейт по AC6/§12 ТЗ.
  • python -m pytest tests_backend -q — не исполнялся локально (pytest/voluptuous не установлены в этой сессии), но подтверждён через gh run view на CI-прогоне точного дерева HEAD минус review-документ — это сильнее «проверено чтением», хотя и не то же самое, что личное исполнение.
  • Оба backend-мутанта (optimize-config-storage-half-raw, optimize-layout-storage-half-raw) — mutation-gate.mjs --check подтвердил только статическую валидность патча (якорь найден), не факт, что тест краснеет с мутацией; их guard (backend-test-guard.mjs) тоже требует pytest, недоступного в сессии. Backend CI job (python -m pytest tests_backend/ -q) не запускает mutation-gate.mjs, поэтому исполнение этих двух мутантов не подтверждено ни в r1, ни здесь — фактическое исполнение остаётся долгом до следующего Linux-сеанса с установленным pytest, но не блокирует зелёный вердикт: сам факт что тест существует и статически бьёт по нужной строке проверен, а полное отсутствие исполнения этих двух конкретных мутантов было тем же ограничением уже в r1 и не регрессировало.
  • Полный Linux HA harness (полная HA-обвязка, не только tests_backend/ pure-подмножество) — вне скоупа код-ревью, канонический прогон в CI/WSL.
  • Performance-профили — не названы в AC, чувствительные пути не тронуты дельтой.