18 KiB
CODE-REVIEW-740-r1
Issue: #740 · «Производительность: слой лестниц — ≈50 мс на переключение на этаж 1»
Трек: ask (перф, §5) · Этап: code · Заход: r1 · Материал: 1b46c3f42bb8a9df627d237dcee115a162c20dc3
(два коммита над origin/dev: 0225fbff — К1, 1b46c3f4 — отпечаток скриншотов после ребейза)
Скоуп
К1 (единственный пункт контракта): все ступени одной лестницы рисуются одним
<path class="hp-stair-tread"> в слое View и в редакторе плана вместо
3–7 отдельных <line>. Строки разметки (контур и d ступеней) строятся один
раз на объект геометрии из cachedStairRenderGeometry и кэшируются WeakMap
(cachedStairMarkup, stairTreadPath, src/stairs.ts). Эффект доказывается
Full Performance (AC4); пиксельная идентичность — golden (AC3, ci:golden).
Файлы диффа: src/stairs.ts, src/stairs-view.ts, src/stairs-editor.ts,
test/stairs.test.mjs, demo/smoke_stairs.mjs, scripts/mutation-registry.mjs,
scripts/smoke-links.mjs, docs/STAIRS.md, docs/testing-notes/mutation-browser-guards.md,
docs/images/screenshots.json (только отпечаток после ребейза).
Как проверялось
| Гейт | Статус | Источник |
|---|---|---|
npx tsc --noEmit, npm test, npm run build, bundle-policy --verify |
зелёные | Validate на 1b46c3f4: 36897816678 — приняты как дешёвые, не перегонял |
npm run build + npm run bundle:sync |
зелёные | прогнал сам, для локального запуска смока и golden-диагностики |
AC1 — test/stairs.test.mjs (две находки с #740) |
прочитан построчно | не перезапускал отдельно — входит в зелёный npm test Validate выше |
AC2 — node demo/smoke_stairs.mjs |
зелёный, все поля true, включая editorTreadsAreOnePath, viewTreadsAreOnePath |
прогнал сам локально |
node scripts/smoke-select.mjs --base origin/dev --head HEAD |
одна зарегистрированная связь — smoke_stairs.mjs (та же, что в AC2) |
прогнал сам |
AC5 — node scripts/mutation-gate.mjs --check |
ok для stairs-view-tread-lines, реестр и mutation-browser-guards.md согласованы; общий WARN 207/200 — существующий фон, не этой задачи |
прогнал сам |
AC3 — golden (ci:golden) |
4 сцены лестниц (stairs-flat-normal-light, stairs-flat-hover-dark, stairs-flat-selected-light, stairs-isometric-dark) passed |
не локально (см. «Чего не проверял») — проверено по логам CI, см. ниже |
| AC4 — Full Performance | не подтверждён на материале ревью | см. находку ниже |
AC3 отдельно: почему локальный прогон не понадобился
Моя попытка npm run golden:verify локально упёрлась в окружение песочницы
(фетч собранного модуля таймаутит ещё до сверки Chromium — то же расхождение
окружения, о котором уже писал автор про 3 PDF-сцены). Вместо повторной
борьбы со средой проверил цепочку переиспользования самого конвейера: job
«Golden-кадры против принятых эталонов» реально выполнился (не был
переиспользован) на прогоне 36888262153
(SHA 5b94c09e) и по логам все 192 сцены, включая все 4 сцены лестниц,
passed. Сам прогон в целом был failure, но из-за несвязанного флака
guard_tail_rejection.mjs (#776 по словам автора) — job golden внутри него
зелёный независимо. Дальше каждый следующий пуш (fc083b96→…→1b46c3f4)
переиспользовал этот результат через маркер reuse-golden-<hash входов>
(#208): лог Validate на 1b46c3f4 прямо называет источник —
source_sha=5b94c09e0b21701ac0dfcff1a800c5de55ee130d, «golden не
прогоняется: входы побайтово те же». Это доказательство исполнением, а не
заявлением автора: я прочитал реальный вывод passed по каждой из четырёх
сцен на конкретном SHA и прямую связь с материалом ревью по хэшу входов.
AC3 — выполнен.
Находки
Medium (в скоупе) — AC4 не доказан на материале ревью: единственный Full Performance прогон стоит на устаревшей базе
Где: AC4 таблицы приёмки (issue #740, раздел «Критерии приёмки»); данные в комментарии автора «Сделано».
Что не так. На ветке есть ровно один прогон performance.yml:
36838891001,
на SHA 610134c3 (самый первый коммит задачи) против базы dev 7ff2b5ae.
После него ветка трижды ребейзилась на ушедший вперёд dev (причины —
красный Validate: устаревший отпечаток скриншотов, конфликт счётчиков
mutation-browser-guards.md, флак guard_tail_rejection.mjs) и в итоге
слилась с dev 0606a366 без конфликтов в коде. Новый Full Performance
после этих ребейзов не запускался — я проверил список прогонов workflow
performance.yml для ветки, там только эта одна запись.
Между измеренной базой (7ff2b5ae) и фактической (0606a366) в dev
попали как минимум три коммита, напрямую меняющих ту же цену тёплого
переключения этажа, которую АС4 сравнивает:
9324d81bperf(iso) #739 — убирает двойной рендер-проход в 2.5D с фоновым изображением, локально ≈50 мс на тёплый заход. Это тот же участок (тёплое переключение, 2.5D-профилиisometric/isometric-stage3), что измеряет АС4.fa18ca81#747 — прямо по тексту коммита использует в том числе и сам прогон #740 (36838891001) как один из четырёх калибровочных рядов, чтобы подтянутьhardMaxMsвниз до 1550 мс (2.5D) и тоже назвать новый потолок. АС4 утверждает «бюджеты и hardMaxMs не меняются» — для базы7ff2b5aeэто было верно, для фактической базы слияния0606a366уже нет: потолок к этому моменту другой, и откалиброван отчасти по старым числам этой же задачи.a0f72280/dc77852e#742/#745 — переключение ключей комнатных фигур поspace+id, тоже на путиswitchCycle.
Риск 4 самого ТЗ предвидел именно это: «AC4 снимается на ветке, приведённой
к актуальному dev» — но после финального приведения к 0606a366 новое
измерение не опубликовано, остались только числа против 7ff2b5ae.
Почему это находка, а не придирка к процедуре. Код К1 между ребейзами не
менялся (конфликты были только в docs/testing-notes/mutation-browser-guards.md
и docs/images/screenshots.json), поэтому сам факт улучшения, скорее всего,
сохранился. Но АС4 требует не «вероятно», а «медиана switchCycleMs
кандидата в large-house ниже базы, в isometric и plan-snap не выше» —
а один из профилей сравнения (isometric) напрямую пересекается с
оптимизацией #739, которая уже снизила именно 2.5D-базу, которую АС4 не
переизмерил. Относительный эффект лестниц (−185…−222 мс по старым данным)
мог заметно сократиться на новой, уже ускоренной базе, а подтверждения, что
isometric/isometric-stage3 по-прежнему «не выше базы» на 0606a366, в
материале нет.
Чем доказано, что это отсутствует, а не что я придираюсь: список
прогонов performance.yml для ветки (ровно один, на устаревшем SHA);
git log --ancestry-path 7ff2b5ae..0606a366 -- src/ demo/performance (пять
перф-значимых коммитов, в т.ч. #739/#747, лежащих между измеренной и
фактической базой); текст коммита fa18ca81, прямо называющий прогон
36838891001 своим калибровочным входом.
Чем закрывается: перезапустить
gh workflow run performance.yml --ref issue/740-stairs-floor-switch -f comparison_ref=0606a366
(или актуальный на момент повторного раунда merge-base) и опубликовать в
issue числа по всем профилям против него — именно то, что уже требует AC4 и
что предвидел риск 4 самого ТЗ.
Вердикт — жёлтый из-за этой находки; она в скоупе задачи (это её же AC4), отдельный issue не заводится.
Что проверено и корректно
- AC1.
stairTreadPathстроитdкак склейкуM a0 a1 L b0 b1поtreadsв порядке геометрии — прочитано построчно, совпадает с контрактом (src/stairs.ts:345).cachedStairMarkupкэширует по объекту геометрии черезWeakMap, без пересборки при повторном вызове — тестtest/stairs.test.mjsдоказывает это через===-идентичность возвращаемого объекта (а не просто равенство строк), и отдельно показывает инвалидацию при смене геометрии черезitem.angle = 45. Нулевой случай (лестница короче 40 см, 0 ступеней → пустая строка) тоже покрыт. Тест умеет падать: наdevсимволовcachedStairMarkup/stairTreadPathнет, импорт не соберётся. - AC2. Разметка:
path.hp-stair-treadровно один вместоline-ов, порядок и остальные классы (outline,hit,trapezoid×3/0,arrow) не изменились — прочитано вsrc/stairs-view.ts/src/stairs-editor.tsи подтверждено прогономdemo/smoke_stairs.mjs(все поляtrue, в т.ч. новыеeditorTreadsAreOnePath/viewTreadsAreOnePath). У лестницы без ступеней элемента нет (markup.treads ? ... : nothing).smoke-selectпоказывает, чтоsmoke_stairs.mjs— единственная и уже зарегистрированная связь для новых символов; новых непокрытых символов нет. - AC3. Golden для всех 4 сцен лестниц
passed, проверено исполнением через цепочку переиспользования CI (см. выше) — не только по словам автора. - AC5. Мутант
stairs-view-tread-linesзарегистрирован, откатывает именноsrc/stairs-view.tsк старым<line>, гард —smoke_stairs.mjs, который на такой откат покраснеет (editorTreadsAreOnePath/viewTreadsAreOnePath/count('line.hp-stair-tread')===0перестанут выполняться). Запись вdocs/testing-notes/mutation-browser-guards.mdиscripts/smoke-links.mjsна месте.mutation-gate.mjs --check— зелёный. - Остальной контракт К1. Порядок DOM-элементов внутри
<g class="hp-stair...">не изменился (outline → hit → trapezoid → treads → arrow); классhp-stair-viewпо-прежнему различает слои — это использует и сам смок для независимой проверки каждого слоя..hp-stair-treadвsrc/styles/plan.styles.ts:1645селектит иpath, иlineбез правки, как и требовал контракт. Редактор (stairs-box.ts, черновик, ручки) К1 не трогает — изменений там нет.src/space-render.tsиsrc/pdf/pdf-scene.tsне задеты, что соответствует «не-скоупу» issue. - Трейлеры. Оба коммита несут
Issue: #740иUser-Visible: no; поведение действительно не видно пользователю (markup-рефакторинг под капотом), changelog не правился — корректно. - Числа. DOM-счётчик «4210 → 2960» в issue и в теле коммита
0225fbffсовпадает дословно — один источник, не разъехался.
Чего не проверял
- Full Performance сам не запускал — это дорогой гейт (десятки минут,
выделенный workflow с
comparison_ref); его обязан прогнать автор, см. находку выше. npm testцеликом иnpx tsc --noEmitотдельно не перегонял — принял зелёный Validate на точном SHA материала (1b46c3f4) как подтверждение дешёвых гейтов согласно прилагаемой инструкции;npm run buildвсё же прогнал сам (для локального смока и попытки golden), он тоже зелёный.golden:verifyлокально — не прогнал до конца (окружение песочницы: Chromium/фетч бандла ведут себя иначе, чем в CI-профиле). Заменил на чтение логов CI (см. AC3 выше) — это проверка исполнением на конкретном SHA, а не доверие к заявлению автора.npm run invariants— не прогонял: диапазон диффа не меняет саму геометрию (stairRenderGeometry,treads,outlineостались прежними данными), меняется только то, как её точки сериализуются в разметку; степень риска инвариантов геометрии к этой задаче не относится.pytest tests_backend— не прогонял, диффа в Python нет.- Полный
golden:capture/ручной визуальный осмотр скриншотов — не делал; положился на пиксельное сравнение CI, которое строже визуального осмотра.
Вердикт
Жёлтый. Единственная находка — Medium, в скоупе задачи (AC4 этой же issue):
перезапустить Full Performance против актуального dev и опубликовать
числа. Код К1, AC1/AC2/AC3/AC5 проверены и корректны — при зелёном AC4
задача готова к повторному зелёному вердикту без доп. находок по коду.
Материал раунда
- Ветка:
issue/740-stairs-floor-switch, коммит1b46c3f42bb8— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
14c4fb8c2402beb23220a3ca0e3d4e45f06f5e43git log --all --format='%H %T' | grep 14c4fb8c2402 - Тело issue:
9cfcfc9e8157d5a25961e793ed530de257848fb5d870dee885fbd71103ffb3bd - Вердикт конвейера:
yellow· High 0 · маршрутfix