Files
houseplan-card/docs/reviews/CODE-REVIEW-649-r2.md
T
claude[bot] 0e8e87303e
Проверка (CI) / Классификация изменённых файлов (push) Successful in 22s
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 48s
Проверка (CI) / HACS: валидация репозитория (push) Failing after 23s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 24s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 46s
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Failing after 34s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Failing after 44s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 8m29s
Проверка (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
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 19s
docs: review document for #649
Issue: #649
User-Visible: no
2026-09-25 16:51:38 +00:00

25 KiB
Raw Blame History

CODE-REVIEW-649-r2

Issue: #649 · 2.5D, этап 6: объёмные иконки-плитки с тенью, мягкий свет по солнцу, слой декора без изменений, режим из experimental в General settings.

Материал: git log --oneline origin/dev..HEAD / git diff origin/dev...HEAD, ровно на SHA 106fdc096d5dd7de55d22a88c3a0a2b894b83b49 (рабочая копия уже на нём; ветку не переключал, git fetch/pull/checkout на другой коммит не выполнял).

106fdc09 fix(2.5D): prove the sun beam is cut by bodies; review r1 lows (#649)
e1f1c7d9 docs: review document for #649
2ed05031 test(mutants): stage3-w12 guard follows the renamed #649 contract test
e5ab4659 feat: 2.5D stage 6 — public setting, raised tiles, soft sun, theme-free walls (#649)

164 файла в фиче + 73 файла в делте раунда. Трейлеры на всех трёх рабочих коммитах корректны: Issue: #649 везде; User-Visible: yes на e5ab4659 (оба changelog в том же коммите — docs/CHANGELOG.md/.ru.md, +12/+12 строк); User-Visible: no на 2ed05031 (гейт мутанта) и на 106fdc09 (делта этого раунда — см. ниже, почему верно). Заход r2 код-ревью; ТЗ (r2 в теле issue) уже прошло зелёное SPEC-REVIEW-649-r2.

SHA материала r1 не резолвится. de4521823ba69c390fda4c0534e52e24a63cc66a (коммит) и дерево 571541905136a48c1729755f6d7877e5ac6aece4 из блока «Материал раунда» документа r1 отсутствуют в git log --all этого клона (не shallow — git rev-parse --is-shallow-repository → false, 3576 коммитов на origin/dev). Это не находка (§2.10: «SHA не резолвится — обычное дело»): у текущего e5ab4659 тот же commit message, тот же автор/дата инструмента, тот же родитель (origin/dev tip) и почти идентичная статистика диффа (164 файла, +5569/−3766 против заявленных в r1 +5570/−3767 — расхождение в одну строку в обе стороны, не содержательное). Это типичная механика конвейера: ветка ребейзится на актуальный origin/dev перед каждым раундом, что меняет все SHA ниже точки ребейза без изменения содержимого. Дельта раунда — ровно то, что показывает git diff e5ab4659..106fdc09 (коммит 2ed05031 между ними не трогает продуктовый код, только чинит протухший guard мутанта — унаследован как есть). Объём разбора: по дельте (§2.10), она полностью локальна — один новый коммит 106fdc09, который сам называет себя ответом на r1 («review r1 lows»).

Скоуп

Без изменений с r1 (см. docs/reviews/CODE-REVIEW-649-r1.md → «Скоуп»). Предмет ЭТОГО раунда — единственный новый коммит 106fdc09: закрывает Medium-находку r1 (M1, обрезка 2.5D-луча барьерами не была доказана тестом) и пять из шести Low (L1, L3, L4, L5, L6; L2 была снята самим r1 без правки кода как опечатка хендоффа — здесь закрывать нечего).

Как проверялось (объём по дельте, §2.10)

  1. Прочитан документ и вердикт предыдущего раунда: docs/reviews/CODE-REVIEW-649-r1.md — жёлтый, High 0, Medium 1 (M1), Low 6 (L2 снята r1 сразу).
  2. Объявлена дельта: git diff e5ab4659..106fdc09 — 73 файла (в основном пересобранный бандл), содержательно: src/iso-sun.ts не тронут (только test/, demo/, scripts/mutation-registry.mjs, src/iso-tiles.ts, src/houseplan-card.ts комментарий, docs/DEVELOPMENT.md, docs/ARCHITECTURE.md, demo/benchmark_large_house.mjs). Полный текст диффа коммита 106fdc09 прочитан построчно.
  3. Для M1 — прочитан src/iso-sun.ts:82-122 (computeIsoSunBeams), подтверждено, что directionalOccluders(input.occluders, away, travel) (строка 112) — ровно то место, которое M1 называл риском (перепутанный знак направления/длина). Новый юнит test/iso-stage6.test.mjs (тест «AC7 the 2.5D beam is cut…») ставит колонну на пути луча и проверяет три точки (перед колонной светло, за колонной темно, рядом с тенью снова светло) плюс площадь тени.
  4. Мутационное доказательство выполнено исполнением, не чтением. Для всех трёх новых мутантов (iso-sun-occluders-toward-sun, iso-sun-occluders-ignored, iso-sun-card-drops-occluders) я вручную применил патч из scripts/mutation-registry.mjs, прогнал именно тот guard, что записан в реестре, убедился в красном результате, затем откатил файл и для двух из них (те, что требуют бандл) пересобрал (npm run bundle:sync) и подтвердил git status чист — см. таблицу ниже, результаты все реальные прогоны этой сессии.
  5. Для L3 (src/iso-tiles.ts:109-117, isoTileStateCss) вручную пересчитана специфичность CSS: новые правила .dev.theme-dark.${state} (3 класса) и .dev.theme-dark.${state}.iso-floor-light (4 класса) перебивают старые .dev.${state} (2) / .dev.${state}.iso-floor-light (3) по специфичности независимо от порядка — правки корректны структурно, не только текстуально. Также перепроверено (как и в r1), что для всех 6 ISO_STATE_BODIES isoEdgeColor сегодня возвращает одно и то же значение независимо от theme, потому что ни один не тёмный (luma·255<70) и ни один не «near-white» в 0..1-шкале (luma>0.98) — значит фикс сегодня не меняет видимый результат ни для одного реального состояния, что и объясняет User-Visible: no.
  6. Для L1 прочитаны все 4 места, названные в r1 (docs/DEVELOPMENT.md:231, docs/ARCHITECTURE.md:31, src/houseplan-card.ts:627, demo/benchmark_large_house.mjs:145) — все четыре переписаны, проверено, что переписанный текст не противоречит соседним абзацам того же файла (docs/DEVELOPMENT.md:249-251, docs/ARCHITECTURE.md:86-88, src/houseplan-card.ts:~2305), и что не осталось других вхождений «experimental/alpha-only/opt-in alpha» применительно именно к 2.5D/Stage 4 (grep -n alpha по трём файлам — только легитимные несвязанные употребления: общий hp_alpha/Labs-инфраструктура и цветовая альфа).
  7. Для L4/L5/L6 прочитаны новые ассерты demo/smoke_iso_tiles.mjs и сверены с CSS-правилами, на которые они опираются (специфичность .dev.theme-dark/.oplock/.virtual .device-shell-frame, iso-overlays?.devices.get(d.id) опциональная цепочка для L6) — все три были задокументированы в r1 как «проверено чтением, корректно, но без теста»; теперь тест есть и я лично прогнал его (все ассерты true).
  8. Прогнаны реально (не приняты по Validate) — таблица ниже.

Гейты — что прогнано и с каким результатом

Гейт Прогнан Результат
Validate на 106fdc09 нет, зачтён по ссылке (условие задачи) success, https://github.com/Matysh/houseplan-card/actions/runs/36161723663
npx tsc -p tsconfig.test.json + node scripts/fix-test-build.mjs да 0 ошибок
npm run bundle:sync (tsc --noEmit && rollup -c + bundle-tree sync) да, дважды (baseline и после отката мутанта) чисто оба раза, git status --porcelain пуст после отката — закоммиченные dist/**, custom_components/.../frontend, demo/srv/assets побайтово совпадают со свежей сборкой на этом SHA
node --test test/iso-stage6.test.mjs (весь файл, 10 тестов) да OK, 10/10 pass
node --test --test-name-pattern="AC7 the 2.5D beam is cut" test/iso-stage6.test.mjs на немутированном коде да OK
Мутант iso-sun-occluders-toward-sun (away→toSun) применён к src/iso-sun.ts, тот же guard да красный — AssertionError: between the window and the body the floor stays lit, ожидалось true, получено false
Мутант iso-sun-occluders-ignored (доп. условие elevation > 90) применён, тот же guard да красный — AssertionError: behind the body, away from the sun, the floor is in shadow
Мутант iso-sun-card-drops-occluders (occluders: [] в houseplan-card.ts) применён, пересобран, node demo/smoke_iso_sun.mjs да красный — bodyCutsTheBeam: expected true, got false; после отката и пересборки git status снова чист
node demo/smoke_iso_sun.mjs на немутированном коде да OK, все 20 проверок true, включая новые bodyCutsTheBeam, beamBackWithoutBody
node demo/smoke_iso_tiles.mjs на немутированном коде да OK, все 39 проверок true, включая новые alarmBeatsHover, alarmBeatsSelected, virtualNoDashedRing, noBordersKeepsTiles, noBordersKeepsShadows
node scripts/check-docs.mjs --external --screenshots=warn (режим обычного пуша, classify-changes.mjs/validate.yml) да OK, 7 файлов, 12 внешних ссылок; один WARN про устаревший фингерпринт скриншотов — унаследован, предрелизный гейт (#479), делта его не касается
node scripts/smoke-select.mjs --base 2ed05031 --head HEAD (скоуп ровно делты раунда) да НЕОПРЕДЕЛЁННОСТЬ — дифф исполняемый, но инструмент не находит символьной связи (делта — 2 файла src/**, изменения в основном текстовые/CSS-generation внутри уже покрытых функций); решение — см. ниже
node scripts/smoke-select.mjs --base origin/dev --head HEAD (полный охват фичи, как в r1) да те же 78 прямых совпадений, что в r1 (делта раунда их не меняет) — решение r1 унаследовано без повтора
python -m pytest tests_backend нет делта раунда не трогает ни одного .py-файла (git show 106fdc09 --stat — 0 совпадений custom_components/.*\.py); гейт неприменим к этой делте
npm run golden:verify нет делта User-Visible: no, ни одна золотая сцена не задета
npm run invariants нет делта не меняет каноническую геометрию (только презентационные occluders/CSS-эмиссию)
Полный npm test нет, принят по Validate на этом SHA —

Решение по smoke-select --base 2ed05031 --head HEAD (НЕОПРЕДЕЛЁННОСТЬ). Инструмент не связал делту ни с одним смоком символьно (изменения в iso-sun.ts не тронуты вовсе — только новый юнит-тест их покрывает; изменения в iso-tiles.ts — две дополнительные CSS-строки внутри уже существующей функции). Это не разрешение ничего не прогонять (по инструкции промпта): я сам выбрал и прогнал smoke_iso_sun.mjs и smoke_iso_tiles.mjs — ровно те смоки, что физически упражняют изменённые функции (computeIsoSunBeams, isoTileStateCss) через реальный рендер в браузере, и лично воспроизвёл падение всех трёх новых мутантов на них. Остальные ~275 смоков матрицы не запускал: делта не трогает ничего вне iso-sun.ts/iso-tiles.ts/тестов/доков.

Находки

Нет ни High, ни Medium, ни Low. Делта раунда полностью и по существу закрывает M1 (доказано исполнением: тест и три мутанта, все три реально покраснели) и пять Low из шести (L1, L3, L4, L5, L6); L2 была снята ещё в r1.

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

Находка Чем закрыта Где это видно
M1 (Medium) — обрезка 2.5D-луча барьерами (физические тела, Solid-перегородки) нигде не доказана тестом; названный риск — перепутанный знак направления или длина экструзии в directionalOccluders(input.occluders, away, travel) Новый юнит test/iso-stage6.test.mjs («AC7 the 2.5D beam is cut…», колонна на пути луча: лит перед ней, тень за ней, лит рядом) + новые ассерты demo/smoke_iso_sun.mjs (bodyCutsTheBeam, beamBackWithoutBody, Solid-перегородка в реальном рендере) + три мутанта реестра (iso-sun-occluders-toward-sun, iso-sun-occluders-ignored, iso-sun-card-drops-occluders). Все три лично применены и подтверждены красными в этой сессии (см. таблицу гейтов) test/iso-stage6.test.mjs:151-179; demo/smoke_iso_sun.mjs:99-130; scripts/mutation-registry.mjs:3526-3546; код неизменён — src/iso-sun.ts:111-114
L1 — устаревшие «alpha/hidden»-формулировки рядом с исправленным текстом Переписаны все 4 места, проверено на противоречие соседним абзацам docs/DEVELOPMENT.md:231-232, docs/ARCHITECTURE.md:31, src/houseplan-card.ts:627, demo/benchmark_large_house.mjs:145
L2 — опечатка числа в хендоффе (не код) Была снята уже в r1 без правки; в этом раунде править нечего —
L3 — isoTileStateCss() не различал тему для торца состояний Добавлены .dev.theme-dark.${state} / ...iso-floor-light со своим вызовом edge(body,'dark',...), специфичность выше старых правил (проверено пересчётом), покрыто новым ассертом юнита src/iso-tiles.ts:113-116; test/iso-stage6.test.mjs:68-70
L4 — «Alert > чистый Hover» и «Alert > чистый Selected» не проверялись отдельно Два новых прогона: реальный mouse.move + alarm без селекта/фокуса; sel+alarm без hover/focus demo/smoke_iso_tiles.mjs:197-200,207-221
L5 — нет теста на .dev.virtual (Q1: без пунктира) Новый ассерт virtualNoDashedRing на .device-shell-frame с классом .virtual demo/smoke_iso_tiles.mjs:201-203,222
L6 — плитки/тени без стен (show_borders:false) не покрыты тестом Новые ассерты noBordersKeepsTiles/noBordersKeepsShadows через реальный setServerConfig demo/smoke_iso_tiles.mjs:224-238

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

Без повторной проверки, документ docs/reviews/CODE-REVIEW-649-r1.md, материал — фактическое содержимое e5ab4659/2ed05031 (идентично по существу тому, что рецензировалось в r1 под старыми SHA e59c89df/de452182, см. обоснование выше — ребейз без изменения содержимого):

  • AC1 (Flat неизменен, test/styles-split.test.mjs, единственный вызов furniturePlanScreenScale), AC2 (геометрия/торец плитки), AC3 (коллизии ×1.12), AC4 (тень, таблица параметров), AC5 (рамки/приоритет — базовая часть, за вычетом закрытых сейчас L4-пар), AC6 (forced-colors), AC7 (формула луча, градиенты, стопы, ворота видимости — за вычетом закрытого сейчас M1), AC8 (мебель, stroke-width), AC9 (тема не красит стены), AC10/AC11 (settings.volumetric_view, alpha-вход убран, backend-валидация), AC12 (макеты, side-by-side), Golden/матрица, мутационный реестр (16 старых мутантов), бюджет бандла — все приняты по документу r1 без повторного прогона в этом раунде, т.к. делта 106fdc09 их не касается.
  • python -m pytest tests_backend не прогонялся ни в r1 (нет pytest в песочнице), ни сейчас (делта не трогает Python) — тот же непокрытый гейт, не расширившийся.
  • ~70 «слабых» совпадений smoke-select --base origin/dev — решение r1 не прогонять сохраняется без изменений (делта их не касается).

Проверено и корректно

  • M1 закрыта доказательством через исполнение, а не только чтением: юнит и смок падают именно там, где ожидается (перед барьером — светло, за ним — тень, рядом — снова светло; площадь луча уменьшается минимум на 20×50 плановых единиц), и все три мутанта реестра лично воспроизведены красными, затем код и бандл возвращены в состояние HEAD (git status чист).
  • L3 — фикс специфичностно корректен (новые селекторы объективно перебивают старые) и сегодня не меняет видимый результат ни для одного из 6 ISO_STATE_BODIES (пересчитано вручную по порогам luma<70/luma>0.98), что подтверждает трейлер User-Visible: no.
  • L1 — все 4 места переписаны без противоречий с соседним текстом; других вхождений «2.5D как alpha/hidden/opt-in» в кодовой базе не осталось (grep по изменённым и смежным файлам).
  • L4/L5/L6 — только новые тесты поверх неизменного, уже корректного (по выводу r1) поведения; лично прогнаны, все зелёные.
  • Три-way синхронизация бандла (dist/**, custom_components/.../frontend, demo/srv/assets) чиста на 106fdc09 — подтверждено дважды: один раз как есть, один раз после применения и отката мутанта iso-sun-card-drops-occluders.
  • Монолит src/houseplan-card.ts — 12880 строк, без изменений (правка L1 в нём — замена текста комментария в тех же строках, не добавление).
  • Трейлеры всех трёх коммитов раунда корректны; User-Visible: no на 106fdc09 обоснован построчно (доп. тесты + одна текстуально верная, но сегодня недостижимая ветка CSS).

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

  • python -m pytest tests_backend — делта не трогает custom_components/**/*.py (0 совпадений в статистике коммита); не гейт этой делты.
  • npm run golden:verify / npm run invariants — делта User-Visible: no, не меняет золотые сцены и не трогает каноническую геометрию модели.
  • Полный npm test и полный Validate (мутанты) — приняты по зелёному прогону на этом самом SHA (ссылка в задаче); перегонял только те тесты и мутанты, что относятся к делте раунда.
  • ~275 смоков вне smoke_iso_sun.mjs/smoke_iso_tiles.mjs — smoke-select с базой ровно на делту (2ed05031..HEAD) не нашёл символьной связи (НЕОПРЕДЕЛЁННОСТЬ) ни с одним из них, и содержательно делта не выходит за пределы iso-sun.ts/iso-tiles.ts/тестов/документации; полный прогон матрицы против всей фичи (78 прямых совпадений origin/dev..HEAD) унаследован из r1 без повтора.
  • Реальный экран/планшет, Safari — как и в r1, недоступны в песочнице.

Вывод

High: 0. Medium: 0. Low: 0. Делта раунда (106fdc09) полностью закрывает единственную блокирующую находку r1 (M1) доказательством через исполнение — я лично применил все три новых мутанта реестра и подтвердил, что юнит и смок падают именно так, как заявлено в because, а не просто прочитал код на доверие. Пять Low из шести получили тесты или точечные правки без побочных эффектов (L3 пересчитан на отсутствие видимого изменения сегодня — трейлер User-Visible: no корректен); L2 была снята ещё в r1. Дельта локальна и изолирована (src/iso-sun.ts не изменён вовсе, src/iso-tiles.ts — 2 строки, остальное — тесты/доки/бандл), новых находок не внесла.

Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0


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

  • Ветка: issue/649-25d-stage6, коммит 106fdc096d5d — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 77286175d538a146863d0b2f80a63e9ffa20af96
    git log --all --format='%H %T' | grep 77286175d538
    
  • Тело issue: e7d94f185bc9a5d2d7e168933579903e54c88a9819347cb49f0f7c35db60a9e7
  • Вердикт конвейера: green · High 0