18 KiB
CODE-REVIEW — issue #724, заход r1
Материал раунда: 877dccd8430aad63e4ebe8c93dd10febfbdae25e
(git log --oneline origin/dev..HEAD — один коммит; git diff origin/dev...HEAD --stat — 9 файлов,
+193/-182). Трек show. Validate на этом SHA: success
(https://github.com/Matysh/houseplan-card/actions/runs/36784536500).
Скоуп
Продолжение #714: убрать данные оверлейной 2.5D-сцены, которые ничего не решают —
IsoWallSilhouette/isoWallSilhouettesOf (служили только ключом кэша), поля
tether/grounding/raisedScene/owner.area и входы
hovered/focused/selected/filtersSupported в IsoOverlayPlacement*
(игнорировались резолвером), флаг resolveCollisions и парный слот кэша
fit/live. Кэши размещений и рендер-сцены переключены на ключ
structure: IsoWallGeometry (scene.geometry из resolveIsoScene) вместо
искусственно строившегося массива силуэтов. Видимое поведение не меняется
(User-Visible: no, тело issue: «Что видит человек: ничего»).
Соответствие docs/SCOPE.md: техдолг внутри уже принятой подсистемы 2.5D
(Core user jobs это не расширяет и не сужает — правка корректно вне скоупа
трека spec, только show/code).
Как проверялось
Дельта нулевая (r1, предыдущего раунда нет) — разбор полный.
- Чтение диффа построчно по всем 9 файлам (
git diff origin/dev...HEADдля каждого файла отдельно):iso-overlays.ts,iso-scene-render.ts,houseplan-card.ts,scripts/mutation-registry.mjs,docs/ISOMETRIC.md, 4 тестовых файла. - Проверены все использования удалённых символов по всему дереву
(
wallSilhouettes|raisedScene|isoWallSilhouettesOf|resolveCollisions| IsoWallSilhouette|.tether|.grounding|owner.area|filtersSupported) — единственные оставшиеся совпадения (filtersSupportedвiso-openings.ts) относятся к другому, не удалённому контракту (capability-проба фильтров материалов), не кIsoOverlayPlacementInput. - Сверены find-строки всех трёх патчей
mutation-registry.mjs(W2, W6, и косвенно W5) с реальным текстомsrc/iso-scene-render.ts/src/houseplan-card.ts— совпадают побайтово (grep -nна точные строки). - Прогнаны сами мутанты (не просто
--check, а исполнение патча и гварда):node scripts/mutation-gate.mjs --id=iso-placement-cache-survives-silhouette-change→поймано 1 из 1(гвард — новый#473 W2|#724 AC2— реально краснеет на мутанте, дисциплина «тест умеет падать» подтверждена не чтением, а исполнением);--id=stage3-w6-no-borders-keeps-raised-plates→поймано 1 из 1;--id=stage3-w5-runtime-nudge-writes-storage→поймано 1 из 1(описание больше не говорит про «nudge», но гвард по-прежнему реагирует на постороннийlocalStorage.setItem).node scripts/mutation-gate.mjs --check→ 3 предупреждения реестра — то же число, что автор указал как совпадающее с dev.
- Типы:
tsc --noEmitпрогнан как частьnpm run build(см. «Гейты»). - Юнит-тесты новых AC2-кейсов прочитаны построчно: они идут по
продуктовому пути
createIsoStructuralSource → resolveIsoScene → buildIsoOverlayRenderScene, а не через синтетическийstructureOf()— ключ кэша берётся из настоящей структурной LRU, а не собирается вручную, так что тест доказывает именно продовое поведение, а не артефакт фикстуры. - Golden 2.5D и полный набор (
npm run golden:verify) — см. «Гейты».
Находки
Находок нет.
Разобрано отдельно и не являются находками:
- Тождественность семантики
raisedScene→visualScene: до правкиraisedSceneвсегда равнялсяvisualSceneв обеих ветках (floor/raised) — замена мест чтения наvisualScene(вlayoutSignature, вisometric-contract.test.mjs, вiso-overlays.test.mjs) не меняет значение, только имя поля. Подтверждено чтением обоих диффовiso-overlays.ts/iso-scene-render.ts. - Идентичность объекта-ключа: раньше
wallSilhouettesбыл новым массивом при каждом промахе кэшаresolveIsoScene(та же исходная семантика, что описана вdocs/ISOMETRIC.mdдо правки — «структурная LRU выдаёт тот же объект» только при попадании в кэш). Теперь роль идентичности играетgeometry(тоже новый объект на каждый промах структурного кэша,buildIsoWallGeometry(...)вызывается заново). Поведение инвалидации кэша оверлеев (переживает zoom/resize/HA-состояние, пересобирается при правке геометрии) не изменилось — подтверждено и чтением, и двумя новыми тестами AC2, прогнанными зелёными, и третьим прогоном через явную мутацию ключа (см. п. 4 выше). ISO_PLACEMENT_CACHE_ANY = {} as IsoWallGeometryв мутанте W2 — фиктивный объект только для константного ключа WeakMap, не участвует в рантайме продукта, используется исключительно как патч-мутант.
Проверено и корректно
- AC1. Все перечисленные в ТЗ мёртвые поля/входы/типы удалены, ни одного
оставшегося упоминания в
src/**,demo/**,test/**,scripts/**.tsc --noEmit(черезnpm run build) проходит. 21 изометрическая golden-сцена и полный набор golden —passed, без новых кадров (эталоны не тронуты в диффе:git diff --statне содержитdemo/golden/**). Юнит-тесты — 3314 passed / 0 failed / 1 skipped (тот же skip, что и на dev — не новый). Четыре явно названных в ТЗ смока (smoke_iso_flat_parity,smoke_isometric_contract,smoke_isometric_live_touch,smoke_iso_tiles) прогнаны лично (послеnpm run bundle:sync, иначе харнесс бьётся о устаревшийdemo/srvбандл) — все четыре зелёные. - AC2 (кэш). Два новых теста в
test/iso-scene-render.test.mjs(#724 AC2: the overlay scene survives zoom, resize and HA state...и#724 AC2: a room edit that changes the owner is a new structure...) идут по продуктовому пути и явно проверяют: (а) одинаковые стены → тот жеgeometry-объект →zoom/resize/HA-апдейт не пересчитывают размещение (assert.strictEqual); (б) более толстая стена той же комнаты → новый ключ структурной LRU → новыйgeometry→ новая сцена оверлеев, но позиция тайла не меняется (стены не двигают значок); (в) правка комнаты, меняющая владельца точки, даёт новую структуру и нового владельца (не «зависшего» от снесённого плана). Мутационная проверка (п.4) подтверждает, что при подмене реального ключа на константу тест красный. - AC3 (реестр мутантов). W2 переключён на реальный ключ
input.structure(id сохранён для истории, это осознанно и не создаёт путаницы — гвард проверяет актуальный код), гвард дополнительно гоняет оба новых AC2-теста. W6 патч переведён на новую строкуstructure: structural.geometry. W5 описание больше не упоминает «nudge» (которого с #713 нет), гвард не менялся (smoke), проверен мутацией — реагирует. Регуляркаisometric-contract.test.mjsбольше не ищет силуэтную конструкцию, а проверяет актуальный ключ и явно (assert.doesNotMatch) убеждается, чтоwallSilhouettes|isoWallSilhouettesOf|resolveCollisionsне вернулись.mutation-gate --check— 3 предупреждения, совпадает с заявленным (и это число не новое: структурные предупреждения реестра относятся к посторонним мутантам, не к правкам этого диффа). - docs/ISOMETRIC.md. Добавленный абзац («placement и render-scene caches
are keyed by the wall geometry… #724») точно описывает код: ключ —
scene.geometry, инвалидация — на любую структурную правку (стены, комнаты, проёмы — все входят в фингерпринт структурной LRU, см. неизменную часть документа выше по файлу). Никакая другая пользовательская величина в документе не задваивается (правка внутренняя, §8 не применим — нет числа, видимого пользователю). - Трейлеры.
Issue: #724,User-Visible: no— присутствуют, соответствуют телу issue («видит человек: ничего»). Правок changelog нет ни в одном файле — корректно дляUser-Visible: no. - Ветка/dev. Материал — ветка как есть (трек
show, #696), ребейз не требовался в этом раунде.
Гейты — что прогнано и почему
Дешёвые гейты на 877dccd8 уже подтверждены Validate (tsc --noEmit,
npm test, npm run build со сверкой бандла) — по правилам раунда их не
обязательно перегонять, но для проверки AC1/AC2 (кэш, тип IsoWallGeometry
как ключ WeakMap) и мутационного реестра (AC3) потребовалось фактическое
исполнение, поэтому прогнано:
npm run build(включаетtsc --noEmit) — зелёный.npm test— зелёный, 3314 passed / 0 failed / 1 skipped (тот же skip, что и на dev).npm run bundle:sync— нужен был для смоков (демо-харнесс подхватываетdemo/srv/assets, неdist/напрямую; без синка смоки падали сFailed to fetch dynamically imported module— это не находка, а артефакт локального состояния, не тронутого диффом).- 4 браузерных смока, явно названных в ТЗ (
smoke_iso_flat_parity,smoke_isometric_contract,smoke_isometric_live_touch,smoke_iso_tiles) — все зелёные. node scripts/smoke-select.mjs --base origin/dev --head HEAD— ответ НЕОПРЕДЕЛЁННОСТЬ (ни один смок не связан доказуемо с изменёнными символамиIsoWallGeometry,isoOverlayPlacementCacheи т. д.) плюс список слабых связей (35 смоков через общий_config). Так как ТЗ явно называет 4 смока (закрыты выше), а остальные связи инструмент считает слабыми (решение ревьюера, не обязанность) — дополнительные смоки из списка слабых связей не прогонялись: изменение не трогает ни один из путей, которые они бы покрывали (диалоги, локали, kiosk и т. п.), а прямая логика 2.5D покрыта названными 4 смоками + golden + юнитами.npm run golden:verify— зелёный, полный набор, включая все 21 изометрические сцены (isometric-*), без единогоfail. Формальноci:goldenметки на этом PR не было, но правка трогает рендер-путь (iso-scene-render.ts), поэтому прогнано по инструкции «golden, если diff трогает рендер», а не по метке.node scripts/mutation-gate.mjs --check— зелёный (3 предупреждения, как на dev).- Точечное исполнение трёх изменённых мутантов
(
--id=iso-placement-cache-survives-silhouette-change,--id=stage3-w6-no-borders-keeps-raised-plates,--id=stage3-w5-runtime-nudge-writes-storage) — все поймали мутацию (поймано 1 из 1в каждом случае).
Что не проверял:
python -m pytest tests_backend— не запускал: диффа вcustom_components/**/*.pyнет.npm run invariants— не запускал: диффа в геометрии модели (координаты, wall_segments, room poly) нет, правка — кэш-ключи и мёртвые поля presentation-слоя, не модель.- Performance-профили (
benchmark:*) — не запускал: не названы в AC, профиль кэша не меняет алгоритмическую сложность (тот же паттерн WeakMap-по-идентичности-объекта, просто другой объект-идентификатор). - Полный
npm run gate:small(автор его уже прогнал и привёл результат «зелёный», а дешёвые компоненты — tsc/test/build — перепроверены выше напрямую) — не гонял отдельно, поскольку это предрелизный/полный набор, избыточный для объёма этой задачи (PROCESS.md §8); прогонял именно то, что этот диф требует доказать. - Слабые/широкие смоки из списка
smoke-select(35 шт. по общему_config, и «широкий» порог 56) — не прогонял: связь слабая и признана ревьюером недостаточной, диффа за пределамиiso-*файлов нет. - Визуальный минимум (8 смоков, упомянутый выводом smoke-select) — не прогонял: он актуален для диффов, трогающих общий визуальный рендер-конвейер плоского вида; здесь диф ограничен 2.5D-веткой, покрытой явно названными смоками и golden напрямую.
Побочные наблюдения (не находки)
Во время прогона npm run build/bundle:sync в рабочей копии возникли
недетерминированные хеши файлов dist/houseplan-assets/* (новые имена
чанков при том же содержимом) — это артефакт локальной пересборки ревьюера,
не диффа PR; откачено (git checkout -- dist/ + удаление новых untracked
файлов) перед завершением ревью, рабочая копия чистая.
Вердикт
Все AC доказаны: автотестом (AC2 — двумя новыми тестами, проверенными исполнением мутации на красноту), реестром мутантов, исполненным напрямую (AC3), и прямым исполнением golden/смоков/юнитов/build (AC1). High/Medium находок нет. Зелёный.
Материал раунда
- Ветка:
issue/724-iso-overlay-dead-data, коммит877dccd8430a— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
d3fee2ae704d2a89b3317e722e04272ef5d71453git log --all --format='%H %T' | grep d3fee2ae704d - Тело issue:
b6202e7a98a172c6dc44df0980c516b466317988c8375cb5fad9e728bb4fa820 - Вердикт конвейера:
green· High 0