Files
houseplan-card/docs/reviews/CODE-REVIEW-282-r4.md
T
claude[bot] 7518cb2d4d
Validate / changes (push) Successful in 54s
Validate / frontend (push) Skipped
Validate / hassfest (push) Skipped
Validate / hacs (push) Skipped
Validate / reuse (push) Successful in 52s
Validate / smoke (1) (push) Skipped
Validate / smoke (2) (push) Skipped
Validate / smoke (3) (push) Skipped
Validate / smoke_done (push) Skipped
Validate / golden (push) Skipped
Validate / performance_smoke (push) Skipped
Validate / backend (push) Skipped
Validate / docs (push) Failing after 21s
Validate / process-workflow-sync (push) Successful in 47s
Validate / provenance (push) Successful in 49s
Validate / process-gate (push) Failing after 54s
docs: review document for #282
Issue: #282
User-Visible: no
2026-08-26 00:11:34 +00:00

16 KiB
Raw Blame History

CODE-REVIEW-282-r4

  • Issue: #282 — стабильная идентичность сегментов стен (ADR Stage 1)
  • Ветка: issue/282-wall-geometry-model, HEAD f37efdad8a8f677f0af94ce0297641add5617c4e
  • ТЗ: docs/specs/282-stable-wall-segment-identity.md (spec-review зелёный на r2, 2f30c481)
  • Этап: код-ревью (PROCESS.md §2.7)
  • Заход: r4 · блокирующих циклов израсходовано 3 из 4 до этого вердикта
  • Предыдущий раунд: docs/reviews/CODE-REVIEW-282-r3.md, красный, HEAD на тот момент 95fa23ca79db830b201e82d9a34a05dcc3cecb29
  • Вердикт: зелёный

1. Скоуп проверки

Разбор по дельте (PROCESS.md §2.10): финальный ребейз на dev уже был сделан до r3 (без конфликта), между r3 и этим раундом ветка на dev не перебазировалась повторно — git merge-base --is-ancestor origin/dev HEAD истинно, дополнительных коммитов слияния нет.

Дельта 95fa23ca..HEAD — 2 коммита:

fb6cc3ca  docs: review document for #282   (публикация CODE-REVIEW-282-r3.md, docs-only, не код)
f37efdad  fix: keep wall-hosted passages open in static view

Файлы правки (без учёта review-документа, класс C):

src/space-render.ts                                        (1 строка)
demo/smoke_open_passage.mjs                                (усилена одна ассерция)
custom_components/houseplan/frontend/houseplan-card.js     (сгенерировано)
dist/houseplan-card.js                                     (сгенерировано)
docs/CHANGELOG.md, docs/CHANGELOG.ru.md                     (бюллетень)
docs/images/screenshots.json                                (sourceFingerprint)

Дельта предметно локальна и совпадает по границам с единственной находкой r3 (H1): изменена ровно та строка, которую r3 указал как источник дефекта, тем же паттерном, что уже применён двумя строками выше в том же файле. Новых продуктовых путей, новой подсистемы или смены контракта дельта не вносит — полный повторный разбор подсистемы не требуется, разбор ограничен этой строкой и её прямыми следствиями, как и предполагал r3 в своём резюме (§9 r3).

Отдельно проверено предостережение самого r3 («grep не ловит эквивалентную логику, выраженную иначе»): grep -n "\.host\b" src/*.ts по всему src/ показывает, что все прочие потребители opening.host/o.host уже различают kind === 'wall' от kind === 'partition' (plan-geometry-preflight.ts:207,259, houseplan-card.ts:9204,15471, room-deletion.ts:88, wall-segment-model.ts, space-render.ts:221,279 и т. д.) — space-render.ts:437 был единственным местом с устаревшим одноусловным !opening.host, и дельта его закрывает. Других эквивалентных, но иначе выраженных фильтров не найдено.

2. Как проверялось — таблица гейтов

Гейт Команда Результат
Typecheck npx tsc --noEmit зелёный
Unit/frontend npm test 1335 тестов, 1334 passed, 0 failed, 1 skipped
Build + sync бандла npm run build && npm run bundle:sync зелёный; cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js и cmp … demo/srv/assets/houseplan-card.js — побайтово идентичны; git status после сборки пуст
Docs fingerprint node scripts/check-docs.mjs зелёный (7 файлов, 10 внешних ссылок) — обязателен, дельта трогает src/**; sourceFingerprint в коммите пересчитан верно (совпал с visualFingerprint(ROOT) на HEAD)
Смок-подбор по дельте node scripts/smoke-select.mjs --base 95fa23ca --head HEAD НЕОПРЕДЕЛЁННОСТЬ — изменённая строка не даёт инструменту символа для сопоставления (0 символов на изменённых строках); решение по существу ниже
Целевой AC7-смок (прямое совпадение по существу находки r3, не по инструменту) node demo/smoke_open_passage.mjs OK, все 13 проверок, включая усиленную геометрическую staticCutsAndFillsPassage
Дисциплина «тест умеет падать» сборка изолированного git worktree на пред-фикс кандидате fb6cc3ca (HEAD r3, до строки-фикса) с той же усиленной версией смока staticCutsAndFillsPassage: false — тест детерминированно падает на дефектном коде и проходит на HEAD
Model invariants не перепрогонял отдельно дельта не трогает рёбра/layout/marker.space/open_spans — только фильтр-предикат рендера Static-карточки, геометрия модели не меняется; не требуется в этом раунде
golden:verify не прогонял локально поверхность правки — houseplan-space-card (renderSpaceStatic), который не входит в demo/golden/matrix.mjs вовсе (там только режимы plan/view/devices/decor основной карточки; проверено grep -n "mode:" demo/golden/matrix.mjs) — гейт неприменим к этой поверхности. CI golden job на точном SHA тем не менее зелёный (см. ниже)
pytest tests_backend не прогонялся backend не в дельте (все файлы — frontend/demo/docs)
Mutation gate не перепрогонялся дельта не трогает ни один из именованных wall-identity-*-мутантов (файлы вне дельты)
CI на точном SHA gh api .../commits/f37efdad.../check-runs, run 32912755206 Validate: completed/success; все активные джобы (frontend, smoke×3, golden, performance_smoke, docs, process-gate, provenance) — success; backend/hacs/hassfest — skipped (путь-фильтр changes, backend/manifest не в дельте — ожидаемо)

Дешёвые гейты и целевой AC7-смок — зелёные; тест по существу находки r3 проверен на фальсифицируемость лично мной (не только принят на слово автора).

3. High / Medium — новые находки

Нет. Единственная блокирующая находка r3 (H1) закрыта по строке кода и подтверждена исполнением (см. §5).

4. Low

Нет новых.

5. Закрытие раунда r3

Находка r3 Чем закрыта Где это видно
H1 — src/space-render.ts:437: Static-карточка не вырезает физический разрыв кладки под wall-hosted passage после миграции в v8 (wallBodiesUnionPath фильтровал по устаревшему !opening.host, который перестал быть верным предикатом с самого Stage 1) Строка 437 приведена к тому же паттерну, что уже применён на строках 221/279 того же файла: staticPassages.filter((opening) => opening.host?.kind !== 'partition') — теперь и «нет host», и host.kind==='wall' попадают в список вырезов кладки, исключён только host.kind==='partition' (материализуется отдельно через hostedCompositeOpenings) src/space-render.ts:437 (коммит f37efdad); demo/smoke_open_passage.mjs усилен геометрической проверкой (path.isPointInFill по точке в центре прохода и по точке в сплошном участке стены) вместо проверки одного лишь присутствия DOM-узла — именно та проверка, отсутствие которой r3 назвал причиной, по которой дефект прежде проходил незамеченным. Проверено лично: на pre-fix HEAD (fb6cc3ca) с той же усиленной ассерцией смок падает (staticCutsAndFillsPassage: false), на f37efdad — проходит

6. Унаследовано из r3

Дельта 95fa23ca..HEAD не касается — принято на основании выводов docs/reviews/CODE-REVIEW-282-r3.md (HEAD на котором получен вывод — 95fa23ca79db830b201e82d9a34a05dcc3cecb29), которая сама наследовала большую часть от r2/r1 (см. §6 документа r3):

  • Migration determinism/idempotence, backend-зеркало валидации (unique-id, owner-count, dangling refs, edge-geometry parity, _wall_catalog_projection), import/export remap, i18n-ключи, документация подсистемы (WALL-THICKNESS.md, CANVAS.md, USER-GUIDE.{ru,}.md, TESTING.md), config-field-registry.mjs, три wall-identity-* мутанта mutation-gate, lineage hints, off-grid guard — вне дельты r3→r4, наследуется без переразбора.
  • Guard-порядок #278, 9 именованных structural-smoke, sun-rays после структурного редактирования, additionalAuthoredPoints, явный ноль толщины не воскресает, wall_model_client_outdated на реалистичном stale-client сценарии, AC7 для символа/материализации проходов (resolvedHosted/resolvedRawOpenings) — файлы вне дельты r3→r4, наследуются из r3 §6/§7, которая сама перепроверила их исполнением на своём HEAD.
  • Model invariants на реальной нагрузке (large-house, 0% шума у узла до и после commitWallSegmentModel) — вне дельты, наследуется из r3 §7.
  • CI-покрытие golden/performance/backend/hacs/hassfest на предыдущем HEAD — наследуется из r3 §2/§7 и дополнительно переподтверждено собственным прогоном CI на новом точном SHA (см. §2 этого документа) — не голое наследование, а повторная проверка.

7. Что проверено и корректно (в этом раунде)

  • Фикс не вводит двойного учёта: hostedCompositeOpenings (составные partition-стены) по-прежнему строится из resolvedHosted, отфильтрованного по kind === 'partition' (строка 221, не тронута) — пересечения множеств «wall-hosted passages» и «partition-hosted composite openings» нет.
  • Тип OpeningHost = PartitionOpeningHost | WallOpeningHost (src/types.ts:198) — ровно два варианта kind, поэтому !== 'partition' исчерпывающе и типобезопасно покрывает 'wall' и undefined (нет host).
  • Трейлеры: оба коммита дельты несут Issue: #282; f37efdad — User-Visible: yes с правками в docs/CHANGELOG.md и docs/CHANGELOG.ru.md в том же коммите (проверено git show --stat f37efdad).
  • «Одно число — один источник» — не применимо к этой дельте: правка не добавляет и не меняет видимую пользователю величину (число/подпись/площадь), только предикат включения объекта в список геометрических вырезов.
  • docs/images/screenshots.json: обновлён только sourceFingerprint/sourceSha256 (пересчитан правильно, check-docs зелёный); imageSha256 не менялся — реальные PNG не изменены (сценарии doc-скриншотов не задевают Static-карточку с wall-hosted проходом), лишних правок нет.
  • CI на точном SHA кандидата подтверждает то же самое независимо от локального прогона: golden и performance_smoke зелёные, несмотря на то что локальный golden:verify не запускался (см. §2) — не молчаливый пропуск, а гейт, пройденный другим путём.

8. Чего не проверял и почему

  • golden:verify локально — поверхность правки (houseplan-space-card static-режим) не входит в demo/golden/matrix.mjs; CI golden job на точном SHA зелёный, это покрывает то немногое, что матрица вообще проверяет по соседству.
  • pytest tests_backend, mutation-gate, model invariants — вне дельты r3→r4 (см. §1), не требуются по методологии §2.10.
  • Полный performance_smoke локально — предрелизный гейт; CI на точном SHA зелёный.
  • Полная матрица demo/smoke_*.mjs (190 файлов) — инструмент подбора не нашёл ни прямых, ни слабых связей (изменённая строка без узнаваемого имени символа); решение по существу — прогнан только AC7-смок, прямо соответствующий содержанию находки r3, плюс он же на pre-fix коде для проверки фальсифицируемости. Остальные 189 смоков дельты не касаются: фикс меняет один булев предикат в одной функции одного файла (renderSpaceStatic), не тронутой ни одним другим смоком по имени или маршруту.

9. Резюме

Единственная блокирующая находка r3 закрыта ровно тем фиксом, который сам документ r3 предсказал («фикс структурно локален... не должен требовать полного повторного разбора подсистемы в r4»), и дополнительно проверено его собственное предостережение о скрытых эквивалентных фильтрах — таких не найдено. Тест, доказывающий AC7 для этого код-пути, усилен с DOM-присутствия до геометрической проверки формы пути и лично проверен на фальсифицируемость. Дешёвые гейты, целевой AC7-смок и CI на точном SHA — зелёные. Новых находок нет.

Вердикт: зелёный.