Files
houseplan-card/docs/reviews/CODE-REVIEW-288-r3.md
T
claude[bot] 10fa0dc7fb
Validate / process-gate (push) Failing after 44s
Validate / changes (push) Successful in 29s
Validate / hacs (push) Skipped
Validate / hassfest (push) Skipped
Validate / frontend (push) Skipped
Validate / reuse (push) Successful in 1m0s
Validate / smoke_done (push) Skipped
Validate / backend (push) Skipped
Validate / smoke (1) (push) Skipped
Validate / smoke (2) (push) Skipped
Validate / smoke (3) (push) Skipped
Validate / golden (push) Skipped
Validate / performance_smoke (push) Skipped
Validate / docs (push) Failing after 39s
Validate / process-workflow-sync (push) Successful in 38s
Validate / provenance (push) Successful in 43s
docs: review document for #288
Issue: #288
User-Visible: no
2026-08-24 13:14:55 +00:00

24 KiB
Raw Blame History

CODE-REVIEW-288-r3

  • Issue: https://github.com/Matysh/houseplan-card/issues/288
  • Этап: code (PROCESS.md §2.7)
  • Заход: r3 · блокирующих циклов израсходовано 1 из 4
  • Ветка: issue/288-bounded-multiwall-corridor
  • HEAD на момент ревью: f2c0721a9da6db255145abd14f1b52c20e15bbad
  • Диапазон: origin/dev...HEAD (origin/dev = f472af88a3e9c89fec1f2b90b6f2c10471d5c693, включает слитый в dev #289)

Почему разбор полный, а не по дельте

Формально третий заход код-ревью, но объём разбора — полный §2.7, а не по дельте §2.10. Причина явно названа владельцем в issue, а не выведена мной: после зелёного вердикта CODE-REVIEW-288-r2 слияние ветки в dev конфликтовало (владелец, issue #288, предпоследний комментарий): «Не удалось только слияние: ветка issue/288-bounded-multiwall-corridor конфликтует с dev. … Повторный прогон ревью — не формальность: после ребейза на новый dev это другой код, и принимать его без проверки нельзя». Это прямое применение PROCESS.md §7.2/§2.10 («ребейз на ушедший вперёд dev — другой код»), подтверждённое фактом: git merge-base origin/dev HEAD = f472af88 — текущий tip dev, то есть ветка #288 целиком лежит поверх свежего dev, в который между r2 и r3 влился #289 («fix: prevent mixed-role walls during resize»).

Доказательство, что это настоящий ребейз, а не просто новый коммит сверху: SHA r2 (HEAD на момент ревью: 162f9b0eca8f61b4ae1d716785f1f94f8d448acc) не существует в текущем репозитории:

$ git cat-file -e 162f9b0eca8f61b4ae1d716785f1f94f8d448acc
fatal: Not a valid object name 162f9b0eca8f61b4ae1d716785f1f94f8d448acc

Коммит с тем же сообщением («docs: recapture screenshots after fixture repair») присутствует в текущей истории, но под новым хешем 406e46ec — это переигранный (rebased), а не идентичный объект: контент коммита совпадает, хеш — нет, потому что родитель сменился (старый dev → новый dev с #289).

Итог: пункты 3 и 5 инструкции («чем закрыта находка» / «унаследовано») выполнены ниже, но объём проверки — полный §2.7, включая живые прогоны всех гейтов заново на текущем HEAD, как и в r2.

Скоуп диффа

git diff origin/dev...HEAD --stat: src/wall-thickness.ts (+134/-6, то же ядро фикса, что в r1/r2), test/wall-thickness.test.mjs (+51), scripts/mutation-gate.mjs (+14), demo/smoke_real_plan_masonry.mjs (числа PLANS/KNOWN_GAP_* на нулях), docs/{WALL-THICKNESS,ARCHITECTURE,TESTING, CHANGELOG,CHANGELOG.ru}.md, 2 PNG + docs/images/screenshots.json (пересъёмка после интеграции с #289), docs/reviews/{SPEC-REVIEW-288-r1, SPEC-REVIEW-288-r2,CODE-REVIEW-288-r1,CODE-REVIEW-288-r2}.md (артефакты предыдущих раундов), dist/houseplan-card.js и custom_components/houseplan/frontend/houseplan-card.js (сгенерированные, класс D).

Продуктовое ядро не изменилось ни байтом относительно того, что получило зелёный вердикт в r2. Прямая проверка контентом, а не по хешам коммитов:

$ git diff e8a4db79..HEAD --stat     # e8a4db79 — коммит доки CODE-REVIEW-288-r2
 docs/images/06-device-display-preview.png | Bin 327898 -> 327899 bytes
 docs/images/07-background-editor.png      | Bin 304173 -> 304173 bytes
 docs/images/09-device-info.png            | Bin 146160 -> 146170 bytes
 docs/images/screenshots.json              |  26 +++++++++++++-------------
 4 files changed, 13 insertions(+), 13 deletions(-)

Единственная разница между деревом, которое видел r2, и текущим HEAD — это одна дополнительная пересъёмка docs-скриншотов (f2c0721a, «после интеграции с #289»), нужная потому, что #289 тоже трогал docs/images/screenshots.json и правит фичи по соседним экранам (06-device-display-preview, 07-background-editor, 09-device-info — не относятся к multi-wall junction, относятся к общему sourceFingerprint, который считается по всему src/**). src/wall-thickness.ts, test/wall-thickness.test.mjs, scripts/mutation-gate.mjs, demo/smoke_real_plan_masonry.mjs идентичны r2 побайтово.

Проверено также, что #289 не пересекается с продуктовым кодом #288: #289 трогает src/resize.ts, src/i18n/*.json, docs/RESIZE.md, demo/smoke_room_resize.mjs, demo/golden/baselines/safe-resize-* — ни один из этих путей не входит в диапазон #288 и не пересекается с wall-thickness.ts/mutation-gate.mjs/wall-thickness.test.mjs. Единственная общая поверхность двух задач — общий отпечаток docs-скриншотов (screenshots.json, весь src/**) и три копии бандла — механическое пересечение, не логическое.

Напоминание ядра изменения (не менялось с r1): buildMultiWallNodeMap добавляет каждому MultiWallNodeRay поле continuations — конечные общие полосы («shared»-стены), примыкающие к дальнему концу одного из supports луча. bevelMultiWallBody восстанавливает эти полосы (multiWallContinuationStripGeometry) внутри маски узла и union-ит результат вместо старой тройки outside/preservedExterior/localInside.

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

r2 — зелёный вердикт, High/Medium в этом раунде не было. Единственный пункт, требующий явного отслеживания в r3, — процессный (не находка в скоупе #288, как и в r2):

Из r2 Статус в r3
Процессное наблюдение r2: вердикт-комментарии не называют SHA явно Не устранено: комментарий владельца с решением «ребейзить и вернуть на code-review» также не называет SHA. SHA пришлось восстанавливать так же, как в r2 — проверкой существования объекта (git cat-file -e 162f9b0e… → отсутствует) и сверкой сообщений коммитов. Не блокирует и не заводится отдельным issue (уже отмечено дважды в r2 и здесь — не новая находка, а тот же неустранённый паттерн; решение по нему не в скоупе #288).

Единственная оставшаяся содержательная находка — унаследованная Low из r1, не тронутая ни в r2, ни в этом раунде (см. ниже).

Унаследовано из r2 (без повторной проверки по существу, но перепроверено гейтами)

Формально разбор полный (rebase = «другой код», см. выше), поэтому все AC перепроверены живыми прогонами на текущем HEAD (таблица гейтов ниже) — не «наследование без проверки». Тем не менее по содержанию не пересматривались повторно, так как дельта их не касается:

  • Продуктовая рамка и соответствие J1/J6 из docs/SCOPE.md (docs/SCOPE.md:39-46) — текст спеки не менялся с версии, принявшей SPEC-REVIEW-288-r2 (зелёный); docs/specs/288-bounded-multiwall-corridor.md идентичен варианту, разобранному в CODE-REVIEW-288-r1/r2.
  • Диагноз причины (узел 577,299, лучи 349/120/5 шагов, 30/30/30 см, соседняя стена 20 см, радиус 4×15=60, вырез 60−15=45) — принят владельцем в теле issue, переоценке в r1/r2/r3 не подвергался.
  • Ручная экспериментальная проверка риска «маска узла A съедает то, что узел B восстановил через continuations» (синтетические сценарии, документированы в CODE-REVIEW-288-r1.md, раздел «Прочитано, не исполнено») — не повторялась, код multiWallContinuationStripGeometry/preservedExterior не менялся.

Документы-источники: docs/reviews/SPEC-REVIEW-288-r2.md (зелёный, SHA диапазона fba615e9), docs/reviews/CODE-REVIEW-288-r1.md (SHA 4aa79dee, не существует в текущем дереве — восстановлен по контенту), docs/reviews/CODE-REVIEW-288-r2.md (SHA 162f9b0e, также не существует — восстановлен по контенту, см. выше).

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

Прочитанное

docs/SCOPE.md (§ Core user jobs, J1/J6), AGENTS.md, PROCESS.md §2.7/§2.9/§2.10/§4/§7/§8/§12, тело issue #288 и все 8 комментариев (аналитика, «ТЗ готово», оба вердикта спек-ревью, правки автора, оба вердикта код-ревью r1/r2, решение владельца о ребейзе), docs/specs/ 288-bounded-multiwall-corridor.md целиком, docs/WALL-THICKNESS.md/ docs/ARCHITECTURE.md/docs/TESTING.md (диффы коммита 17086d3c), docs/reviews/CODE-REVIEW-288-r1.md, docs/reviews/CODE-REVIEW-288-r2.md, полный diff src/wall-thickness.ts, test/wall-thickness.test.mjs, scripts/mutation-gate.mjs, demo/smoke_real_plan_masonry.mjs, полный diff #289 (git diff 6a4e665d^..a7618151 --stat) — для подтверждения отсутствия файлового пересечения с #288.

Гейты — выполнено (живые прогоны на текущем HEAD)

Гейт Команда Результат
Typecheck npx tsc --noEmit чисто
Unit npm test 1225 tests, 1224 pass, 1 skipped, 0 fail
Build + bundle parity npm run build, cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js идентичны; npm run bundle:sync синхронизировал третью копию (demo/srv/assets) без расхождений
Docs fingerprint node scripts/check-docs.mjs Documentation checks passed (7 files, 10 external links) — подтверждает, что пересъёмка f2c0721a соответствует текущему src/**
Выбор смоков node scripts/smoke-select.mjs --base origin/dev --head HEAD 3 «зарегистрированные связи» на MultiWallNodeRaySupport (то же множество, что в r1/r2): smoke_junction_patch_resilience, smoke_multiwall_junction, smoke_multiwall_strip_containment; изменено файлов src/**: 1, символов на изменённых строках: 12; порог «широкого» символа (36 из 181 смоков) не превышен
Целевой смок AC1/AC2/AC5 node demo/smoke_real_plan_masonry.mjs (после bundle:sync) second-floor: gapCount 0, totalGapSteps 0 (45757 samples); first-floor: gapCount 0, totalGapSteps 0 (8048 samples, 830 skipped); все 8 проверок true, итог OK
3 «зарегистрированные связи» node demo/smoke_junction_patch_resilience.mjs, node demo/smoke_multiwall_junction.mjs, node demo/smoke_multiwall_strip_containment.mjs все три — OK, все булевы поля true (включая excessWedgeIsEmpty #249, nodeRemainsFilled #261, viewStopsAtFiniteRayEndpoint #271, cell_5_mixed_depth_t_*/cell_1_thick_crossbar_t_* #275)
Инварианты модели npm run invariants -- --config test/fixtures/real-plan-second-floor.json и то же для real-plan-first-floor.json оба: Инварианты выполнены: ссылки разрешимы, записи толщины находятся
Мутант AC7 node scripts/mutation-gate.mjs --id=multi-wall-shared-continuation-protection-disabled чистый прогон зелёный, мутант красный (тест покраснел, как обязан), поймано 1 из 1
Реестр мутантов целиком node scripts/mutation-gate.mjs --check все патчи (включая #260/#288/#289 в одном файле) применяются к текущему коду без конфликтов, exit 0
Полная золотая матрица npm run golden:verify 80/80 сценариев passed, 0 failed, включая wall-junctions-*, junction-patch-resilience-*, wall-union-isolation-*, multiwall-junction-bevel-view-dark, orthogonal-strip-cell-5/1-view-dark, wall-key-roundtrip-view-dark, isometric-wall-junctions-dark, а также golden-сцены #289 (safe-resize-handles-clamp-*); exit 0
Одно число — один источник node --test test/single-source-numbers.test.mjs pass

Полная золотая матрица прогнана по тем же причинам, что в r2: AC5/AC6 прямо требуют согласованности всех consumers, инструмент не даёт частичного прогона, а сама пересъёмка скриншотов после интеграции с #289 — ровно тот случай, когда дешевле подтвердить визуальную согласованность целиком, чем полагаться только на semantic-smoke.

Гейты — не выполнено, с причиной

  • python -m pytest tests_backend -q — диапазон не трогает custom_components/**/*.py (только сгенерированный бандл).
  • npm run performance/benchmark_* — не назван в AC8; спека §6 относит performance-профиль к предрелизному гейту («перед beta»).

Прочитано, не исполнено

  • Побайтовое содержимое пересобранного dist/houseplan-card.js не читал построчно — детерминированная сборка и cmp трёх копий достаточны.
  • Логика nearbyBuckets/дедупликации continuationKeys/самоисключения через candidate.other == node.point в buildMultiWallNodeMap (src/wall-thickness.ts:1704-1743) не перечитывалась заново построчно в этом раунде — файл байтово идентичен версии, разобранной построчно в r1/r2 (подтверждено git diff e8a4db79..HEAD -- src/, пустой вывод).
  • Отсутствие файлового пересечения между #289 и #288 — проверено чтением списков файлов (comm-эквивалент вручную): пересечение пустое, кроме docs/images/screenshots.json и трёх копий бандла (механическое, не логическое).

Находки

Ни одной находки уровня High или Medium в скоупе задачи в этом раунде. Единственная непогашенная Low — унаследованная из r1, не изменившаяся ни в r2, ни в r3: test/wall-thickness.test.mjs, тест «issue #288 keeps a shared wall attached beyond a short node ray finite» проверяет только метаданные ray.continuations, не строит wallBodiesGeometry и не измеряет фактическую геометрическую потерю площади. Не блокирует: geometрический результат подтверждён напрямую живым прогоном AC1/AC2 (gapCount: 0 на обоих реальных планах) и мутантом AC7 в этом же раунде. Автор может закрыть опционально.

Процессное наблюдение (не находка в скоупе #288, не блокирует, повторяется третий раз подряд — r2 уже фиксировал это же наблюдение): вердикт-комментарии и комментарии с процессными решениями по этому issue не называют SHA явно.

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

  • AC1/AC2 — доказаны напрямую живым прогоном: оба реальных плана дают gapCount: 0/totalGapSteps: 0 на текущем HEAD после ребейза; плотная выборка (45757/8048 точек), не только 4 исторических гэпа; npm run invariants чист на обоих планах.
  • AC3 — юнит проверяет три масштаба (cell_cm: 1/5/30), прямой и реверсированный порядок rays/endpoints, отдельно проверяет отсутствие continuation при kind: 'outer' (не нарушает #249). Ограничение теста (метаданные, не геометрия тела) компенсируется смоком AC1/AC2 и мутантом AC7 — как и в r1/r2 (см. Low выше).
  • AC4 — все семь прежних контрактов (#249/#261/#271/#272/#275/#278/#279) зелёные в npm test и в трёх браузерных смоках с прямым совпадением по MultiWallNodeRaySupport.
  • AC5 — единственный canonical wallBodiesGeometry (multiWallContinuationStripGeometry вызывается один раз внутри bevelMultiWallBody, до всех downstream потребителей — проверено чтением, файл не менялся). Полная золотая матрица подтверждает согласованность всех сцен, включая Static/hidden Iso и light/sun, на текущем HEAD.
  • AC6 — семантический gapCount === 0 подтверждён живым прогоном на текущем HEAD; полный golden:verify (80/80 passed) подтверждает отсутствие визуальной регрессии без единой переприёмки baseline специально для #288.
  • AC7 — мутант зарегистрирован, инвертирует ключевую проверку, убивается юнитом AC3 — воспроизведено живым прогоном на текущем HEAD.
  • AC8 — все локальные гейты выполнены и зелёные (таблица выше).
  • Порядок слияния из issue/§10 (наследие r1) — остаётся соблюдённым: #260 уже в dev с r2, и не затрагивается этим раундом.
  • Docs/changelog — docs/WALL-THICKNESS.md/ARCHITECTURE.md/ TESTING.md описывают именно то, что делает код (файлы не менялись с r2, сверка построчно проведена в r1/r2 и не требует повтора при неизменном диффе); docs/CHANGELOG.md/CHANGELOG.ru.md правлены в fix-коммите 17086d3c, User-Visible: yes. Трейлеры Issue: #288 — на всех 9 коммитах диапазона; User-Visible: yes — ровно на fix-коммите 17086d3c, no — на остальных (спека, документы ревью, пересъёмки скриншотов).
  • Одно число — один источник. Дифф не вводит и не меняет ни одной пользовательски видимой числовой величины; test/single-source-numbers.test.mjs прошёл отдельным прогоном на текущем HEAD.
  • Совместимость/touch/security/perf — диапазон не трогает schema/config/i18n-файлы (проверено: git diff origin/dev...HEAD --name-only | grep -E "schema|config|i18n" → пусто); изменение локально по rays/supports одного узла, не вводит новый глобальный проход по стенам.
  • Независимость от #289 — файловые множества диффов #288 и #289 не пересекаются в продуктовом/тестовом коде (проверено чтением списков путей); общая поверхность — только docs/images/screenshots.json и три копии бандла, что и объясняет коммит f2c0721a.

Гейты, которые прогнал/не прогнал — сводка

Прогнал: tsc --noEmit, npm test, npm run build + cmp трёх копий бандла (npm run bundle:sync), node scripts/check-docs.mjs, node scripts/smoke-select.mjs --base origin/dev --head HEAD, demo/smoke_real_plan_masonry.mjs, три «зарегистрированные связи» смока, npm run invariants на конфигах обеих real-plan фикстур, целевой мутант AC7, node scripts/mutation-gate.mjs --check (весь реестр), полный npm run golden:verify (80/80 passed), test/single-source-numbers.test.mjs.

Не прогнал: pytest tests_backend (Python не тронут), performance-профили (не названы в AC8).

Вердикт

Зелёный. Ребейз на ушедший вперёд dev (после интеграции #289) не изменил ни строки продуктового или тестового кода #288 — подтверждено прямым git diff содержимого дерева, а не только пересчётом хешей. Единственное новое в этом диапазоне — повторная пересъёмка docs-скриншотов, подтверждённая чистым check-docs. Полный повторный прогон всех гейтов, включая полную золотую матрицу (80/80), не выявил новых находок уровня Medium/High. Остаётся одна унаследованная Low-находка (AC3-тест ограничен метаданными, не блокирует) и одно неустранённое процессное наблюдение (SHA не называется в вердикт-комментариях) — оба не блокируют зелёный вердикт.