Files
houseplan-card/docs/reviews/CODE-REVIEW-580-r2.md
T
2026-09-15 06:16:08 +00:00

18 KiB

CODE-REVIEW-580-r2

Issue: #580 · Этап: code · Заход: r2 · Материал: c044e9ea3861b777c04233073a02a0a3e2da354a (рабочая копия проверена на этом SHA, git fetch/checkout не выполнялись)

Почему это r2, а не продолжение r1

Первый код-ревью (CODE-REVIEW-580-r1.md) вынес зелёный вердикт на материале 4cb6827c3d1ea0e8307ff7f406b425262c25649a. Пока ревью шло, origin/dev продвинулся на один коммит, автоматическое слияние отказало (§7.2 — «это другой код»), и автор перебазировал ветку на новый origin/dev скриптом scripts/rebase-on-dev.mjs (комментарий issue от 2026-09-15T06:07:14Z). Новая вершина — c044e9ea — и это материал текущего раунда. Старые SHA r1 (4cb6827c, 23e66392, c22eaf7d) после ребейза не существуют в истории (git cat-file -t — Not a valid object name), поэтому дельта «дословно» через git diff <старый SHA>..HEAD невозможна.

Это ребейз на ушедший вперёд dev — по правилам раунда такой случай не сокращается до дельты и требует полного разбора. Ниже — полный разбор, но методом, который эксплуатирует то, что можно проверить дёшево: побайтовое сравнение каждого изменённого файла в git diff origin/dev...HEAD с тем, что цитировал и проверял r1, плюс независимая проверка, что тяжёлые CI-гейты на новом SHA подтверждены не «переиспользованием ради переиспользования», а совпадением хеша содержимого с ранее проверенным деревом.

Скоуп

git log --oneline origin/dev..HEAD — шесть коммитов, из них три содержательных (остальные три — docs: review document for #580, коммиты документов SPEC-REVIEW r1/r2 и этого самого CODE-REVIEW r1, наследие этапа spec и предыдущего раунда, не трогаются):

  • d2c7e6f7 (fix, User-Visible: yes) — окклюзия комнатной части внешнего луча внутренними откосами толстой стены.
  • a32b280b (fix, User-Visible: no) — типизация того же кода (Geom вместо as any).
  • 8b8260b2 (test, User-Visible: no, несёт Release: v1.76.0-beta.4 и Baseline-Reviewed:) — принятие двух golden-эталонов (DPR1/DPR2).

git diff origin/dev...HEAD --stat: 57 файлов, из них по существу — src/sun.ts, test/sun.test.mjs, test/golden-matrix.test.mjs, demo/smoke_sun.mjs, demo/golden/matrix.mjs, scripts/mutation-registry.mjs, docs/SUN.md, оба CHANGELOG; плюс два новых PNG-эталона, demo/golden/baselines/baselines-index.json и весь бандл (dist/**, custom_components/houseplan/frontend/**) — бандл пересобран с новыми хешами файлов из-за нового dev в основании, это ожидаемо и не несёт логики. custom_components/**/*.py в диффе отсутствует — AC7 по touch/i18n/схеме не задет.

Трейлеры: d2c7e6f7 — User-Visible: yes, оба changelog в этом же коммите (проверено git show --stat). 8b8260b2 — Release:/Baseline-Reviewed: со ссылкой на реальный run.

Проверка того, что ребейз не изменил код по существу

Построчно сравнил git diff origin/dev...HEAD для каждого содержательного файла с текстом, который CODE-REVIEW-580-r1.md цитировал и разбирал:

  • src/sun.ts — diff идентичен описанному в r1 (тот же intersectRings, та же сигнатура rayRimEdges/rimSources, те же номера механик — intersectRings(quad, rayQuad(innerA, innerB, away, len), clipPoly) и дедупликация источников по eps=1e-4).
  • test/sun.test.mjs — оба новых теста (#580: an oblique outer ray..., #580: normal incidence...) побайтово совпадают с тем, что цитировал r1, включая константу 328.4529946162075.
  • demo/smoke_sun.mjs — тот же pointInRing, тот же innerFaceX через отражение экрана, те же четыре новых check(...).
  • demo/golden/matrix.mjs, test/golden-matrix.test.mjs — та же пара сценариев dpr{1,2}, версия матрицы 62→63.
  • scripts/mutation-registry.mjs — те же два новых мутанта (sun-ray-outer-jamb-occlusion-ignored, sun-ray-outer-occluded-rim-source-ignored), find-паттерны совпадают со строками текущего src/sun.ts.
  • docs/SUN.md, оба CHANGELOG — тот же текст.

Вывод: продуктовая дельта #580 после ребейза не изменилась ни на строку — изменился только бандл (новые хеши ассетов) и добавились три коммита документов ревью. Это подтверждает и сам автор в комментарии («продуктовая дельта #580 не менялась»), но здесь это установлено чтением диффа, а не принято на слово.

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

Гейты

Валидация на точном материале c044e9ea — зелёная, дважды: run 34935586944 (упомянут во вводном сообщении) и более ранний 34935525859 того же SHA — оба conclusion: success.

Не поверил заявлению «зелёный» буквально: посмотрел job-логи run 34935586944 и обнаружил, что тяжёлые джобы (browser-смоки, golden, perf-смок, geometry-parity, backend) в нём skipped через Переиспользование: это дерево уже проверено. Чтобы отличить легитимный реюз от «зелёного, потому что не запускалось», прочитал лог этого job'а: ключи реюза считает scripts/gate-reuse.mjs --job=<job> по содержимому, не по SHA коммита и не по полному git-дереву (иначе ребилд бандла сломал бы совпадение):

job результат
smoke Cache hit for: reuse-smoke-bb7d10d9…
golden Cache hit for: reuse-golden-ac206839…
performance_smoke Cache hit for: reuse-performance_smoke-c2e55df8…-glow
geometry_parity Cache not found (ожидаемо — diff не трогает модель геометрии)
backend Cache not found (ожидаемо — custom_components/**/*.py не тронут)

Кэш-хиты для smoke/golden/performance_smoke означают, что релевантное содержимое (то, что реально проверяют эти гейты) на c044e9ea побайтово совпало с деревом, на котором r1 уже нашёл реально выполненный (не переиспользованный) прогон — 34933022019, зафиксированный в r1 как run с 3/3 browser-смоками, golden и 6/6 mutation-шардами зелёными. Это закрывает AC4/AC5 без повторного локального прогона: реюз здесь не «дыра», а детерминированная функция от неизменного содержимого, что я проверил, а не предположил.

Мутационные джобы (Мутанты по диффу 1–6/6) на c044e9ea — не переиспользованы, выполнены заново и зелёные: npx tsc --noEmit/typecheck и юнит-тесты прошли свежо на этом самом SHA.

Не гонял локально: npx tsc --noEmit, npm test, npm run build, node scripts/check-docs.mjs, npm run golden:verify — покрыты зелёным Validate на точном SHA (см. выше), плюс отдельно проверенным содержательным реюзом. npm run invariants, python -m pytest tests_backend — diff не касается персистентной модели геометрии (рёбра/толщина/layout/marker.space/open_spans) и не трогает custom_components/**/*.py; толщина стены здесь только читается (wallDepthByOpening), не записывается.

Разбор кода

Продуктовый код (src/sun.ts) не изменился относительно того, что r1 уже разобрал построчно (независимый вывод формулы окклюзии в базисе {нормаль, тангенциальная ось окна}, ручной пересчёт константы 328.4529946162075 с совпадением до 13 знаков, проверка что мутанты бьют именно по изменённым строкам, проверка что demo/smoke_sun.mjs — независимая, не скопированная из продукта реализация геометрии). Побайтовое совпадение диффа (раздел выше) означает, что этот разбор остаётся в силе без повторного вывода — не потому что я поверил документу r1, а потому что сверил текст, который он проверял, с текстом, который лежит в origin/dev...HEAD сейчас.

Дополнительно к r1 проверил:

  • что три «продуктовых» коммита (d2c7e6f7, a32b280b, 8b8260b2) после ребейза не имеют собственных CI-прогонов (gh run list --commit <sha> → [] для каждого) — Validate гонялся только на итоговом SHA ветки, что ожидаемо для push после ребейза без промежуточных пушей;
  • что коммиты d37b2c9f (Prepare v1.76.0-beta.4) и e54ff33c (новый tip origin/dev, docs-ревью #581), которые вошли в основание после ребейза, не трогают src/sun.ts и вообще не пересекаются с темой солнечных лучей — подтверждено git diff d37b2c9f..e54ff33c -- src/sun.ts (пусто) и git show --stat обоих коммитов;
  • что коммиты #577 (внешний луч, та же тема) целиком лежат в основании до ребейза (были в dev ещё когда открывалась ветка #580) — новых пересечений с #577 не возникло, src/sun.ts в диапазоне d37b2c9f..e54ff33c не менялся.

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

находка r1 статус
Блокирующих находок не было —
Не-Medium наблюдение (неточность формулировки ТЗ «одно дополнительное пересечение») не относится к коду; сам код и диагноз не изменились ребейзом — наблюдение остаётся зафиксированным для памяти, не требует действия

Раздел «находка → чем закрыта» здесь вырожден: r1 был зелёным без замечаний в скоупе задачи, поэтому r2 не закрывает правки — он подтверждает, что зелёный вердикт остаётся верным на новом материале после механического (не содержательного) ребейза.

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

Из docs/reviews/CODE-REVIEW-580-r1.md (материал 4cb6827c3d1ea0e8307ff7f406b425262c25649a, дерево eb607a98d8c5a3f34ff9545a589358b50988c20f) принято без повторного самостоятельного вывода, поскольку побайтовое сравнение диффов (раздел выше) подтвердило отсутствие изменений в этих файлах:

  • независимый математический вывод формулы окклюзии и ручной пересчёт константы 328.4529946162075 — сверено, что тест и код не изменились;
  • анализ rayRimEdges/uniqueSources/отката на [ray.a, ray.b] при rimSources === undefined (AC3, режим inner/d<=0) — код не изменился;
  • проверка независимости demo/smoke_sun.mjs (pointInRing, innerFaceX через отражение) от продуктовой формулы — файл не изменился;
  • проверка demo/golden/matrix.mjs/хешей PNG на не-пустой принятый эталон и на корректный DPR при рендере — сценарии не изменились, только версия матрицы;
  • вывод «единственный источник числа» (обрезанная ширина луча входит в тот же SunRay.polys, второго независимого вычисления в продукте нет) — код не изменился.

Найдено

Блокирующих находок нет.

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

  • AC1–AC3 — код идентичен r1-проверенному, юнит-тесты не изменились.
  • AC4 (browser smoke), AC5 (golden DPR1/DPR2) — реюз-кэш на точном SHA c044e9ea подтверждён как совпадение по содержимому с деревом, где эти джобы реально выполнялись (см. таблицу gate-reuse выше), а не как слепое доверие статусу Validate.
  • AC6 — Validate зелёный на точном материале раунда, двумя независимыми run.
  • AC7 — diff по-прежнему ограничен солнечной геометрией/тестами/документацией и пересборкой бандла; custom_components/**/*.py, UI, i18n, touch-контракт и схема конфигурации не тронуты.
  • Трейлеры и оба changelog — в правильном коммите (не изменилось).
  • Ребейз на новый dev — чисто механический: без конфликтов исходников, продуктовая дельта #580 не изменилась ни на строку, новое основание (d37b2c9f, e54ff33c) не пересекается с темой солнечных лучей.

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

  • Не запускал npx tsc --noEmit, npm test, npm run build, node scripts/check-docs.mjs, npm run golden:verify локально — переиспользовал зелёный Validate на этом самом SHA, дополнительно проверив легитимность реюза тяжёлых джобов по ключам содержимого (см. выше), а не только по статусу.
  • Не запускал npm run invariants — diff не касается персистентной модели геометрии (рёбра/толщина/layout/marker.space/open_spans).
  • Не запускал python -m pytest tests_backend — custom_components/**/*.py не тронут.
  • Не перевыводил независимо математику окклюзии и не пересчитывал константу 328.4529946162075 — унаследовано из r1, поскольку строки src/sun.ts и test/sun.test.mjs не изменились (подтверждено побайтовым сравнением диффа).


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

  • Ветка: issue/580-outer-sun-wall-occlusion, коммит c044e9ea3861 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 723c351607934580f0bfbbc19bef5b182273f939
    git log --all --format='%H %T' | grep 723c35160793
    
  • Тело issue: 558be5cfc33a8fe1f475fbc5a0210392339265c20fad0c22e82858b57c96b848
  • Вердикт конвейера: green · High 0