19 KiB
CODE-REVIEW #295 — r2
Issue: https://github.com/Matysh/houseplan-card/issues/295
Ветка: issue/295-preflight-diagnostics, ревьюируемый HEAD: 7273a3ad9a1fcad8fd1321d90b6f06986492872f.
Ревьюируемый r1 (для дельты): 822e6223f74ae0df993e1f9e9e702faa67fd12ba.
Спецификация: docs/specs/295-preflight-diagnostics.md, ревизия 2 (принята SPEC-REVIEW-295-r2, зелёный) — не менялась.
Заход: r2 — второй цикл этапа code. Дельта локальна: два коммита
(b542f44b — фикс обеих Medium из r1, 7273a3ad — репин baseline-фингерпринта
после фикса), без ребейза, без смены подсистемы, без изменения контракта
поведения. Разбор сужен по PROCESS.md §2.9 до дельты плюс всё, до чего она
дотягивается.
Скоуп дельты
git diff 822e6223..HEAD --stat:
custom_components/houseplan/frontend/houseplan-card.js | 12 ++++++------
demo/golden/baselines/baselines-index.json | 2 +-
demo/smoke_preflight_diagnostics.mjs | 33 ++++++++++++++--
dist/houseplan-card.js | 12 ++++++------
docs/images/screenshots.json | 22 ++++++-------
docs/reviews/CODE-REVIEW-295-r1.md | 297 ++++++++++++
scripts/mutation-gate.mjs | 31 ++++++++++--
src/houseplan-card.ts | 38 ++++++++++++----
Единственная содержательная правка — src/houseplan-card.ts (два метода
диагностики + пять точек закрытия/открытия диалога Optimize). Остальное —
производные: пересборка трёх копий бандла, обновление sourceFingerprint в
baselines-index.json/screenshots.json (тот же паттерн, что описан в r1 —
хэш дерева src/** меняется при любой правке фронтенда, картинки/сцены
байт-идентичны), расширение смока и двух новых мутантов под фикс, публикация
документа r1. Ни plan-geometry-preflight.ts, ни custom_components/**/*.py,
ни layout/marker.space/open_spans/рёбра комнат/записи толщины дельта не
трогает — геометрическая модель не задета.
Закрытие r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
M1 — _preflightDiagnostics считал spaceGeometryFingerprint от this._serverCfg (сохранённая конфигурация), а preflight всегда проверял кандидата (r.config/d.config) — значение совпадало с тем, что дал бы хэш экспорта, и не несло ничего сверх него (AC4) |
Метод принимает candidate: ServerConfig | null явным параметром; оба вызывающих места (_reportPreflightFailure из отчётного пути и повторной проверки) и кнопка копирования передают именно кандидата, не _serverCfg |
src/houseplan-card.ts:15845-15850 (_preflightDiagnostics(preflight, candidate), spacesById строится из candidate.spaces); вызовы :16007 (_reportPreflightFailure(preflight, r.config)), :16044 (..., d.config)), :15901 (_preflightDiagnostics(preflight, this._alignDialog?.config ?? null)). Смок demo/smoke_preflight_diagnostics.mjs:88-98 — fingerprintTracksCandidate: сохраняет хэш от кандидата, затем подменяет _alignDialog.config на _serverCfg и требует, чтобы хэш изменился. Мутант preflight-fingerprint-from-saved-config (scripts/mutation-gate.mjs:2261-2272) — откат правки красит смок, поймано 1/1 исполнением |
M2 — _preflightClipboardFallback не сбрасывался при открытии/закрытии диалога Optimize; переживший закрытие блок мог показать JSON чужого (предыдущего) отказа в отчёте об ошибке |
Поле обнуляется во всех пяти путях закрытия диалога (escape, сброс режима, успешный apply, hp-close, кнопка «Отмена») и при открытии нового диалога (_previewAlignDialog) |
src/houseplan-card.ts:2552 (escape), :6484 (сброс режима), :16019 (открытие нового диалога), :16077 (успешный apply), :17182 (hp-close), :17345 (кнопка «Отмена»). Смок demo/smoke_preflight_diagnostics.mjs:110-119 — fallbackClearedOnClose: диспатчит настоящее событие hp-close и требует _alignDialog === null && _preflightClipboardFallback === null. Мутант preflight-fallback-survives-dialog-close (scripts/mutation-gate.mjs:2273-2284) — откат сброса в hp-close-обработчике красит смок, поймано 1/1 исполнением |
Обе находки закрыты по коду, не только по заявлению автора — проверено
чтением всех точек чтения/записи _preflightClipboardFallback (grep по
всему файлу, 10 вхождений, единственное место чтения — рендер фолбэк-блока
:17202/17204, гейтится тем же _alignDialog, что и остальной диалог) и
исполнением обоих новых мутантов и расширенного смока.
Унаследовано из r1
Документ: docs/reviews/CODE-REVIEW-295-r1.md (SHA ревью 822e6223).
Принято без повторной проверки — дельта их не задевает:
- AC1 (7 причин в i18n RU/EN, маппинг полный) —
i18n, диалоговый рендер причин не менялись. - AC2 (структура dev-лога, дедуп по fingerprint) — единая точка
_reportPreflightFailureта же, дедуп-поле_reportedPreflightFingerprintне тронуто; содержимое записи наследует фикс M1 (лог теперь тоже видит кандидата — см. таблицу закрытия выше, это и есть предмет дельты). - AC3 (кнопка копирует, фолбэк при отказе clipboard) — оба пути не менялись, кроме сброса фолбэка (M2, разобран отдельно).
- AC5 (сцена с ломаной геометрией →
failedс причиной, тест падает на dev) — новые символы дельты (candidate-параметр) не существуют на dev, логика провала теста на dev не меняется. - AC6 (условный «обновите», канал
integration_version) — backend и_preflightVersionsDiffer()не тронуты дельтой. - AC7 (три мутанта AC7 исходной реализации) — не менялись, кроме
синтаксической правки find/replace в
preflight-dev-log-disabledпод новую сигнатуру (void preflight; void candidate;) — перепрогнан и подтверждён (см. «Как проверялось»). - Граница приватности #199 (
detail= класс исключения, неmessage) — не тронута; триassert.doesNotMatchне менялись. - Трейлеры формата коммитов,
USER-GUIDE.ru.md, golden-провенанс исходной реализации — не тронуты дельтой, повторно не проверялись. - Скоуп не расширен и не сужен относительно ревизии 2 спецификации.
Как проверялось (дельта, заново)
Дешёвые гейты — прогнаны заново на HEAD 7273a3ad, так как код менялся:
| Команда | Результат |
|---|---|
npx tsc --noEmit |
чисто, без вывода |
npm test |
1338 tests, 1337 pass, 1 skip, 0 fail — то же состояние, что в r1 |
npm run build && npm run bundle:sync |
сборка ок, git status --porcelain после пересборки пуст — три копии бандла (dist/, custom_components/.../frontend/, demo/srv/assets/) побайтово совпадают с закоммиченными |
node scripts/check-docs.mjs |
Documentation checks passed (7 files, 10 external links) — обязателен, дельта трогает src/** |
node --test test/single-source-numbers.test.mjs |
3/3 pass — диагностический блок не добавляет новых дублирующих величин; смена источника хэша (M1) не создаёт второй видимый источник, само число как было производным d.preflight.failures, так и осталось |
Смок-выборка. node scripts/smoke-select.mjs --base 822e6223 --head HEAD:
- Прямое совпадение (1):
demo/smoke_preflight_diagnostics.mjs— прогнан, OK (все 18 проверок, включая новыеfingerprintTracksCandidateиfallbackClearedOnClose). - Слабая связь (9, все — общее имя
_alignDialog):smoke_grid_snap.mjs,smoke_near_axis_optimize.mjs,smoke_optimize_coincident_partition.mjs,smoke_optimize_coordinate_canonicalization.mjs,smoke_optimize_geometry_preflight.mjs,smoke_optimize_micro_interval.mjs,smoke_orphan_space_references.mjs,smoke_resize_outer_reconciliation.mjs,smoke_warm_dialogs.mjs. Не прогонял восемь из девяти — обоснование не «слабая связь = пропуск», а прочтение:grep -n "_preflightClipboardFallback" src/houseplan-card.tsдаёт 10 вхождений, единственное чтение поля — рендер фолбэк-блока внутри отказной ветки диалога Optimize (:17202/17204), сама запись в остальных пяти точках дельты — чистое добавление= nullрядом с уже существующимthis._alignDialog = null, не меняющее ни форму, ни поведение_alignDialogдля успешных/неотказных сценариев, которые эти девять смоков и проверяют. Один из девяти (smoke_optimize_geometry_preflight.mjs, уже изменён в r1 под новый контракт диалога) перепрогнан для полноты — OK, 16/16 проверок.
Мутанты — точечно, node scripts/mutation-gate.mjs --id=<id>:
preflight-fingerprint-from-saved-config(новый, M1) — чистый прогон зелёный, мутант красный, поймано 1/1.preflight-fallback-survives-dialog-close(новый, M2) — чистый прогон зелёный, мутант красный, поймано 1/1.preflight-dev-log-disabled(существующий, find/replace обновлён под новую сигнатуру_reportPreflightFailure(preflight, candidate)) — перепрогнан, чистый зелёный, мутант красный, поймано 1/1 — правка сигнатуры не сломала гвард.
npm run golden:verify — 129/129 passed (полный вывод содержит все
сцены; включая обе, изменённые в исходной реализации:
optimize-preflight-dialog-dark-en, optimize-preflight-dialog-light-ru —
passed). Обязателен: docs/images/screenshots.json и
baselines-index.json меняют sourceFingerprint/sourceSha256 в дельте.
Коммит 7273a3ad несёт Baseline-Reviewed на тот же CI-прогон
(32940625718), что уже был отсмотрен в r1 — заявление автора «пиксели не
менялись» проверено: диф baselines-index.json — ровно одна строка
(sourceFingerprint), per-scenario хэши не тронуты; диф screenshots.json —
меняются только sourceFingerprint/sourceSha256 (10 вхождений), ни один
imageSha256 не изменился. Дельта (данные диагностики + состояние диалога)
не может задеть рендер — подтверждено и логически (никакой правки в
render()/шаблонах кроме перестановки уже существующего = null рядом с
новым), и байт-сверкой.
Инварианты модели (npm run invariants) — не гонял, как и в r1: дельта
не трогает plan-geometry-preflight.ts, рёбра комнат, записи толщины,
layout/marker.space/open_spans — подтверждено git diff --stat
(единственный тронутый исходник — src/houseplan-card.ts, только методы
диагностики и обработчики закрытия диалога).
Backend (python -m pytest tests_backend) — не гонял, дельта не трогает
ни один .py-файл (подтверждено списком файлов диффа выше); канал
integration_version из r1 не менялся.
Находки
Нет. Обе Medium из r1 закрыты по коду и подтверждены исполнением (смок + мутант на каждую), новых регрессий дельта не вносит — весь тронутый код (два метода диагностики, пять точек закрытия/открытия диалога) прочитан целиком, а не только строки диффа.
Что проверено и корректно
- Обе находки r1 (M1, M2) закрыты именно так, как было предложено в
исправлении в скоупе (
this._alignDialog?.config/r.config/d.configвместо_serverCfg; сброс на всех точках закрытия) — без архитектурных сюрпризов. - Трейлеры дельты:
Issue: #295на обоих коммитах;User-Visible: no— верно, т.к.docs/CHANGELOG.md/docs/CHANGELOG.ru.mdуже описывают функциональность на уровне «называет причину, копируемый блок, фолбэк» (запись из исходной реализации,docs/CHANGELOG.md:8-13) без обещания конкретного источника хэша — фикс не меняет видимое поведение, только корректность уже заявленных данных; ни один CHANGELOG-файл в дельте не тронут, что согласуется.Release/Baseline-Reviewedна7273a3ad— оба присутствуют, ссылаются на реальный, уже отсмотренный прогон. - «Одно число — один источник»: единственная затронутая величина —
spaceGeometryFingerprintв диагностическом блоке/dev-логе. У неё один источник и до, и после фикса — разница в том, какую конфигурацию она хэширует (кандидата вместо сохранённой), не в дублировании. Второго места, где эта величина отображалась бы независимо, нет — подтвержденоgrepпоspaceGeometryFingerprint(единственное вычисление в_preflightDiagnostics). - Скоуп дельты не расширен относительно указаний r1 — оба фикса ограничены файлом/методами, названными в находках, без сопутствующего рефакторинга.
Чего не проверял
- Восемь из девяти «слабых связей» по имени
_alignDialog(smoke_grid_snap.mjsи далее по списку выше) — не прогонял; обоснование выше (прочтение всех точек записи/чтения_preflightClipboardFallbackпоказывает чистое добавление, не влияющее на успешные/неотказные пути, которые эти смоки покрывают), не просто «слабая связь → пропуск». - Полный набор
demo/smoke_*.mjs(193 файла) и полныйnpm run mutation-gateбез--id— вне диапазона дельты, дорого; предрелизная обязанность, не обязанность этого раунда. npm run invariants— дельта не меняет геометрическую модель (см. выше).python -m pytest tests_backend— дельта не трогает.py-файлы.- Performance-профили — не названы в AC, дельта не добавляет операций на
ok-пути (правки только в отказной ветке и в обработчиках закрытия
диалога, которые и раньше выполняли присваивание
_alignDialog = null). - Ручное тестирование в браузере HA — вне цикла ревью по процессу.
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0