Files
houseplan-card/docs/reviews/CODE-REVIEW-242-r2.md
T
claude[bot] b49983fd8d
Validate / docs (push) Failing after 24s
Validate / reuse (push) Successful in 36s
Validate / provenance (push) Successful in 1m11s
Validate / changes (push) Successful in 1m11s
Validate / process-gate (push) Failing after 1m17s
Validate / hacs (push) Failing after 19s
Validate / hassfest (push) Failing after 20s
Validate / backend (push) Failing after 8m40s
Validate / frontend (push) Successful in 8m46s
Validate / golden (push) Failing after 15m26s
Validate / performance_smoke (push) Failing after 14m38s
Validate / smoke (push) Failing after 41m9s
docs: review document for #242
Issue: #242
User-Visible: no
2026-08-22 18:35:35 +00:00

19 KiB
Raw Blame History

CODE-REVIEW-242-r2

  • Issue: #242 — «Проём в толстой стене рисуется у произвольной грани, а не по центру толщины»
  • Этап: code (PROCESS.md §2.7)
  • Заход: r2 (блокирующих циклов израсходовано 1 из 4; CODE-REVIEW-242-r1 — жёлтый)
  • Диапазон полного цикла: origin/dev..HEAD = 0033138..1885c77
  • Предыдущий раунд: CODE-REVIEW-242-r1, вердикт жёлтый, получен на SHA 5e13e34
  • Дельта этого раунда: git diff 5e13e34..HEAD = 2 коммита:
    • a65db39 fix(openings): preserve gate flip turn direction (User-Visible: yes) — правка H1
    • 1885c77 docs: refresh opening screenshot provenance (User-Visible: no) — обновление фингерпринта скриншотов после правки, тронувшей src/**
  • Вердикт: зелёный

Скоуп

r1 закрыл жёлтым вердиктом с одной блокирующей находкой (H1, High, в скоупе) и одной находкой в скоупе (M1, Medium): flip_v у ворот не менял знак 10° поворота на общей стене двух комнат и на независимой (partition) стене — только на однокомнатной внешней стене; и заявленный автором golden-охват («2 сцены») был занижен на порядок (фактически ~48). Разбор этого раунда ограничен дельтой 5e13e34..HEAD, потому что дельта локальна: два коммита, оба прицельно адресуют H1 и M1, не трогают резолверы wall-thickness.ts / partition-openings.ts (AC1/AC2/AC7) и не меняют контракт flip_v.

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

Находка Чем закрыта Где это видно
H1 (High) — flip_v не менял знак 10° поворота ворот на общей стене и на независимой стене a65db39 убирает вторую зеркальную инверсию sy для ворот в renderOpeningVisibleGeometry (src/render/opening-symbol.ts) и в leafBasis/buildIsoOpeningBasis (src/iso-openings.ts): gateAngle = spec.face.side * 10 * amount вместо spec.face.side * sy * 10 * amount. face.side уже несёт знак flip_v через faceFlipV в houseplan-card.ts/space-render.ts — второе умножение на sy его гасило src/render/opening-symbol.ts:107-117, src/iso-openings.ts:80-118; подтверждено моим независимым прогоном demo/smoke_wall_thickness.mjs (sharedGateFlipReversesTurn: true, partitionGateFlipReversesTurn: true) и demo/smoke_isometric_contract.mjs (isoGateFlipReversesTurn: true), плюс ad hoc browser-проверкой третьего (однокомнатного внешнего) пути, который новые тесты не покрывают напрямую (см. «Как проверялось»)
M1 (Medium, в скоупе) — заявленный golden-охват занижен на порядок; §12.3 сцены не добавлены a65db39 добавляет 4 обязательные семантические сцены (opening-symbol-room-wall-light, opening-symbol-diagonal-partition-dark, opening-symbol-flip-pairs-light, isometric-opening-symbol-parity-dark) с pre-PNG semantic guard в demo/golden/run.mjs (assertOpeningSymbolContract), который до сравнения PNG проверяет центр, jamb-глубину, op-glass, отсутствие scaleY-инверсии у ворот и противоположный знак поворота в паре flip_v. Отдельно документирует точный список из 67 существующих golden-сцен, задетых общими фикстурами (docs/TESTING.md, OPENING_SYMBOL_EXISTING_GOLDEN_IMPACT в demo/golden/matrix.mjs, застрахован unit-тестом) demo/golden/matrix.mjs (OPENING_SYMBOL_EXISTING_GOLDEN_IMPACT, длина 67), demo/golden/run.mjs:486-600 (assertOpeningSymbolContract), docs/TESTING.md раздел «Opening symbol centreline (#242)», test/golden-matrix.test.mjs (новый тест «opening symbol goldens lock…»). Частично не сходится с фактом — см. L1 ниже

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

Из CODE-REVIEW-242-r1 (документ на SHA 5e13e34) принято без повторной проверки, так как дельта 5e13e34..HEAD не касается этого кода:

  • AC1/AC2 для room-wall и partition путей (центр стены, независимость от порядка комнат/направления оси) — src/wall-thickness.ts, src/partition-openings.ts, src/opening-symbol-placement.ts дельтой не тронуты.
  • AC3 (flip_v: true у door/window, окно целиком едет одним translate) — логика для non-gate типов не изменена (renderSy/sy для door/window идентичны до и после a65db39; подтверждено тем, что затронутые тесты test/opening-symbol.test.mjs для door/window не менялись в дельте, и полный npm test зелёный).
  • Jambs full-depth — jambHalf в openingVisibleMetrics дельтой не тронут.
  • AC7 (без миграции/схемы) — OpeningCfg.flip_v не тронут этой дельтой.
  • Три копии бандла и mutation-gate (кроме нового мутанта) — методология подтверждения не изменилась.

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

Прочитан git diff 5e13e34..HEAD построчно: src/render/opening-symbol.ts, src/iso-openings.ts (продуктовый код), demo/golden/harness.mjs, demo/golden/matrix.mjs, demo/golden/run.mjs, demo/smoke_wall_thickness.mjs, demo/smoke_isometric_contract.mjs, scripts/mutation-gate.mjs, test/opening-symbol.test.mjs, test/iso-openings.test.mjs, test/golden-matrix.test.mjs, docs/TESTING.md, docs/CHANGELOG*.md, docs/images/screenshots.json. Дистрибутивные копии бандла (dist/**, demo/srv/assets/**, custom_components/**/frontend/**) не читались построчно — сверены по npm run build (см. ниже).

Гейты, реально прогнанные в этой сессии:

Гейт Результат
npx tsc --noEmit green
npm test green, 1096/1096
npm run build + сверка dist / demo/srv/assets / custom_components/.../frontend green, все три sha256 = fc19a1dce77b39b7bf67008f165980d59705b84a844f723fb84abfb185d32d16, git status чист
node scripts/check-docs.mjs green (7 файлов, 10 внешних ссылок)
node scripts/mutation-gate.mjs --check green, включая новый мутант opening-gate-flip-cancels-turn
node scripts/smoke-select.mjs --base origin/dev --head HEAD «Зарегистрированная связь» (2): smoke_isometric_contract.mjs, smoke_wall_thickness.mjs — вывод инструмента приведён ниже
node demo/smoke_wall_thickness.mjs green: sharedGateFlipReversesTurn: true, partitionGateFlipReversesTurn: true (и весь остальной набор), OK
node demo/smoke_isometric_contract.mjs green: isoGateFlipReversesTurn: true (и весь остальной набор), OK
npm run golden:verify на HEAD (1885c77) 92 сцены: 16 passed, 68 different, 8 missing-baseline; сравнено построчно с origin/dev
npm run golden:verify на origin/dev (5bf1868, отдельный git worktree) 22 different / 66 passed / 8... фактически другой набор — использован для сравнения actualSha256, не для приёмки

Вывод smoke-select.mjs:

Изменено файлов src/**: 4 · символов проекта на изменённых строках: 6
Матрица: 168 смоков · порог «широкого» символа: больше 33 смоков

Зарегистрированная связь (2):
  demo/smoke_isometric_contract.mjs ← openingSymbolOffset
  demo/smoke_wall_thickness.mjs ← openingSymbolOffset

Оба прогнаны (см. таблицу). Полная матрица (168 смоков) не прогонялась: дельта — два файла рендера ворот плюс golden/mutation-инфраструктура, не задевающая несвязанные подсистемы; выбор smoke-select уже покрывает ровно тот геометрический путь, который правит a65db39.

Дополнительная независимая проверка (не покрыта новыми тестами автора). Новые тесты (test/opening-symbol.test.mjs, test/iso-openings.test.mjs, demo/smoke_wall_thickness.mjs, demo/smoke_isometric_contract.mjs, golden-фикстура opening-symbol-flip-pairs-light) проверяют знак поворота ворот только на общей стене двух комнат и на независимой стене. Ни один из них не воспроизводит третий путь из r1 (однокомнатная внешняя стена, available[0].side вместо константы -1) — единственный, который в r1 уже работал корректно, и который эта дельта могла случайно сломать, так как формула в обеих затронутых функциях изменена безусловно для всех ворот, а не только для «сломанных» путей. Я independently прогнал ad hoc browser-скрипт (тот же харнесс demo/serve.mjs, что и существующие demo/smoke_*, не закоммичен — репозиторий трогать нельзя): толстая внешняя стена одной комнаты (_wallThickClick([50,250]), cm=20), двое ворот flip_v: false/true. Результат: extGateDefaultTurn: 10, extGateFlippedTurn: -10, extGateFlipReversesTurn: true — путь по-прежнему работает корректно, регрессии нет.

Проверка «умеет ли тест падать». Для новых unit-тестов — scripts/mutation-gate.mjs'ный мутант opening-gate-flip-cancels-turn восстанавливает старую формулу (spec.face.side * sy * 10 * amount) и гейт подтверждает, что таргетный тест opening-symbol.test.mjs его ловит (--check прогнан и зелёный, что означает: без патча тест проходит, с патчем — нет; это и есть механика mutation-gate.mjs). Для golden semantic guard — код assertOpeningSymbolContract явно бросает semantic golden gate flip does not reverse turn при pair[0].turn + pair[1].turn) > epsilon, что структурно не может не упасть при регрессии знака. Для двух browser-смоков — проверено чтением: до a65db39 эти же строки (sharedGateFlipReversesTurn, partitionGateFlipReversesTurn, isoGateFlipReversesTurn) не существовали; их ассерты === -default буквально требуют разных знаков, что при откате патча (см. мутант выше) стало бы false.

Находки

L1 (Low) — точный список из 67 существующих golden-сцен на самом деле содержит 2 сцены, не задетые этой правкой, и не хватает 0 сцен

Что не так. docs/TESTING.md и OPENING_SYMBOL_EXISTING_GOLDEN_IMPACT в demo/golden/matrix.mjs заявляют «измерено сравнением actualSha256 для HEAD и origin/dev» и дают список ровно из 67 id. Я повторил это сравнение буквально: прогнал npm run golden:verify на HEAD (1885c77) и на origin/dev (5bf1868 — это ровно git merge-base HEAD origin/dev, тот же SHA, на который должен был ссылаться автор) в отдельном git worktree, и сравнил artifacts/golden/golden-report.json.results[].actualSha256 попарно по id. Из 69 сцен с расхождением по 4 новым opening-symbol-* исключены (их не с чем сравнивать на dev — они там не существуют); из оставшихся 65 всё совпадает с декларированным списком, кроме двух: junction-patch-resilience-plan-dark и large-house-zoom-250-dark заявлены как часть списка, но их actualSha256 идентичен на HEAD и на dev (проверено: оба совпадающих хэша процитированы в логе) — то есть эти два кадра не изменились из-за правки #242, у них обоих статус different относительно baseline по совсем другой, уже существующей на dev причине (та самая «unrelated pending pre-release candidates», о которой предупреждает сам документ). Не хватает ни одной сцены — то есть под-учёта (того, что было проблемой в M1 r1) больше нет; есть только два лишних пункта.

Почему это Low, а не Medium. Направление ошибки безопасное: список переоценивает влияние (2 лишних пункта из 67), а не недооценивает его — риск пропущенной регрессии не растёт. Расхождение не влияет ни на один AC и не меняет видимое поведение; unit-тест (test/golden-matrix.test.mjs) фиксирует именно то число и тот список, что сейчас в коде, поэтому сам гейт не "врёт" сам себе — врёт только английская/русская проза документа и сопроводительный комментарий о методе измерения.

Серьёзность. Low. Не блокирует. Можно поправить точечно (убрать два id из OPENING_SYMBOL_EXISTING_GOLDEN_IMPACT и синхронизировать docs/TESTING.md) в любой последующей правке этой области; отдельный issue не нужен ни по объёму, ни по риску.

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

  • H1 закрыт end-to-end на всех трёх путях host (общая стена, независимая стена, однокомнатная внешняя стена) и в Iso — см. «Как проверялось» и таблицу «Закрытие раунда r1». Trailers Issue/User-Visible на a65db39 корректны, оба changelog обновлены в этом же коммите.
  • M1 практически закрыт: 4 обязательные по §12.3 golden-сцены добавлены с pre-PNG semantic guard, точный (за вычетом L1) список существующих задетых сцен задокументирован и застрахован unit-тестом, GOLDEN_MATRIX_VERSION поднят до 37 и это отражено в тесте на sun-ray сценарий. docs/TESTING.md явно запрещает локальный golden:accept для этих 71 сцены (4 новых + 67 существующих) — только через reviewed Linux-артефакт.
  • 1885c77 — точечное обновление docs/images/screenshots.json и одного PNG (05-plan-context-tray.png) после того, как sourceFingerprint устарел из-за правки src/**; check-docs.mjs зелёный, только один скриншот реально сменил пиксели (гейт ворот виден в тестовом фрейме), что соответствует масштабу правки.
  • Отсутствие регрессии для door/window. renderSy/sy в render/opening-symbol.ts и sy в iso-openings.ts для типов, отличных от gate, не изменились; npm test зелёный без единого затронутого door/window-теста в дельте.
  • Три копии бандла и git status — синхронны и дерево чисто после build.

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

  • Полный Linux golden-артефакт и его reviewed-приёмка — вне цикла код-ревью (§13), как и в r1. Локальный golden:verify обслуживает только проверку L1/M1-охвата, не приёмку эталонов.
  • python -m pytest tests_backend — не прогонял: custom_components/**/*.py дельтой 5e13e34..HEAD не тронут (только скопированный бандл custom_components/houseplan/frontend/houseplan-card.js, сверенный по sha256).
  • Performance-профили — не прогонял: дельта не меняет асимптотику, только константу (одно сравнение типа вместо перемножения на sy).
  • Полная browser-smoke матрица (168 сценариев) — не прогонял всю, кроме двух названных smoke-select; см. обоснование выше. Дополнительно прогнал вручную (не закоммичено) третий host-путь ворот, который выбор smoke-select не called напрямую.
  • Унаследованные из r1 пункты — см. раздел «Унаследовано из r1»; они не передоказывались, так как дельта их не касается.