mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
@@ -0,0 +1,301 @@
|
||||
# CODE-REVIEW-451-r1
|
||||
|
||||
- **Issue:** #451 «План подтормаживает: диагностика на каждый кадр, реактивная камера, отсутствие фильтра HA-тиков»
|
||||
- **Этап:** код-ревью, заход r1 (первый действительно проведённый — предыдущая попытка не читала код: ветка не ребейзилась на `dev` без конфликта, цикл не израсходован)
|
||||
- **Материал:** `git log --oneline origin/dev..HEAD` / `git diff origin/dev...HEAD`, SHA `1850eb18b5e8f0813f9d2efb50694dc4ca1fff26`
|
||||
- **ТЗ:** `docs/specs/451-render-performance.md` (полный трек, `SPEC-REVIEW-451-r2` зелёный)
|
||||
- **Вердикт:** см. итог в конце документа
|
||||
|
||||
## Скоуп диффа
|
||||
|
||||
75 файлов, +3947/‑1166 (без учёта `dist/**`/`custom_components/.../frontend/**`, класс D). Новые модули:
|
||||
`src/render-invalidation.ts`, `src/houseplan-render-lifecycle.ts`, `src/live-viewport.ts`,
|
||||
`src/live-hover.ts`, `src/live-editor.ts`, `src/live-interaction-runtime.ts`,
|
||||
`src/pointer-move-queue.ts`, `src/interaction-types.ts`. Изменены `src/houseplan-card.ts`
|
||||
(overridden `requestUpdate`, diagnostics, terminal reconciliation), `src/houseplan-editor-runtime.ts`
|
||||
(pointer-move очереди для plan/decor/backdrop/opening/resize), `src/render-device-snapshot.ts`
|
||||
(`entityIds`), `src/zigbee-topology-overlay-bridge.ts` (live-layer маркер). Плюс performance harness
|
||||
(`demo/benchmark_large_house.mjs`, `budgets-large-house-interaction.json`, `card-contract.mjs`,
|
||||
`evaluate.mjs`, `performance.yml`) и точечные правки существующих `demo/smoke_*.mjs` под новую разметку
|
||||
`data-hp-live-*`. Геометрия/модель стен, `layout`, `marker.space`, `open_spans` не тронуты — гейт
|
||||
`npm run invariants` не требуется, что подтверждается и явным «Non-scope» ТЗ.
|
||||
|
||||
## Как проверялось
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| typecheck | `npx tsc --noEmit` | зелёный |
|
||||
| unit | `npm test` | 1956 tests, 1955 pass, 1 skip, 0 fail |
|
||||
| build + бандл | `npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` + `diff -rq` на `houseplan-assets` | совпадают байт-в-байт |
|
||||
| docs fingerprint | `node scripts/check-docs.mjs` (обязателен — diff трогает `src/**`) | «Documentation checks passed (7 files, 12 external links)» |
|
||||
| any-гейт | не запускал отдельно — новые файлы не содержат `any` (проверено чтением), `npm test`/`typecheck` зелёные | проверено чтением |
|
||||
| smoke-select | `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | 98 прямых совпадений + 36 слабых из 221 (см. `smoke-select.txt`) |
|
||||
| smoke (почти полный набор) | все 134 файла из прямых+слабых совпадений, `node demo/smoke_<name>.mjs` по одному | 133 OK, 1 нестабильный (см. ниже, не регрессия) |
|
||||
| golden | `npm run golden:verify` | 153/153 сценариев `passed`, 0 diff — доказывает AC9 (pixel-equivalence) |
|
||||
| performance (структурные assert'ы) | `npm run benchmark:large-house-interaction -- --samples=7 --warmups=1` | завершился без throw — структурный контракт (0 full render на hover/pan/editor move, 1 terminal, 0 diagnostics scans) подтверждён на этом SHA |
|
||||
| performance (абсолютные потолки) | `npm run benchmark:compare -- --absolute-only --budgets=demo/performance/budgets-large-house-interaction.json --candidate=<report>` | **6 проверок красные** — см. находку H1 |
|
||||
| backend | не запускал — диф не трогает `custom_components/**/*.py` (проверено чтением: `git diff --name-only` не содержит `.py`) | проверено чтением |
|
||||
| invariants | не запускал — диф не трогает геометрию/`layout`/`marker.space`/`open_spans` (проверено чтением) | проверено чтением |
|
||||
|
||||
Не прогонял полный `demo/smoke_*.mjs` (все ~221) и полный `performance.yml` (`Full Performance`,
|
||||
парный base/candidate на двух чекаутах) — первое избыточно (134 из 221 уже покрывают все «прямые» и
|
||||
«слабые» совпадения smoke-select, второе требует парного окружения, которое не гейт код-ревью
|
||||
(PROCESS.md §8: «полные наборы — предрелizный гейт»). Раздельный прогон `--absolute-only` (см. выше)
|
||||
компенсирует это для абсолютных потолков нового профиля — и именно он нашёл H1.
|
||||
|
||||
## Находки
|
||||
|
||||
### H1 (High, блокирует) — новый performance-профиль `large-house-interaction-v1` не проходит собственный бюджет
|
||||
|
||||
**AC10** требует: «новый interaction profile проходит все structural, absolute и base-relative checks
|
||||
§11». Структурные проверки (0 full render на hover/pan/camera/editor move, ровно 1 terminal, 0
|
||||
diagnostics scans, стабильный heavy DOM, ошибка совмещения SVG/HTML ≤1px) — подтверждены, профиль не
|
||||
бросает исключение. Но абсолютные потолки, которые сам же диф вводит в
|
||||
`demo/performance/budgets-large-house-interaction.json`, не выполняются при фактическом прогоне.
|
||||
|
||||
**Воспроизведение** (детерминировано, не связано со скоростью машины — см. ниже):
|
||||
|
||||
```
|
||||
npm run benchmark:large-house-interaction -- --samples=7 --warmups=1 --output=/tmp/interaction-review7.json
|
||||
npm run benchmark:compare -- --absolute-only \
|
||||
--budgets=demo/performance/budgets-large-house-interaction.json \
|
||||
--candidate=/tmp/interaction-review7.json --output=/tmp/interaction-compare7.json
|
||||
```
|
||||
|
||||
Результат — 6 красных строк:
|
||||
|
||||
```
|
||||
❌ timing.interactionSeriesMs.median | 3501.5 | 3000
|
||||
❌ timing.editorSeriesMs.median | 1841.9 | 750
|
||||
❌ longTask.editorSeries.maxSingleMs | 878 | 150
|
||||
❌ longTask.editorSeries.countP95 | 4 | 3
|
||||
❌ longTask.editorSeries.totalP95Ms | 1633 | 300
|
||||
❌ cache.entries.cleanFloor | 140 | 100
|
||||
```
|
||||
|
||||
`cache.entries.cleanFloor` — счётчик записей `Map`, не таймінг, машинно-независим. Проверено на **двух**
|
||||
независимых прогонах (`--samples=3` и `--samples=7`), значение **140 во всех 10 сэмплах без единого
|
||||
отклонения**. Для контроля прогнан немодифицированный `large-house-v1` (существует и на `dev`, диф его
|
||||
не трогает):
|
||||
|
||||
```
|
||||
npm run benchmark:large-house -- --samples=3 --warmups=1 --output=/tmp/base-profile-review.json
|
||||
# cleanFloor: 100, 100, 100 — ровно документированный потолок (demo/performance/README.md:
|
||||
# «the reviewed fixture warms exactly 100 deterministic room/physical-body entries»)
|
||||
```
|
||||
|
||||
Значит переполнение специфично именно для новой `editor series` части `large-house-interaction-v1`
|
||||
(последовательность `_setMode('plan')` → `_tool='resize'` → cancel → `_setMode('decor')` →
|
||||
`_setMode('view')`), которую этот же диф добавил в `demo/benchmark_large_house.mjs`. Разница ровно
|
||||
+40 записей (два «лишних» полных прохода флор-кэша сверх документированного одного) коррелирует с тем,
|
||||
что тайминговые метрики именно `editorSeriesMs`/`longTask.editorSeries.*` превышены в 2,4× и более —
|
||||
похоже на одну и ту же причину (лишний(е) пересчёт(ы) `_cleanFloor`/`floorMinusBodies` при переключении
|
||||
режима редактора), а не на независимый шум.
|
||||
|
||||
Попытка воспроизвести на упрощённой ручной фикстуре (`page.evaluate` с прямым вызовом `_setMode` без
|
||||
полного цикла реального pointer-move, который использует бенчмарк) рост `_cleanFloorCache.size` не
|
||||
показала — то есть причина не в самом факте смены режима, а где-то в фактической
|
||||
resize/decor-транзакции бенчмарка (drag-preview → cancel) или в стечении с реальными pointer-событиями.
|
||||
Дальнейшая локализация — задача автора; здесь важен сам факт: **AC10 не выполнен по написанному самим
|
||||
автором для этой задачи бюджету**, а не только гипотеза о причине.
|
||||
|
||||
Тайминговые превышения (`interactionSeriesMs`, `editorSeriesMs`, `longTask.editorSeries.*`) в отличие
|
||||
от `cleanFloor` теоретически могут быть частично усилены более медленной/разделяемой машиной ревьюера
|
||||
против выделенной машины владельца (`demo/performance/README.md`: «Local Windows checkout is the
|
||||
day-to-day environment... local report is diagnostic only»), но margin (1842 мс против потолка 750 мс,
|
||||
878 мс одна long task против потолка 150 мс) для чистого шума великоват, и коррелирует с детерминированным
|
||||
`cleanFloor`-превышением. Автору стоит перепроверить оба числа на каноническом Linux CI парном прогоне
|
||||
(`performance.yml`), но независимо от результата **cache-count уже доказан красным и машинно не
|
||||
объясним** — это не «непрогнанный дорогой гейт», это прогнанный и красный.
|
||||
|
||||
**Почему High, а не Medium.** Задача, ценность 10/10 и P1, явно посвящена именно тому, чтобы новый
|
||||
перф-контракт «краснел сам по себе, а не только на фоне предыдущего коммита» (issue, раздел «Почему это
|
||||
не поймал перф-гейт»). Проверка, которую AC10 называет своим доказательством, при фактическом запуске
|
||||
красная. Это не вопрос «недостаточно полно проверили», а обнаруженный красный автотест по собственному,
|
||||
введённому этим же диффом контракту — ровно то, что код-ревью обязано поймать (PROCESS.md §2.7:
|
||||
«ревьюер отвечает за AC… либо доказан автотестом… либо разобран по коду»). Фикс — в скоупе (тот же
|
||||
`demo/benchmark_large_house.mjs`/бюджет), отдельный issue не заводится.
|
||||
|
||||
### M1 (Medium, в скоупе) — обещанный в самом ТЗ «targeted production-bundle smoke» не создан
|
||||
|
||||
ТЗ §13.2 и §14 (план реализации, п. 5) явно называют отдельный артефакт: «Targeted `demo/smoke_*.mjs` —
|
||||
production-bundle render-count и interaction matrix», инструментирующий full-render count, lightweight
|
||||
paints, diagnostics scans, heavy node identity, writes на PRODUCTION-бандле (не на perf-фикстуре) — и
|
||||
именно это заявлено как доказательство AC5/AC6/AC7/AC8 (`smoke` в их графе доказательства).
|
||||
|
||||
`git diff origin/dev...HEAD --diff-filter=A` показывает: новых файлов `demo/smoke_*.mjs` нет вовсе.
|
||||
Изменения в существующих смоках (`smoke_decor.mjs`, `smoke_furniture.mjs`, `smoke_decor_text.mjs`,
|
||||
`smoke_resize_pointer_real_plan.mjs`, `smoke_pan_any_zoom.mjs`, `smoke_room_fit.mjs`,
|
||||
`smoke_infinite_canvas.mjs`, `smoke_opening_measure.mjs`, `smoke_partition_openings.mjs`,
|
||||
`smoke_drag_bounds.mjs`, `smoke_room_tooltip_toggle.mjs`, `smoke_ux_fixes.mjs`) — точечная подгонка под
|
||||
новую разметку `data-hp-live-*`/тайминг RAF, ни один не считает full-render/diagnostics-scan счётчики.
|
||||
|
||||
Единственное место, где эти структурные гарантии вообще проверяются — `demo/benchmark_large_house.mjs
|
||||
--profile=large-house-interaction-v1`, которое подключено только к job'е `performance.yml` → «Full
|
||||
Performance», запускаемой (см. `demo/performance/README.md`, «CI contracts») **на промоушен в `main`,
|
||||
раз в неделю и по ручному диспатчу** — не на каждый push в `dev`. Блокирующий `performance_smoke` в
|
||||
`validate.yml` меряет только `large-house-glow-overlay-v1`, этого профиля не касается. При принятой в
|
||||
проекте модели прямых коммитов в `dev` без PR (AGENTS.md) это означает: следующий обычный коммит,
|
||||
который случайно вернёт diagnostics-scan на каждый рендер или полный render на hover, **не будет
|
||||
пойман никаким гейтом до следующего еженедельного/promotion-прогона** — то есть тем самым классом
|
||||
слепого пятна, ради которого заведён #451 (issue, раздел «Почему это не поймал перф-гейт»: «гейт
|
||||
исправно подтверждал, что "не стало хуже", пока пользователь смотрел на 451-миллисекундные фризы»).
|
||||
|
||||
Фикс в скоупе: добавить обещанный `demo/smoke_*.mjs` (инструментирование `_renderBody`/
|
||||
`_bindingStatus` аналогично тому, что уже сделано внутри `benchmark_large_house.mjs`, но на
|
||||
production-бандле через `demo/serve.mjs`, в постоянно запускаемом наборе).
|
||||
|
||||
### M2 (Medium, в скоупе) — нет мутанта в `scripts/mutation-gate.mjs` для новых защитных механизмов
|
||||
|
||||
PROCESS.md §2.7: «Мутант в `scripts/mutation-gate.mjs` обязателен, когда защита живёт в продуктовом
|
||||
коде и проверяется дорогим гейтом (смок, бэкенд, golden): там ревьюер не воспроизведёт отрицательный
|
||||
прогон второй раз». `git diff origin/dev...HEAD -- scripts/mutation-gate.mjs` — пусто, ни одной новой
|
||||
записи.
|
||||
|
||||
Часть новых защит — чистые юниты, и для них я сам прогнал мутацию и увидел красный тест (см. «Таблица
|
||||
доказательств» ниже: `classifyHassRenderChange`, `RenderLifecycle.diagnostics`) — этого достаточно,
|
||||
отдельный мутант не нужен. Но интеграционная проводка в `houseplan-card.ts` (переопределённый
|
||||
`requestUpdate`, `_flushHa`/terminal reconciliation, RAF-coalesced camera/hover/editor paints) —
|
||||
юнитами не покрыта и проверяется только дорогим Playwright-бенчмарком (тем же, что в M1/H1). Без
|
||||
постоянного мутанта в следующем раунде (после того, как H1 будет исправлен и профиль перестанет
|
||||
краснеть) ревьюер снова не сможет дёшево доказать «тест умеет падать» — придётся заново гонять
|
||||
браузерный бенчмарк вручную, как это делал я.
|
||||
|
||||
### Low — известная нестабильность `smoke_smooth_zoom.mjs` под нагрузкой (не регрессия, к сведению)
|
||||
|
||||
При параллельном прогоне 134 смоков (4 конкурентных пакета) `demo/smoke_smooth_zoom.mjs` дал один
|
||||
красный результат (`wheelRetargetsRunningTween`/`wheelHasIntermediateFrames`/`wheelStreamPersistsOnce`
|
||||
= false). Проверил отдельно: последовательно (без конкуренции за CPU) — 11/11 прогонов зелёные, как на
|
||||
этой ветке, так и на чистом `origin/dev`. Затем воспроизвёл красный результат **на обеих** ветках
|
||||
одинаково при явной конкуренции (4 параллельных `smoke_smooth_zoom.mjs` + 3 тяжёлых смока рядом: 2-3 из
|
||||
4 падали и на `dev`, и на PR). Вывод: это существующая до #451 чувствительность теста к CPU-контеншну
|
||||
(reliance на реальные `requestAnimationFrame` тайминги), не регрессия этой задачи. Не блокирует, в
|
||||
скоуп #451 не входит, отдельный issue не заводится (не находка, а объяснённая флакиность).
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **AC1** (HA intake ≠ visual invalidation): `requestUpdate('hass', …)` в `houseplan-card.ts`
|
||||
делегирует решение `LiveRuntime.hass()` → `classifyHassRenderChange()`; при `'none'`/deferred-`'state'`
|
||||
intake (`intakeHass`) всё равно выполняется синхронно через `RenderLifecycle.observe`. Доказано
|
||||
юнитом `test/houseplan-render-lifecycle.test.mjs` («HA intake runs once even when an unrelated visual
|
||||
update is skipped») + мутацией (см. ниже).
|
||||
- **AC2** (dependency classifier): `src/render-invalidation.ts`. Структурные top-level поля (`entities`,
|
||||
`devices`, `themes`, `locale.*`, неизвестный новый ключ) — fail-open на `'structural'`; только `states`
|
||||
сравнивается по dependency-scoped `entityIds`. Доказано `test/render-invalidation.test.mjs` (4/4) +
|
||||
мутацией: убрал ранний `return 'none'`→заменил на безусловный `return 'state'` в скомпилированном
|
||||
`test-build/render-invalidation.js` (не в `src/**`, генерируемый файл) — тест
|
||||
«an unrelated HA state row does not invalidate the plan frame» покраснел (`expected 'none', actual
|
||||
'state'`), затем восстановил файл, тест снова зелёный.
|
||||
- **AC3** (intake переживает пропущенный рендер): dependency projection в `_captureRenderDeviceSnapshot`
|
||||
(houseplan-card.ts:4563+) собирает entity/device/area из bindings, room temp/hum source, opening
|
||||
entity refs, live-text, `sun.sun`, vacuum source — доказано чтением и подтверждено проходом смоков
|
||||
`smoke_linked_virtual_light`, `smoke_static_icon`, `smoke_cover_no_plate`,
|
||||
`smoke_cover_plate_precedence`, `smoke_entity_parent_dedup`, `smoke_yellow_principle` (все зелёные).
|
||||
- **AC4** (diagnostics cache): `RenderLifecycle.diagnostics()`/`invalidate()` в
|
||||
`houseplan-render-lifecycle.ts`. Единственный вызов `houseplanDiagnostics()`-подобной логики теперь и
|
||||
в `_renderBody` (через `this._renderLife.diagnostics(...)`), и в публичном `houseplanDiagnostics()` —
|
||||
один источник (см. «Одно число — один источник» ниже). Доказано юнитом («diagnostics scan is cached
|
||||
and invalidated by tracked state presence») + мутацией: заменил guard `if (this.diagnosticsCache)` на
|
||||
`if (false && this.diagnosticsCache)` в `test-build/houseplan-render-lifecycle.js` — тест покраснел
|
||||
(`2 !== 1`, ожидался один скан вместо двух), восстановил, снова зелёный.
|
||||
- **AC5/AC6/AC7** (0 full render на hover/pan/pinch/camera/editor move, 1 terminal, deferred-relevant-tick
|
||||
last-wins): структурные assert'ы внутри `demo/benchmark_large_house.mjs` (throw при нарушении)
|
||||
подтверждены самостоятельным прогоном на этом SHA — все условия выполнены (`irrelevantFullRenders:0`,
|
||||
`hoverFullRenders:0`, `panMoveFullRenders:[0,0,0,0]`/`terminalFullRenders:[1,1,1,1]`,
|
||||
`cameraMoveFullRenders:0`/`cameraTerminalFullRenders:1`, `editorMoveFullRenders:[0,0,0]`,
|
||||
`relevantDuringGestureFullRenders:0` при `relevantDuringGestureIntakes:3`, `diagnosticsScans:0`,
|
||||
`heavyNodeStable:true`, `overlayErrorPx:0.01`). Тайминги того же прогона см. H1.
|
||||
- **AC8** (coalescing сохраняет конечную координату/commit/cancel): `pointer-move-queue.ts`
|
||||
(`queueMicrotask`, гарантированно опустошается до следующего браузерного события) + подключение во
|
||||
всех точках (`_pointerMove`/device, `_physicalMove`, `_rszMove`, `_bdMove`, `_opPointerMove`, `_dtMove`,
|
||||
`_decorMoveUpdate`, `_markupMove`) с явным `flush` на terminal (`up`) и `cancel` на отмене. Доказано
|
||||
юнитом `test/live-editor.test.mjs` («pointer move queue is event-turn coalesced, last-wins and
|
||||
flushable») + прогоном затронутых production-bundle смоков (`smoke_furniture`, `smoke_decor`,
|
||||
`smoke_decor_text`, `smoke_drag_bounds`, `smoke_partition_openings`, `smoke_opening_measure`,
|
||||
`smoke_resize_pointer_real_plan` — все зелёные, включая ровно те сценарии shift-snap/magnet/rotate,
|
||||
которые чувствительны к потере промежуточного состояния).
|
||||
- **AC9** (pixel-equivalence): `npm run golden:verify` — 153/153 `passed`, 0 diff, включая
|
||||
`large-house-zoom-040/250-dark`, `large-house-warm-remount-dark`, junction-серию, hover-серию,
|
||||
dark/light/RU/EN варианты. Прямое доказательство отсутствия непреднамеренных визуальных изменений.
|
||||
- **AC11** (implementation-loop gates): typecheck/test/build зелёные (таблица выше).
|
||||
- **AC12** (schema/backend/i18n/dependencies не изменены): `git diff --name-only` не содержит
|
||||
`.py`/`i18n`/`translations`/`manifest.json`/`hacs.json` — проверено чтением. Новые кэши
|
||||
(`RenderLifecycle.diagnosticsCache`, `LiveRuntime`/`live-viewport`/`live-hover`/`live-editor` state) —
|
||||
все `WeakMap<object,…>`, ключ — сам инстанс карточки; `disconnectedCallback` вызывает
|
||||
`_liveRt?.dispose()` и `_editorRuntime?._disposeLiveEditor()` (cancel RAF, restore hidden/transparent
|
||||
DOM, `cancelHouseplanPointerMove`) — bounded per card, очищаются при disconnect, независимы между
|
||||
несколькими карточками на дашборде (WeakMap не даёт общий каталог).
|
||||
- **AC13** (документация): `demo/performance/README.md` описывает новый профиль, сценарии, структурные
|
||||
assertions, абсолютные потолки, local diagnostics-команду; `docs/CHANGELOG.md`/`docs/CHANGELOG.ru.md`
|
||||
обновлены в том же коммите `c0d61ca3` (`User-Visible: yes`), формулировка «внешний вид и результат
|
||||
действий не меняются» соответствует AC9/non-scope.
|
||||
- **Одно число — один источник**: `houseplanDiagnostics()` (публичный support-report) и внутренний
|
||||
render-path теперь читают один и тот же `RenderLifecycle.diagnostics()` — раньше было два независимых
|
||||
прохода по маркерам с одинаковой логикой (потенциальное расхождение при будущей правке одного из них),
|
||||
теперь физически один вызов на одну инвалидацию (доказано юнитом «scans === 1» выше). Другой видимый
|
||||
дважды параметр — zoom badge (`Math.round(zoom*100)%`) и `_zoom`/`_view`: и DOM-бейдж
|
||||
(`live-viewport.ts:paintLiveViewport`), и canonical-поле читают один и тот же `frameOf(host)` —
|
||||
`host._zoom`, без второго независимого округления.
|
||||
- **Трейлеры**: все 10 коммитов несут `Issue: #451`; ровно один `User-Visible: yes`
|
||||
(`c0d61ca3`, «fix: keep large-plan interactions off the full render path») с обоими changelog в том
|
||||
же коммите; остальные `User-Visible: no` (документация/фикс-ап/ребилд бандла) — корректно.
|
||||
- **Touch/pen/kiosk/fixed-floor контракт** (§9 ТЗ): не даёт отдельной ветки поведения — подтверждено
|
||||
прохождением `smoke_isometric_live_touch`, `smoke_kiosk`, `smoke_kiosk_pan_lock`, `smoke_fixed_floor`,
|
||||
`smoke_touch_tips`, `smoke_grid_scale_invariance` (все зелёные) плюс golden dark/light/RU/EN сценарии.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- Полный `demo/smoke_*.mjs` (все ~221) — прогнал 134 (все «прямые» и «слабые» совпадения
|
||||
smoke-select.mjs), сознательно не прогонял оставшиеся ~87 без связи с диффом (полный набор —
|
||||
предрелизный гейт, PROCESS.md §8).
|
||||
- Парный `performance.yml` («Full Performance», base vs candidate на двух чекаутах, реальный Chromium
|
||||
CI-раннер владельца) — недоступен в этом окружении; вместо него прогнал `--absolute-only` (см. H1,
|
||||
нашёл реальную красноту) и структурные assert'ы бенчмарка напрямую. Относительное (base-vs-candidate)
|
||||
сравнение не проверено — вероятно, тоже покраснеет из-за той же причины, что и абсолютное, но это не
|
||||
проверено напрямую.
|
||||
- Backend (`python -m pytest tests_backend`) — не запускал, диф не касается `custom_components/**/*.py`
|
||||
(проверено чтением diff, не исполнением).
|
||||
- `npm run invariants` — не запускал, диф не касается геометрии/`layout`/`marker.space`/`open_spans`
|
||||
(проверено чтением diff и Non-scope раздела ТЗ).
|
||||
- Точная локализация причины лишних 40 записей `cleanFloor` (H1) — воспроизвёл и доказал факт, не нашёл
|
||||
точную строку кода; это оставлено автору при исправлении H1.
|
||||
- Ручная проверка в реальном браузере (вне headless Playwright) и на настоящей Home Assistant — вне
|
||||
доступного окружения; полагался на golden/smoke/unit/performance гейты.
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/451-render-performance`
|
||||
- SHA материала: `1850eb18b5e8f0813f9d2efb50694dc4ca1fff26`
|
||||
- Диапазон: `origin/dev...HEAD` (10 коммитов, `029aba6b`..`1850eb18`)
|
||||
- ТЗ: `docs/specs/451-render-performance.md`, ревью — `docs/reviews/SPEC-REVIEW-451-r2.md` (зелёное)
|
||||
|
||||
## Итог
|
||||
|
||||
**Вердикт: красный.** Одна High-находка (H1 — заявленный самим диффом перф-контракт `AC10` красный при
|
||||
фактическом прогоне `compare.mjs --absolute-only` по собственному новому бюджету, доказано
|
||||
детерминированным счётчиком `cache.entries.cleanFloor: 140 vs 100` плюс коррелирующими превышениями
|
||||
таймингов `editorSeriesMs`/`longTask.editorSeries.*`) блокирует переход. Плюс две Medium-находки в
|
||||
скоупе (M1 — не создан обещанный ТЗ `demo/smoke_*.mjs` для render-count на production-бандле, M2 — нет
|
||||
мутанта в `scripts/mutation-gate.mjs` для интеграционной проводки, проверяемой только дорогим
|
||||
бенчмарком). Сама архитектура решения (разделение intake/visual invalidation, dependency classifier,
|
||||
diagnostics cache, RAF-coalesced live-слои камеры/hover/редакторов, pointer-move очереди) сделана
|
||||
аккуратно и проверяемо: юнит-защиты действительно ловят мутацию, structural assertions бенчмарка
|
||||
действительно бросают исключение при нарушении контракта, golden/smoke/typecheck/build/docs — все
|
||||
зелёные. Возврат автору — на исправление конкретно H1 (и желательно M1/M2 в том же цикле, раз без
|
||||
High они и так были бы Medium-в-скоупе), без расширения скоупа задачи.
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/451-render-performance`, коммит `1850eb18b5e8` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `30dcff478a0287b209be4c3066972158d6e6b539`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 30dcff478a02
|
||||
```
|
||||
- ТЗ `docs/specs/451-render-performance.md`, блоб `7c323a29110974aae369077214b9e2a74d9387c1`
|
||||
```
|
||||
git log --all --find-object=7c323a29110974aae369077214b9e2a74d9387c1 -- docs/specs/451-render-performance.md
|
||||
```
|
||||
Reference in New Issue
Block a user