21 KiB
CODE-REVIEW-745-r2
Issue: #745 · Трек: show · Заход: r2 · блокирующих циклов 0/2
Материал: git log --oneline origin/dev..HEAD / git diff origin/dev...HEAD
SHA материала: 7be2d456003f83148be113e37288ee915a075bb6 (сверено git rev-parse HEAD перед итогом — совпадает)
Причина нового захода — не находка r1 (там их не было), а ребейз: во
время r1 dev продвинулся на 4 коммита, патч потерял совпадение
patch-id с рецензированным (ca708d84 → 6cd29f9b, то же дерево
7be2d456 для коммитов публикации — комментарий к issue зафиксировал
это явно: «Материал ревью ca708d84 и кандидат 7be2d456 дают разные
patch-id»). Это «ребейз на ушедший вперёд dev» из правил — разбор ниже
полный, а не по дельте.
Скоуп диффа
demo/smoke_space_card.mjs | 153 +++++++++++++++++++-
docs/CHANGELOG.md | 3 +
docs/CHANGELOG.ru.md | 3 +
docs/reviews/CODE-REVIEW-745-r1.md | 193 ++++++++++++++++++++++++++
docs/reviews/INDEX.md | 3 +-
docs/testing-notes/mutation-browser-guards.md | 1 +
scripts/mutation-registry.mjs | 13 ++
src/space-render.ts | 12 +-
src/styles/plan.styles.ts | 9 +-
9 files changed, 382 insertions(+), 8 deletions(-)
Из девяти файлов продуктовая правка — ровно по ТЗ, одна поверхность:
src/space-render.ts (534→540 вставляет keyed(space.id, repeat(shownRooms, (r, index) => r.id || index, (r) => {…})) вместо
голого map(), импорты keyed/repeat добавлены строками 10-11).
plan.styles.ts — только комментарий (строка 503+, дополняет заметку
#742 словами про карточку пространства), сам переход .room { transition: 0.12s } не тронут. scripts/mutation-registry.mjs и
docs/testing-notes/mutation-browser-guards.md — один новый мутант
space-card-rooms-rendered-without-keys и его регистрация. CHANGELOG
RU/EN — по одной строке, дословно текст из ТЗ. Остальные два файла
(docs/reviews/CODE-REVIEW-745-r1.md, docs/reviews/INDEX.md) —
публикация документа прошлого раунда, не продуктовый код; я их не
редактирую и не оцениваю как находки, только как контекст прошлого
раунда.
Я построчно сверил текущий git diff origin/dev...HEAD с тем, что
описывает CODE-REVIEW-745-r1.md: по src/space-render.ts,
src/styles/plan.styles.ts, scripts/mutation-registry.mjs,
docs/CHANGELOG*.md дифф посимвольно совпадает с тем, что разбирал
r1 (один символ различия — несущественная цифра в собственной таблице
r1 по mutation-browser-guards.md, 5+- вместо фактических 1+, к
нынешнему материалу не относится). Отличие ровно там, где его и
следовало ждать после ребейза: контекст диффа пересчитан относительно
нового dev, browser guards: 205/200 вместо 204/200 — потому что
dev между раундами получил ещё один гард (#759), не потому что эта
ветка что-то добавила сверх.
Как проверялось
Рабочая копия уже стояла на материале (7be2d456). Validate зелёный
на этом же SHA
(run 36860677601)
подтверждает дешёвые гейты — не перегонял tsc --noEmit/npm test/npm run build по этой причине. Но ребейз — не локальная
дельта, поэтому браузерные гейты (смоки, мутант, smoke-select,
gate:small) прогнал заново сам, не полагаясь на цифры r1.
| Гейт | Результат | Примечание |
|---|---|---|
npx tsc --noEmit, npm run build |
не перегонял отдельно | Validate зелёный на 7be2d456; npm run bundle:sync ниже всё равно гоняет build как часть сборки бандла для смоков — тоже чисто |
npm run bundle:sync |
✅ | нужно для честного запуска браузерных смоков (demo/srv/assets не обновляется автоматически) |
node demo/smoke_space_card.mjs (раздел #745, AC1–AC2) |
✅ зелёный | прогнан лично; все 9 полей keyedRooms совпали с ожиданием дословно (см. ниже) |
node scripts/mutation-gate.mjs --id=space-card-rooms-rendered-without-keys |
✅ «поймано 1 из 1» | независимое исполнение: гейт сам прогнал чистый смок (ok), применил патч мутанта во временном дереве, пересобрал и прогнал смок снова — покраснел по заявленной причине |
node scripts/mutation-gate.mjs --check --changed=origin/dev..HEAD |
✅ exit 0, browser guards: 205/200 (WARN, не fail, #699) |
рост с 204 (r1) до 205 — новый гард дал dev между раундами (#759), не эта ветка; missingReasons пуст |
node scripts/smoke-select.mjs --base origin/dev --head HEAD |
выполнен | то же решение по строкам, что в r1 (см. ниже) |
npm run gate:small |
✅ зелёный, 113 с, упало 0 | сборка+typecheck, no-new-any, no-new-private-writes, smoke-select, npm test, bundle-policy --verify, bundle:budget, lint:unused — все шаги ok |
demo/smoke_space_card_bg.mjs (AC3) |
✅ | |
demo/smoke_space_card_decor_capability.mjs (AC3) |
✅ | |
demo/smoke_moon_static.mjs (AC3) |
✅ | |
demo/smoke_room_settings.mjs (прямое совпадение smoke-select) |
✅ | |
demo/smoke_space_switch_transitions.mjs (companion #742, делит plan.styles.ts) |
✅ | подтверждает, что правка не расшатала прецедент #742 |
npm run golden:verify |
не прогонялся | нет метки ci:golden на issue (сверено gh issue view 745 --json labels); дефект транзитный (виден только в первом кадре перехода) — keyed/repeat не добавляют обёрточных DOM-узлов и не меняют порядок фигур относительно прежнего map() (проверено чтением: roomShapes используется один раз, svg того же вида для polygon/rect, data-hp/data-id/классы не изменились) — кадр в покое не меняется |
python -m pytest tests_backend |
не прогонялся | дифф не касается custom_components/**/*.py |
npm run invariants |
не прогонялся | дифф не меняет геометрию комнат или ссылки на неё — только идентичность DOM-узлов списка (ключи keyed/repeat), координаты/формы r.poly/r.x/y/w/h не тронуты |
npm run bundle:clean |
✅ | рабочая копия приведена к закоммиченному виду после гейтов, git status --short пуст |
smoke-select — решение по строкам (повторная проверка)
Изменено файлов src/**: 2 · символов проекта на изменённых строках: 2
Матрица: 286 смоков · порог «широкого» символа: больше 57 смоков
Прямое совпадение (1): demo/smoke_room_settings.mjs ← roomFillModeOf
Не учитывались как широкие: setConfig
То же решение, что в r1, и я его не унаследовал слепо — пересчитал: со
смещением базы на новый dev матрица и совпадение не изменились.
setConfig снова не расширяю — совпадение идёт из тестового файла
(demo/smoke_space_card.mjs сам вызывает el.setConfig(...)
многократно), а не из продуктового кода диффа.
Доказательство негатива (свидетель умеет падать)
Прогнал mutation-gate --id=space-card-rooms-rendered-without-keys
сам, не доверяя отчёту прошлого раунда: гейт (1) прогнал чистый смок
на правке — ok; (2) применил патч мутанта (внутренний repeat
заменён на голый .map(), внешний keyed остался) во временном
дереве, пересобрал бандл и прогнал тот же смок — красный по
заявленной причине. Итог инструмента: «поймано 1 из 1». Это
независимое исполнение на нынешнем SHA, а не повтор чужого вывода.
AC — разбор
| AC | Вердикт | Как доказано |
|---|---|---|
AC1 (путь A — смена space в setConfig того же элемента) |
✅ выполнен | Раздел смока, прогнан лично: фикстура держит ожидаемую форму (pathAFixtureHolds: true), после setConfig({ space: 'garden' }) и одного кадра нет CSSTransition на [data-hp="room"] (pathANoRoomTransition: []), ни один старый узел не остался подключён под чужим data-id (pathANoRoomNodeOutlivesTheSwitch: []), новый узел g1 рождается сразу с итоговым rgb(96, 125, 139) / 0.18 (pathANewRoomBornInItsFill, сверено с DEFAULT_CUSTOM_FILL в src/logic.ts:1301 — не тронут диффом, источник один) |
| AC2 (путь B — событие конфигурации меняет состав комнат; реальный переход не сломан) | ✅ выполнен, красит мутант space-card-rooms-rendered-without-keys (поймано лично, см. выше) |
Вставка комнаты первой в f1.rooms через __hpTest.setServerConfig: список вырос (pathBRoomListGrew: true), ни один из прежних узлов не подменён (pathBNoRoomNodeSwapped: []), переходов не было (pathBQuietRooms: []). Настоящая смена custom_fill после этого по-прежнему идёт на том же узле (pathBRealFillChangeKeepsTheRoomNode: true) и анимируется (pathBRealFillChangeAnimates: true) — контроль против ложного фикса transition: none, подтверждён отдельно прогоном smoke_space_switch_transitions.mjs |
| AC3 (ничего не сломано, выпуск) | ✅ выполнен | gate:small зелёный лично; smoke_space_card, smoke_space_card_bg, smoke_space_card_decor_capability, smoke_moon_static зелёные лично; smoke-select учтён построчно; мутант пойман исполнением (не только --check); mutation-gate --check зелёный (205/200, WARN не fail); CHANGELOG RU/EN в том же коммите, текст дословно совпадает с ТЗ; golden не заказан и не нужен (резюме выше) |
Единственное число, видимое пользователю дважды в этом диффе —
DEFAULT_CUSTOM_FILL (#607d8b / 0.18, src/logic.ts:1301): диффом
не менялось (src/logic.ts вне скоупа правки), источник один, смок
читает фактическое значение через getComputedStyle, а не дублирует
константу в продуктовом коде (дублирование — только в самом свидетеле,
FINAL_FILL в smoke_space_card.mjs, это тестовая проверка, не
источник истины).
Находки
Нет. High: 0. Medium: 0.
Что проверено и корректно
- Диффы
src/space-render.ts,src/styles/plan.styles.ts,scripts/mutation-registry.mjs,docs/CHANGELOG*.mdна нынешнем материале посимвольно совпадают с тем, что уже разобрал r1 — ребейз сдвинул контекст и patch-id, но не содержание правки. - Форма правки — буквально та же, что #742 поставил в
houseplan-card.ts:keyed(space.id, repeat(shownRooms, (r, index) => r.id || index, …)); импортыkeyed/repeatдобавлены там, где их не было (space-render.ts:10-11). - Пустой
idключуется числовым индексом (r.id || index) — число никогда не совпадает со строковымidпоMap-семантикеrepeat. .room { transition: 0.12s }не тронут; AC2-проверка настоящей смены заливки (иsmoke_space_switch_transitions) подтверждает, что это не фикс черезtransition: none.roomShapesиспользуется один раз (space-render.ts:964по старой нумерации), встроен в<svg>тем же способом, что и раньше —keyed/repeatне добавляют обёрточных DOM-узлов и не меняют порядок фигур.- Мутант
space-card-rooms-rendered-without-keysв реестре синтаксически корректен (сборка мутанта проходит), guard пойман исполнением гейта, а не только текстовой проверкой--check, лично на этом SHA. - Трейлеры
Issue: #745,User-Visible: yesна коммите6cd29f9b; CHANGELOG RU/EN правлены в том же коммите, текст дословно совпадает с ТЗ (сверено построчно). no-new-private-writes: свидетель пишет конфиг только через__hpTest.setServerConfig, отклонение от ТЗ названо и обосновано автором, подтверждено зелёным шагомgate:smallна этом прогоне.- Не-скоуп, названный в ТЗ (маркеры
devs.map, проёмыresolvedHosted.map), не тронут — верно, задача явно ограничивает поверхность одним списком комнат. - Рабочая копия после всех проверок чиста (
npm run bundle:clean,git status --shortпуст,HEAD=7be2d456).
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| — (r1 вынес зелёный вердикт, находок не было) | Раунд r2 открыт не находкой, а ребейзом: dev продвинулся на 4 коммита во время r1, patch-id кандидата разошёлся с рецензированным (ca708d84 → текущий материал), конвейер потребовал новый заход по §2.10 |
Комментарий к issue между r1 и r2: «Материал ревью ca708d84 и кандидат 7be2d456 дают разные patch-id: соседние правки в dev изменили содержимое патча» |
Унаследовано из r1
- Факт воспроизведения бага на
dev(зонд8b7725ac, временный, в ветку не шёл) — принят из тела issue, как и в r1; сам зонд повторно не гонял, он не часть материала этого раунда. - Решение не заказывать
golden:verifyна паттерне, идентичном #742 (CODE-REVIEW-742-r1.md) — согласие с рассуждением r1 принято, но в этом раунде я его не просто унаследовал, а перепроверил чтением кода заново (см. таблицу гейтов выше):keyed/repeatне меняют состав/порядок DOM-узлов относительноmap(). - Остальное (AC1/AC2/AC3, мутант,
smoke-select,gate:small, CHANGELOG) в этом раунде прогнано и перепроверено лично, не унаследовано — ребейз на ушедший вперёдdevтребует полного разбора, а не доверия цифрам прошлого захода. Документ r1:docs/reviews/CODE-REVIEW-745-r1.md, SHA материала r1 —ca708d8443ae(не в текущем дереве — ребейз его осиротил).
Маршрут вердикта (§5)
Все критерии пройдены: сложность и риск низкие (копия уже
проверенного на #742 паттерна, локальная правка одного списка),
одна поверхность (src/space-render.ts),
нет миграции конфига, нет нового UX-контракта (устраняется баг в
рамках уже описанного поведения #525/#534/#742), нет влияния на
производительность/touch-контракт (список рисуется тем же способом,
без новых DOM-узлов), ожидаемое поведение зафиксировано тем же
правилом и текстом CHANGELOG. route: fix.
Чего не проверял
- Полную сверку бандла из трёх независимых копий — не требовалась:
Validate зелёный на этом SHA подтверждает
build; локальная пересборка (bundle:syncдля смоков +--idмутанта) прошла ещё раз без расхождений. - 285 «слабых»/неотобранных смоков вне прямого совпадения
smoke-select— единственная прямая связь (smoke_room_settings) прогнана;setConfigкак источник совпадения разобран и сознательно не расширен. golden:verify,pytest tests_backend,npm run invariants, performance-профили — не требуются по диффу/AC (нет правки Python, нет правки геометрии комнат, меткиci:goldenнет, перф не затронут); решение по golden перепроверено чтением кода в этом раунде, не просто унаследовано.- Ручная проверка в браузере глазами — доказательство построено на
getComputedStyle/getAnimations, тех же сигналах, которые увидел бы человек на первом кадре; отдельно не повторял. - Повторный прогон зонда бага на
dev(8b7725ac) — принят из тела issue как контекст, не как материал этого раунда. - Пакетное/ночное ревью, release-review — вне объёма этого раунда (§8: объём гейтов соразмерен задаче).
Материал раунда
- Ветка:
issue/745-space-card-room-keys, коммит7be2d456003f— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
5ff5a59993d85725fbb729297ce1aec86f3ba266git log --all --format='%H %T' | grep 5ff5a59993d8 - Тело issue:
8c17362760b68e61c8c6b00cda0e18656cacfb312c80e5828034f3cc79599971 - Вердикт конвейера:
green· High 0 · маршрутfix