19 KiB
CODE-REVIEW-739-r1
Issue: #739 · «perf(iso): keep the theme paper across floor switches in 2.5D»
Трек: ask (перф, §5) · Заход: r1 · блокирующих циклов использовано 0/4
Материал: 20733ac436faa574837812698bb17e98ebd09383 (один коммит поверх origin/dev,
merge-base 5a49c8c32df959a5f5450e856b204e0120b5125f)
Вердикт: зелёный
Скоуп
Единственная цель — К1 из ТЗ: в 2.5D с подложкой цвет «бумаги» (фон карточки
темы под изображением плана) должен определяться один раз на идентичность
темы+режима, а не на каждое переключение этажа. До правки каждое переключение
роняло кэш из-за space в ключе isoPaperContext, что давало второй полный
проход рендера (невидимую, но дорогую заставку .bootveil + getComputedStyle
requestUpdate()). Эффект — чисто перформансный; экран не меняется (User-Visible: no, подтверждено в ТЗ и трейлерах коммита).
Файлы диффа: src/iso-first-frame.ts (единственный продуктовый файл),
test/iso-stage6.test.mjs, demo/smoke_iso_floor_switch.mjs (новый),
scripts/mutation-registry.mjs, scripts/smoke-links.mjs, docs/ISOMETRIC.md.
src/houseplan-card.ts не тронут — соответствует заявлению автора и ТЗ
(«Затронутые файлы»).
Закрывает: J1 (docs/SCOPE.md) — «живой обзор дома», частое действие
переключения этажей в киоске/на телефоне; ускоряет путь, которым пользуется
каждый дом с подложкой в 2.5D.
Как проверялось
Дешёвые гейты подтверждены Validate на этом SHA (прогон
36845529191,
success): npx tsc --noEmit, npm test, npm run build + сверка бандла — не
перегонялись повторно.
Сверх Validate прогнано лично в этом раунде:
| Гейт | Команда | Результат |
|---|---|---|
smoke-select по диффу |
node scripts/smoke-select.mjs --base origin/dev --head HEAD |
2 «зарегистрированные связи»: demo/smoke_iso_first_frame.mjs, demo/smoke_iso_floor_switch.mjs (символы commitPaper, themePaper) — оба названы в AC2/AC3, оба прогнаны ниже |
| Мутант AC5, гард | npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node --test --test-name-pattern="#654|#739" test/iso-stage6.test.mjs |
4/4 passed |
scripts/mutation-gate.mjs --check |
— | ok iso-paper-resolved-per-floor, предупреждения те же, что на dev (4 общих + browser guards 201/200, те же, что были) |
demo/smoke_iso_floor_switch.mjs (AC2) |
node demo/smoke_iso_floor_switch.mjs |
OK на материале; красный (everySwitchRendersOnce, noSwitchProbesThePaperAgain, noSwitchInsertsTheVeil = false, 2 обновления/1 проба/1 веил на переключение) после отката src/iso-first-frame.ts к версии origin/dev — тест умеет падать, проверено лично (не со слов автора) |
demo/smoke_iso_first_frame.mjs (AC3) |
node demo/smoke_iso_first_frame.mjs |
OK, все 7 проверок true |
demo/smoke_isometric_contract.mjs (AC3) |
node demo/smoke_isometric_contract.mjs |
OK |
demo/smoke_iso_flat_parity.mjs (AC3) |
node demo/smoke_iso_flat_parity.mjs |
OK |
demo/smoke_iso_theme_walls.mjs (AC3) |
node demo/smoke_iso_theme_walls.mjs |
OK |
node scripts/no-new-private-writes.mjs --base origin/dev --head HEAD |
— | «Новых записей в приватное состояние карточки нет» (перехват card.update/card._cssColor в смоке не считается нарушением; у _cssColor стоит // private-ok: #739) |
npm run bundle:budget |
— | initial View headroom 541 Б (ceiling не превышен); предупреждение про низкий запас — долг #367/#474, не новый, не из этого диффа |
Перед смоками потребовалось npm run build && node scripts/bundle-sync.mjs,
чтобы demo/srv/assets увидел свежий бандл (сам по себе dist/ на диске при
старте раунда не был синхронизирован с demo/srv). После проверок рабочая
копия возвращена npm run bundle:clean — git status чист.
Полная матрица смоков, golden:verify, pytest tests_backend, performance —
не гонялись (обоснование ниже, «Чего не проверял»).
AC — разбор
AC1 (состояние, test/iso-stage6.test.mjs). Тест добавлен и покрывает все
шаги (а)–(е) плюс отдельно прогон «уход в редактор и обратно». Прочитан код
src/iso-first-frame.ts построчно и прослежен исполнением:
isoPaperContextбольше не кладётspaceни вtheme(идентичность темы), ни вkey(теперьkey = [imagePlan, theme]). Из-за этогоkeyу двух разных этажей с подложкой и одинаковой темой совпадает — это и есть механизм К1: вprepare()условиеcontext.key === this.paperContextстановитсяtrueуже на первом же сравнении, без обращения кthemePaper-кэшу вообще.- Переход «подложка → нарисованный план → снова подложка» — другой случай:
keyменяется ({imagePlan:false}≠{imagePlan:true}),paperReadyсбрасывается, ноprepare()находитthis.themePaper?.theme === context.themeи мгновенно коммитит закэшированный rgb без похода в DOM — это шаг (г) теста.commitPaperпри этом помечаетfloorMemo = null(т.к.!paperReadyна момент вызова истинно), так чтоlightFloors()пересчитывается на новом (не белом) paper — тест проверяет это явно ([...]→[]вместо['unfilled']). memoIsoLightFloorRooms(не тронут диффом) сам ключует наfills+paper(src/iso-materials.ts:118), поэтому коллапсkeyмежду разными этажами с одинаковой темой не ломает пересчёт светлых комнат при сменеfills— проверено чтением, отдельного теста на эту комбинацию в АС нет, но риска регрессии не вижу:floorMemoпереиспользуется только если оба входа (цвет и fills) совпали.- Смена темы или
_modeвходит вcontext.theme, поэтому шаги (д)/(е) сбрасываютthemePaper(строкаif (this.themePaper.theme !== context.theme) this.themePaper = null) и идут путём #654 — подтверждено тестом и чтением.
Проверено исполнением (node --test, фильтр #654|#739): 4/4 зелёные.
Проверено также, что тест красный на коде origin/dev конкретно на той
проверке, которую заявляет коммит-сообщение («a floor switch shows no veil»,
pending() ожидаемо true на dev вместо false) — убедился чтением логики
(dev-версия не содержит themePaper, значит pending() после prepare() на
шаге (б) обязана остаться true).
AC2 (один рендер на переключение, demo/smoke_iso_floor_switch.mjs).
Смок прогнан лично: на материале — OK, 1 update, 0 _cssColor, 0 вставок
.bootveil на каждое из 6 переключений, readiness всегда ready, светлые
комнаты совпадают с первым показом этажа. После отката файла до origin/dev —
смок красный ровно на тех трёх счётчиках, которые и должен был поймать
(everySwitchRendersOnce, noSwitchProbesThePaperAgain, noSwitchInsertsTheVeil
= false, 2/1/1 вместо 1/0/0) — дисциплина «тест умеет падать» выполнена не со
слов автора, а исполнением.
AC3 (холодный путь и смежные контракты не изменились). Четыре названных
смока (smoke_iso_first_frame, smoke_isometric_contract,
smoke_iso_flat_parity, smoke_iso_theme_walls) прогнаны — все зелёные.
smoke-select по диффу вернул только эти два файла как связанные с диффом
(«зарегистрированная связь», не «прямое совпадение» и не «неопределённость») —
оба в списке прогнанных. Красные песочницы (smoke_summary_*,
smoke_infinite_canvas, smoke_live_pan_coverage) из AC3 не проверялись
повторно — судит Validate на этом SHA, как и прописано в самом AC.
AC4 (без регрессий, Full Performance). Ссылка на прогон
36839009721
проверена через gh run view: conclusion: success, headSha: 31464bb69125ec8eaa9df8c67b97f7e08ac9074f. Эта SHA — более ранняя точка ветки
(до ребейза на текущий dev, когда база была 7ff2b5ae); сам объект-коммит в
локальном репозитории недоступен (история переписана ребейзом), поэтому
побайтово дифф между 31464bb6 и 20733ac4 не сверить без git fetch,
который запрещён инструкцией материала. Компенсирующая проверка: git diff --stat 7ff2b5ae..origin/dev показывает, что все 4 коммита, которые легли
между старой базой прогона и текущей, — AGENTS.md, PROCESS.md,
docs/process/*, scripts/process-gate.mjs, scripts/task-packet.mjs,
test/moon.test.mjs, test/process-*, test/task-packet.test.mjs — не
пересекаются ни с одним файлом диффа #739 (src/iso-first-frame.ts,
test/iso-stage6.test.mjs, demo/smoke_iso_floor_switch.mjs,
scripts/mutation-registry.mjs, scripts/smoke-links.mjs,
docs/ISOMETRIC.md). Значит ребейз не мог изменить проверяемый диф, и
прогон на 31464bb6 доказывает то же самое, что доказал бы прогон на
20733ac4. Числа в прогоне (9/9 профилей зелёные, switchCycleMs в пределах
±1% на плоских профилях, в isometric modelReady −9%, switchCycleMs −4% —
улучшение, не регрессия) совпадают с заявленным. Сам Full Performance заново
не гонялся — дорогой гейт, ссылка уже даёт нужное доказательство.
AC5 (гейт задачи). mutation-gate.mjs --check: ok iso-paper-resolved-per-floor, патч мутанта (добавляет space обратно в
идентичность темы) и гард (#654|#739 в test/iso-stage6.test.mjs)
осмысленны — я прочитал: мутант ломает ровно ту строку, из-за которой правка
вообще нужна. gate:small не перегонялся отдельно — он часть Validate,
подтверждённого на этом SHA. test/core-file-budget.test.mjs не актуален —
houseplan-card.ts диффом не тронут.
Трейлеры и changelog
Коммит несёт Issue: #739 и User-Visible: no — соответствует ТЗ («экран не
меняется»). Правки обоих CHANGELOG не требуются и отсутствуют — корректно.
Единственное число, которое «видно дважды» в материале — бюджетный потолок
бандла View (не меняется диффом, только фактическое значение растёт в
пределах полосы 2000 Б, подтверждено bundle:budget, источник один —
scripts/bundle-budget.mjs). Других дублирующихся чисел в диффе нет (нет
новых UI-текстов, нет новых констант, видимых пользователю).
Отклонения ТЗ → реализация
Три отклонения автор назвал сам в хендоффе, все проверены чтением и совпадают с кодом:
spaceостался параметромisoPaperContext, но не входит вtheme/key— подтверждено (см. AC1 выше); тесты #654 действительно не менялись (git diffв тестовом файле — только добавление нового теста).- Сброс кэша при смене
_mode— действительно строже «принято предположительно» ТЗ (там говорилось только про тему); обоснование (редактор между подложками другого типа плана не должен брать старый цвет) логично и не противоречит «Режимам» ТЗ («уход в редактор... цвет ищется заново»). commitPaper,themePaperдобавлены и в запись#654smoke-links— подтверждено в диффеscripts/smoke-links.mjs.
Что проверено и корректно
- Устранение второго прохода рендера реализовано корректно и доказано и юнитом, и смоком, с фактическим «тест умеет падать» (не теория).
- Коллапс ключа между разными этажами с одной темой — найден чтением,
совместим с
memoIsoLightFloorRooms, не ломает пересчёт светлых комнат. - Холодный путь (#654), смена темы/режима, отказ чанка — не затронуты
(код
pending/readiness/runtimeFailureне менялся). - Гейт приватных записей и бюджет бандла — зелёные, без новых предупреждений
сверх тех, что уже были на
dev. - Документация (
docs/ISOMETRIC.md) обновлена в том же коммите, формулировка соответствует новому поведению.
Чего не проверял
- Golden. Диф меняет путь отрисовки 2.5D (
src/iso-first-frame.ts), видимый риск по правилу §5.1 реален в общем случае, но здесь не реализуется: кадр не меняется — то же решение «нет» подтверждено ТЗ («На экране ничего не меняется: та же сцена... заставки при переключении нет — ни сейчас, ни после правки») и структурно: правка не трогает ни геометрию, ни материалы/заливки (iso-materials.ts,iso-scene-render.tsне в диффе), а только то, сколько раз и когда резолвится тот же самый цвет и нужен ли промежуточный.bootveil, который и до, и после невидим глазу (кадр с заставкой и без неё идёт в одной задаче доrequestAnimationFrame). Меткиci:goldenна задаче нет,golden:verifyне гонял. - Full Performance заново. Не гонял — использовал существующий зелёный прогон (см. AC4) с обоснованием, что ребейз его не инвалидировал.
pytest tests_backend. Диф не трогаетcustom_components/**/*.py— не применимо.- Инварианты модели. Диф не трогает геометрию комнат/устройств — не применимо.
- Полная матрица смоков. Не гонял — только смоки, названные в AC2/AC3 и
подтверждённые
smoke-select; полный прогон — предрелизная обязанность. - Мутации вне реестра/регистрация нового мутанта в CI-прогоне. Мутанты в
разработке не гоняются (§2.7, #709); якорь патча проверен
--check.
Находки
Нет. High: 0, Medium: 0.
Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0 → в задаче
Материал раунда
- Ветка:
issue/739-layout-reads-followup, коммит20733ac436fa— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
886102777be1249389fcd2a662dbe551200250fcgit log --all --format='%H %T' | grep 886102777be1 - Тело issue:
b4865b454fae1e35d41752478fda842157505fded49f39f20bbaff6fa2794b37 - Вердикт конвейера:
green· High 0 · маршрутfix