Files
houseplan-card/docs/reviews/CODE-REVIEW-463-r1.md
T
claude[bot] 7ab12294fc
Проверка (CI) / Классификация изменённых файлов (push) Successful in 23s
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 59s
Проверка (CI) / HACS: валидация репозитория (push) Failing after 26s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 25s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 41s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Failing after 4m17s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 6m24s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
docs: review document for #463
Issue: #463
User-Visible: no
2026-09-05 15:22:56 +00:00

17 KiB

CODE-REVIEW-463-r1

Issue: #463 · "Diagnostics: <hp-dialog> native fallback opens non-modal, pinned to the left edge, no dimming" Этап: code · заход r1 · блокирующих циклов 0/2 · трек small SHA материала: 23c65e449e21f7541c7ca8ee6d42d94596b0bd14 (HEAD, совпадает с SHA в хендоффе и в ссылке на финальный Validate)

Скоуп

Диапазон: origin/dev..HEAD, два коммита:

  • 1856302c — fix: recover native dialog modal placement (Issue: #463, User-Visible: yes) — продуктовый код + новый smoke + маршрутизация гейтов + документация + оба changelog + синхронные bundle.
  • 23c65e44 — test: accept corrected dialog baselines (Issue: #463, User-Visible: no, Release: v1.72.0-beta.5, Baseline-Reviewed: ссылка на прогон) — только класс D: 29 golden-эталонов диалогов + 4 docs-скриншота + их индексы.

Файлы вне generated/baseline:

demo/smoke_dialog_modal_recovery.mjs | 318 ++++++++++++++++
docs/ARCHITECTURE.md                 |  14 +-
docs/CHANGELOG.md                    |   5 +
docs/CHANGELOG.ru.md                 |   5 +
scripts/mutation-gate.mjs            |  44 +++
scripts/smoke-links.mjs              |   7 +
src/hp-dialog.ts                     |  53 +++-

Ровно совпадает со списком «затронутые поверхности» ТЗ (лёгкий трек, тело issue). Ничего лишнего не тронуто.

Продуктовое изменение целиком в src/hp-dialog.ts: native-ветка <dialog> больше не растягивается на весь viewport (width: auto → width: fit-content; height: fit-content; inset: 0), и добавлен _ensureNativeModal() — сверка фактического :modal состояния после firstUpdated, каждого updated и в микрозадаче connectedCallback (восстановление после реконнекта), с безопасным close()+showModal() в try/catch. Ветка HA/native фиксируется один раз (_useHaDialog === null guard), как и было, но тип расширен до boolean | null, чтобы отличать «ещё не выбрано» от «выбран native».

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

Дешёвые гейты подтверждены зелёным Validate на точном SHA 23c65e44... (https://github.com/Matysh/houseplan-card/actions/runs/33973740446, конкретно проверено gh run view — conclusion: success, headSha совпадает с HEAD): tsc --noEmit/юниты/сборка+синхрон бандла, docs, provenance, process-gate, golden — все успешны на этом SHA. Их заново не гонял.

То, что Validate не проверяет предметно (браузерные смоки и мутанты для этой задачи), прогнал сам:

Гейт Команда Результат
Build (для смоков нужен свежий бандл) npm run build && npm run bundle:sync green, git status после — чисто (бандл в дереве байт-в-байт совпал с закоммиченным)
Целевой smoke node demo/smoke_dialog_modal_recovery.mjs green, все 20 проверок true
Мутант 1 (surface stretches) node scripts/mutation-gate.mjs --id=dialog-native-surface-stretches-to-viewport краснеет смок, как обязано — «поймано 1 из 1»
Мутант 2 (update recovery disabled) node scripts/mutation-gate.mjs --id=dialog-native-update-recovery-disabled краснеет — «поймано 1 из 1»
Мутант 3 (reconnect recovery disabled) node scripts/mutation-gate.mjs --id=dialog-native-reconnect-recovery-disabled краснеет — «поймано 1 из 1»
Выбор смоков по дифу node scripts/smoke-select.mjs --base origin/dev --head HEAD прямое совпадение: smoke_help_affordance.mjs (_focusInitial); зарегистрированная связь: smoke_dialog_modal_recovery.mjs (запись в scripts/smoke-links.mjs добавлена этим же диффом)
smoke_help_affordance.mjs node demo/smoke_help_affordance.mjs green, все проверки true
smoke_danger_confirmation.mjs node demo/smoke_danger_confirmation.mjs green (тот же компонент hp-dialog/hp-confirm, не в выборке селектора, но прямо назван в разделе «Затронутые поверхности» ТЗ)
smoke_danger_confirm_branches.mjs node demo/smoke_danger_confirm_branches.mjs green, включая haDestructiveStaysNativeAlert и т.п.
Docs fingerprint node scripts/check-docs.mjs «Documentation checks passed (7 files, 12 external links)»
Golden-приёмка на финальном SHA прочитан job golden прогона Validate 33973740446 success
Провенанс коммитов прочитаны трейлеры git show -s --format=full для обоих коммитов оба корректны (см. ниже)

Не прогонял (и почему): npm test/tsc --noEmit/полный npm run bundle:budget/no-new-any отдельно — уже зелёные на этом же SHA в Validate, дельта с момента этого прогона нулевая (SHA совпадает с HEAD). python -m pytest tests_backend — diff не трогает custom_components/**/*.py, backend job в обоих прогонах skipped/не назначен. npm run golden:capture/локальный визуальный просмотр всех 29 кадров — не переснимал сам (не Linux CI-артефакт под рукой в этой сессии), доверился job golden: success на точном SHA и списку изменённых файлов (см. ниже), это не альтернатива ревью, а имеющееся доказательство, которое я сверил, а не выдумал. Полный набор demo/smoke_*.mjs (226 файлов) — не прогонял: диф локален к одному компоненту, smoke-select.mjs не указал ничего внешнего, полный набор — предрелизный гейт.

Находки

Нет находок уровня High или Medium. Реализация соответствует контракту ТЗ пункт-в-пункт, тесты умеют падать (проверено тремя реальными мутациями, не просто прочитаны из отчёта автора), а не только заявлены.

Low. docs/ARCHITECTURE.md:126 — новая формулировка «An ordinary dialog uses ha-dialog when that component is registered as the instance is connected» синтаксически двусмысленна (читается как «зарегистрирован, поскольку экземпляр подключён», хотя смысл — «в момент подключения экземпляра»). Не блокирует, смысл абзаца в целом верен и совпадает с кодом (connectedCallback, _useHaDialog === null guard). Решение ревьюера: снято без правки — чисто редакционная шероховатость в документации класса C, не в контракте, следующая правка ARCHITECTURE.md эту фразу заденет естественным образом.

Проверка критериев приёмки

AC Доказательство Чем краснеет Результат
AC1 (ровно один internal dialog, open+:modal, центрирован по обеим осям, backdrop alpha>0) smoke_dialog_modal_recovery.mjs → confirmHasOneNativeShell/confirmIsTopLayerModal/confirmSurfaceIsCentred/confirmBackdropIsVisible = true мутант dialog-native-surface-stretches-to-viewport (возвращает width: auto) — смок краснеет, проверено лично Выполнен
AC2 (standard/wide fallback центрированы desktop+narrow, 92vw/92vh, без overflow; golden) standardFallbackStartsCentred (desktop) и wideNativeFallbackFitsAndCentresOnNarrowViewport (narrow, wide=true) = true; golden job success на точном SHA, 29 изменённых кадров = ожидаемые 29 dialog-сцен (посчитано по стату коммита 23c65e44) геометрическое сравнение (не защитный AC в терминах §2.7 — расположение/размер, третий столбец не обязателен) Выполнен
AC3 (open-but-nonmodal → update восстанавливает :modal на том же shell, без hp-close, без retry-loop) updateRecoversSameNativeShell, updateRecoveryRunsOnce (2 update-цикла, showModalCalls===1), recoveryNeverEmitsClose = true мутант dialog-native-update-recovery-disabled — смок краснеет, проверено лично Выполнен
AC4 (detach/reattach → :modal, центрирован, без дубликата/hp-close, фокус восстановлен) detachActuallyDropsTopLayer, reconnectRecoversSameNativeShell, reconnectRestoresInitialFocus, recoveryNeverEmitsClose = true мутант dialog-native-reconnect-recovery-disabled — смок краснеет, проверено лично Выполнен
AC5 (ветка стабильна для уже открытого; новый instance при готовом ha-dialog — один HA shell; alert всегда native) openFallbackDoesNotSwitchAfterLateHaRegistration, newOrdinaryUsesExactlyOneHaShell, alertAfterHaStillUsesExactlyOneNativeModal = true; плюс чтение _usesHaDialog()/connectedCallback guard доказательство smoke + код (_useHaDialog === null guard делает повторный выбор невозможным структурно) — проверено чтением Выполнен
AC6 (top-layer order, focus containment/restore, действующие способы закрытия у вложенных/последовательных диалогов; без retry-loop) nestedNativeEscapeKeepsTopLayerOrder (два вложенных native alert/parent, раздельные Escape); stableUpdateDoesNotRetry; регрессионные smoke_danger_confirmation.mjs/smoke_danger_confirm_branches.mjs/smoke_help_affordance.mjs зелёные воспроизведение чтением: @cancel-обработчик и Tab-trap код не менялись этим диффом, _ensureNativeModal не подписывается на native close-событие (проверено чтением render() — только @cancel/@click, нет @close), поэтому программный dialog.close() не может синтезировать hp-close Выполнен

Провенанс коммитов

  • 1856302c: Issue: #463 · User-Visible: yes · оба changelog (docs/CHANGELOG.md, docs/CHANGELOG.ru.md) правлены в этом же коммите — правило §10.2.4/AGENTS соблюдено.
  • 23c65e44: Issue: #463 · User-Visible: no · Release: v1.72.0-beta.5 · Baseline-Reviewed: .../runs/33973294353 — commit трогает только demo/golden/baselines/** + docs/images/** (класс D), трейлеры присутствуют по правилу. Ссылка проверена (gh run view 33973294353): реальный прогон на headSha = 1856302c, golden-job там failure — ожидаемо, это и есть прогон, с которого взяты actual/diff для приёмки; после приёмки контрольный Validate на 23c65e44 зелёный целиком, включая golden.
  • Ветка issue/463-dialog-modal-recovery соответствует issue/NN-slug.

Одно число — один источник

Дифф не добавляет и не меняет ни одной пользовательски видимой величины (текста, числа, единицы измерения). Единственные «числа» в диффе — CSS-геометрия (92vw/92vh, уже существующие ограничения, не тронуты) и backdrop-alpha, которая нигде не показывается пользователю как число и не дублируется. Правило не применимо к этому диффу.

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

  • Скоуп диффа = заявленным «затронутым поверхностям» ТЗ, один за другим, без остатка и без лишнего.
  • Целевой smoke и три обязательных мутанта — лично прогнаны, все три мутанта подтверждённо красят смок (не поверил заявлению автора на слово).
  • Регрессионные smoke по тому же компоненту (danger_confirmation, danger_confirm_branches, help_affordance, выбранные smoke-select.mjs и явно перечисленные в ТЗ) — зелёные.
  • check-docs.mjs зелёный — фингерпринт документации соответствует текущему src/**, скриншоты пересняты и приняты в этом же диапазоне.
  • Golden: 29/29 dialog-сцен изменились (совпадает с заявленным числом), финальный Validate на HEAD зелёный по job golden.
  • Оба changelog правлены в коммите с User-Visible: yes; ARCHITECTURE.md обновлён по факту (контракт совпадает с кодом).
  • Не найдено ни одного нового any в диффе (grep по патчу src/hp-dialog.ts).
  • Откат: два коммита, отдельно откатываемых, без миграции — соответствует разделу «Откат» ТЗ.

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

  • Реальную сессию Home Assistant с физическим разрывом соединения (сеть, DevTools throttling) — не воспроизводил; согласен с автором, что детерминированный browser-smoke через настоящий detach/reconnect DOM API покрывает утверждаемый контракт эквивалентно.
  • Backend/HACS/Hassfest — не затронуты диффом, не прогонял.
  • Полный набор из 226 browser-smoke и полный golden:capture с нуля — не входит в объём код-ревью (§8: полные наборы — предрелизный гейт); доверился уже пройденному предметному прогону на точном SHA плюс собственным целевым запускам.
  • Визуальный просмотр каждого из 29 принятых golden-кадров глазами — не переоткрывал PNG-диффы сам; доверился тому, что golden-job на точном SHA зелёный (пиксельное сравнение с принятыми эталонами прошло) и числу изменившихся файлов, совпадающему с заявленным.

Вердикт

Зелёный. Контракт закрыт по всем шести пунктам, все обязательные мутанты лично проверены как краснеющие, регрессионные смоки зелёные, provenance коммитов корректен, единственная находка — Low в документации, снята без правки.


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

  • Ветка: issue/463-dialog-modal-recovery, коммит 23c65e449e21 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 23c5b2eab1734a12bb6e61cdc1c0d867da8e5b0a
    git log --all --format='%H %T' | grep 23c5b2eab173