Files
houseplan-card/docs/reviews/CODE-REVIEW-528-r1.md
T
2026-09-11 07:09:35 +00:00

9.4 KiB
Raw Blame History

CODE-REVIEW — issue #528, заход r1

SHA материала: f37ccdcebc3f454cb03c8cd25f5081a45eb4e22f (рабочая копия уже на нём) Ветка: issue/528-marker-identity-witness Класс изменения: B (demo/smoke_space_switch_transitions.mjs) — issue переиспользован, продуктовый код и реестр мутантов не тронуты (совпадает с заявленным «Ограниченный объём»).

Скоуп

Свидетель #525 (demo/smoke_space_switch_transitions.mjs) ловил мутант device-markers-rendered-without-keys через побочный признак: отсутствие бегущего перехода box-shadow на .device-shell-frame после переключения пространства. #524 убрал этот переход из стилей (перф-регрессия Firefox), признак стал тривиально истинным, и позиционное переиспользование DOM-узлов маркеров перестало ловиться — свидетеля ослепили не намеренно, а как побочный эффект соседней задачи.

Задача #528 узкая: маркерная половина свидетеля должна судить по идентичности узла (data-id привязанного к узлу до/после переключения), а не по анимации. Дверная половина (op-leaf/op-arc) остаётся как есть — там переход часть продуктового контракта (#525) и её не просили трогать.

Диф (весь, 16 строк)

Один файл, git diff origin/dev...HEAD --stat:

demo/smoke_space_switch_transitions.mjs | 17 ++++++++++++++++-

Добавлено:

  1. Перед первым переключением (c._pickSpace(ids[0])) собираются ссылки на все узлы [data-hp="device"] вместе с их data-id (markersBefore).
  2. markersDrawnInTheFirstSpace = markersBefore.length >= 3 — страховка от вырожденного сравнения на пустом множестве (сама тоже проверяется checkAll, дефолт true).
  3. После второго _pickSpace вычисляется reused — узлы из markersBefore, которые остались подключены (isConnected) и сменили data-id; noMarkerNodeReusedForAnotherDevice и диагностический список reusedMarkerNodes (ожидаемо пустой, checkAll(res, { ..., reusedMarkerNodes: [] })).
  4. Старая проверка noMarkerTransitionOnSwitch (по переходу оболочки) не удалена — она просто перестала быть единственной, что безопасно (AC не требовал её убирать).

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

Дешёвые гейты этого SHA уже зелёные (Validate 34572622663, success): typecheck/test/build не перегонял — diff не трогает src/**, test/** юнитов, конфиги сборки. node scripts/check-docs.mjs также не требуется: diff не задевает src/**, отпечаток скриншотов не устаревает.

node scripts/smoke-select.mjs --base 78bc76073ea2...(merge-base) --head HEAD подтвердил ожидаемое: «Исполняемого frontend-диффа нет (src/**/*.ts не тронут)», browser-smoke по этому инструменту не выбираются — выбирать нечего, изменённый файл сам и есть свидетель.

Главная проверка — не общий гейт, а прямое доказательство по AC, и оно уже есть на этом самом SHA в логах Validate, job «Мутанты по диффу (1/6)» (id 103177915852):

07:05:52 ok   чистый прогон: node demo/smoke_space_switch_transitions.mjs
07:06:16 ok   device-markers-rendered-without-keys: тест покраснел, как обязан

Это ровно то, что требуют AC2 и AC3: чистый (немутированный) прогон смока целиком зелёный, и на замутированном коде (repeat(devs, (d) => d.id, ...) → devs.map(...) в src/houseplan-card.ts:11787, реестр scripts/mutation-gate.mjs:7325) свидетель краснеет. Не заявление автора — факт из лога CI на материале ревью.

Дополнительно прочтением (без исполнения):

  • demo/serve.mjs:68-78 (check/checkAll) — сравнение через JSON.stringify, так что reusedMarkerNodes: [] в checkAll действительно требует пустой массив, а не просто «поле определено».
  • src/space-render.ts:607 — реальная разметка маркера несёт data-hp="device" data-id="${d.id}", соответствует селектору смока.
  • src/styles/devices.styles.ts:200-215 — .device-shell-frame сейчас имеет только transition: border-color .15s, opacity .2s (без box-shadow), подтверждает описанную в issue причину: старый переход правда убран #524, и старая проверка правда стала тривиальной.
  • Логика reused (строки 124-129): узел считается «переиспользованным чужим id», только если он ещё подключён к дереву (isConnected) и его data-id изменился относительно зафиксированного до переключения — именно позиционное переиспользование, а не обычное удаление узла или сохранение того же устройства между пространствами (когда data-id не меняется). Разобрано корректно.

Проверено и корректно

  • AC1 — реализовано: сравнение по ссылкам на узлы, а не по CSS-переходу; подтверждена и защита от вырожденного случая (markersDrawnInTheFirstSpace).
  • AC2 — доказано логом CI на этом SHA (мутант красный), не голым заявлением.
  • AC3 — доказано тем же логом («чистый прогон» = OK целиком); дверная половина (op-leaf/op-arc, noOpeningTransitionOnSwitch, theTwoDoorsDifferInState и т. д.) не тронута, дифф это подтверждает.
  • Трейлеры: Issue: #528, User-Visible: no — верно, диф не меняет видимое поведение продукта, changelog не требуется.
  • Скоуп: ни продуктовый код, ни scripts/mutation-gate.mjs не изменены — соответствует заявленному в issue ограничению.

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

  • npx tsc --noEmit, npm test, npm run build, сверку бандла — не требуется: diff не трогает src/**/test/**/конфиги сборки, а Validate на этом же SHA уже зелёный.
  • node scripts/check-docs.mjs — diff не трогает src/**.
  • npm run invariants — diff не трогает геометрию/toplogy/marker.space.
  • npm run golden:verify — diff не может изменить видимый рендер (только тестовый файл).
  • python -m pytest tests_backend — Python не тронут.
  • Полный прогон demo/smoke_*.mjs вручную — не требуется отдельно: ровно тот смок, который меняется, уже прогнан CI на материале ревью дважды (чистый и замутированный), что сильнее локального прогона без мутанта.

Вердикт

Зелёный. AC1–AC3 выполнены и подтверждены логом CI на самом материале ревью, а не заявлением автора; изменение в границах заявленного скоупа, трейлеры верны.


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

  • Ветка: issue/528-marker-identity-witness, коммит f37ccdcebc3f — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 3a16a24c59b27eac3280c63ce77f48ca13875750
    git log --all --format='%H %T' | grep 3a16a24c59b2
    
  • Тело issue: f178b89645d3d0967d356c110b81f4262fd676aa9b7bf30203cb8ffb9b63190b
  • Вердикт конвейера: green · High 0