mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
docs: review document for #449
Проверка (CI) / Классификация изменённых файлов (push) Successful in 37s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 36s
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 57s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Классификация изменённых файлов (push) Successful in 37s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 36s
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 57s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Issue: #449 User-Visible: no
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
# CODE-REVIEW-449-r2
|
||||
|
||||
- Issue: https://github.com/Matysh/houseplan-card/issues/449
|
||||
- Этап: код-ревью (PROCESS.md §2.7), заход r2, блокирующих циклов израсходовано 1/4
|
||||
- ТЗ: `docs/specs/449-double-fit-all.md` (трек полный)
|
||||
- Материал: `05ef3181` (SHA r1) → `aa51ed2b` (`fix: complete double background fit delivery`) → `1d8edf4a` (`docs: refresh screenshot fingerprint for double fit`)
|
||||
- SHA материала этого раунда: `1d8edf4a016736eb1ee6e865ab6d686cec0ac403`
|
||||
- Ветка: `issue/449-double-fit-all`
|
||||
- Предыдущий раунд: `docs/reviews/CODE-REVIEW-449-r1.md`, вердикт **красный**, High: 3, Medium: 2, Low: 1, получен на SHA `05ef318133ca54b1f702844253d06edd5a665c5e`
|
||||
|
||||
## Скоуп
|
||||
|
||||
Разбор — по дельте (PROCESS.md §2.9/§2.10): между SHA r1 и текущим HEAD два
|
||||
коммита, ребейза не было (`git merge-base 05ef3181 HEAD` = `05ef3181`),
|
||||
контракт поведения не менялся, новая подсистема не задета — дельта локальна к
|
||||
трём High и двум Medium предыдущего раунда.
|
||||
|
||||
`git diff --stat 05ef3181..HEAD` (30 файлов, из них 374 строки — это сам
|
||||
документ r1, коммитящийся автоматически, не код автора):
|
||||
|
||||
- `src/houseplan-card.ts` — 2 содержательные правки: `_doubleFitEnabled`
|
||||
больше не проверяет `!_suppressClick`; `pointerUp` получает явный
|
||||
`blocked`-аргумент `this._suppressClick || …`, вычисляемый непосредственно в
|
||||
момент отпускания.
|
||||
- `scripts/bundle-budget.mjs` — `INITIAL_VIEW_GZIP_CEILING` 296 000 → 297 000,
|
||||
с комментарием-обоснованием запаса.
|
||||
- `scripts/mutation-gate.mjs` — удалён мутант
|
||||
`room-fit-enters-kiosk-double-tap-sequence` (ослепший свидетель из M2 r1).
|
||||
- `dist/**`, `custom_components/houseplan/frontend/**` — пересборка и
|
||||
синхронизация (закрывает High-1).
|
||||
- `docs/images/screenshots.json` — обновлённый фингерпринт (закрывает
|
||||
High-2).
|
||||
- `docs/reviews/CODE-REVIEW-449-r1.md` — сам документ прошлого раунда,
|
||||
коммитится конвейером, не тронут вручную.
|
||||
|
||||
Тестовые файлы (`test/room-fit.test.mjs`, `demo/smoke_*.mjs`) не менялись —
|
||||
новых unit-доказательств для правки гонки не добавлено, вместо этого
|
||||
перепроверен уже существующий смок, названный ТЗ как доказательство AC3 (см.
|
||||
ниже).
|
||||
|
||||
## Закрытие раунда r1
|
||||
|
||||
| Находка r1 | Чем закрыта | Где это видно |
|
||||
|---|---|---|
|
||||
| **High-1** — `custom_components/houseplan/frontend/**` не синхронизирован с `dist/**`, фича не доставлена | `npm run bundle:sync` выполнен и закоммитен в `aa51ed2b` | `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` → идентичны (перепроверено: свежая пересборка `npm run build` на HEAD не меняет рабочее дерево — `git status --short` пуст) |
|
||||
| **High-2** — `docs`-гейт красный, фингерпринт скриншотов устарел | Коммит `1d8edf4a`, `docs/images/screenshots.json` пересчитан | `node scripts/check-docs.mjs` → `Documentation checks passed (7 files, 12 external links)`, exit 0 (перепрогнано) |
|
||||
| **High-3** — `smoke_kiosk_pan_lock.mjs`: `doubleTapStillResetsZoom` красный из-за гонки `_suppressClick` ↔ double-tap | `_doubleFitEnabled` больше не гейтит взвод (`pointerDown`) через `_suppressClick`; блокировка при завершении (`pointerUp`) читает `this._suppressClick` напрямую и синхронно, без зависимости от порядка со сброшенным в этом же `pointerDown` флагом | `node demo/smoke_kiosk_pan_lock.mjs` → **`doubleTapStillResetsZoom: true`**, весь сценарий 4/4 OK (перепрогнано, было `expected true, got false`) |
|
||||
| **Medium-1** — запас bundle-budget ниже шумовой полосы (257 Б < 500 Б) | `INITIAL_VIEW_GZIP_CEILING` 296 000 → 297 000 в `aa51ed2b`, с задокументированным расчётом | `npm run bundle:budget` → `295746 B gzip (потолок 297000 B ±2000, headroom 4254 B)`; `npm test` — тест `#438` зелёный (перепрогнано) |
|
||||
| **Medium-2** — мутант `room-fit-enters-kiosk-double-tap-sequence` ничего не ловит (устаревшее место защиты) | Мутант удалён из `scripts/mutation-gate.mjs` (один из двух вариантов исправления, предложенных r1: «удалить или перенаправить») | `grep -n "room-fit-enters-kiosk-double-tap-sequence" scripts/mutation-gate.mjs` → пусто; функциональное покрытие того же контракта остаётся в unit-тесте `#449 moved, cancelled, multitouch, foreign-owner…` и в полях смока `kioskRoomTapDoesNotEnterDoubleTap` (оба зелёные, см. ниже) |
|
||||
| **Low-1** — AC5 для device/opening доказана только чтением, не смоком | Не тронуто в этой дельте — оставлено с той же пометкой, автор не заявлял правку | Инвентаризация не изменилась, см. «Унаследовано из r1» |
|
||||
|
||||
## Унаследовано из r1 (без повторной проверки)
|
||||
|
||||
Дельта не затрагивает следующие поверхности — их вывод принят из
|
||||
`docs/reviews/CODE-REVIEW-449-r1.md` (SHA `05ef318133ca`) без повтора:
|
||||
|
||||
- Архитектура `DoubleFitGestureRecognizer` / `beginDoubleFitPointer` /
|
||||
`completeDoubleFitPointer` как чистых функций без DOM/side-effects — код
|
||||
этих функций не менялся в дельте.
|
||||
- `planGestureOwnerFromPath` и единый owner-контракт с #152 — `src/room-fit.ts`
|
||||
не входит в дельту.
|
||||
- AC1, AC2, AC4, AC6, AC7, AC8, AC9, AC10 — доказательства (unit-таблицы,
|
||||
мутанты `double-fit-*`, `smoke_room_fit.mjs`, `smoke_smooth_zoom.mjs`) их
|
||||
код не менялся; тем не менее 4 мутанта #449 и оба смока перепрогнаны в этом
|
||||
раунде заодно с AC3/AC11 (см. таблицу гейтов) и остаются зелёными — это не
|
||||
«наследование вслепую», а побочная переподтверждение той же дельтой правки в
|
||||
соседних строках того же файла.
|
||||
- Low-1 (AC5 device/opening — доказательство чтением) — не переоценивалось,
|
||||
дельта его не касается.
|
||||
- `docs/CANVAS.md`, `docs/TOUCH-SUPPORT.md`, `docs/UX-MODES.md`,
|
||||
`docs/USER-GUIDE.md(.ru)`, оба `CHANGELOG` — не менялись в этой дельте,
|
||||
содержание принято как проверенное в r1.
|
||||
|
||||
## Как проверялось — гейты этого раунда
|
||||
|
||||
Зелёного Validate на SHA `1d8edf4a` нет — все гейты ниже прогнаны локально.
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Типы | `npx tsc --noEmit` | зелёный |
|
||||
| Юниты | `npm test` | **зелёный**: 1913 pass / 0 fail / 1 skip (пропуск — известный `#281` private fixture, не относится к #449); все 3 красных теста r1 (`#438`, `#349` манифест бандла, release ZIP) теперь проходят |
|
||||
| Сборка | `npm run build` | зелёный, `git status --short` после сборки пуст — бандл детерминированно воспроизводит закоммиченный |
|
||||
| Три копии бандла | `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` | зелёный: идентичны |
|
||||
| Синхронизация стенда | `npm run bundle:sync` (нужна демо-смокам, `demo/srv/assets` не коммитится) | зелёный, `git status --short` пуст — ничего не разошлось с закоммиченным |
|
||||
| docs-гейт | `node scripts/check-docs.mjs` | зелёный: «Documentation checks passed (7 files, 12 external links)» |
|
||||
| any-гигиена | `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | зелёный: «Новых any нет» |
|
||||
| bundle-budget | `npm run bundle:budget` | зелёный: 295746 Б / потолок 297000 ±2000, headroom 4254 Б (в тесте `#438` запас на обеих границах полосы выше 500 Б) |
|
||||
| Смок AC3 (ранее красный) | `node demo/smoke_kiosk_pan_lock.mjs` | **зелёный**: `doubleTapStillResetsZoom: true`, весь набор 23/23 |
|
||||
| Мутанты #449 (4, затронуты соседними строками) | `node scripts/mutation-gate.mjs --id=<id>` × 4 | все 4 «покраснел, как обязан» |
|
||||
| Подбор смоков по дельте r1→HEAD | `node scripts/smoke-select.mjs --base 05ef3181 --head HEAD` | «Прямое совпадение» — 23 файла (сузилось с 35 у полной задачи — символы дельты: `_suppressClick`, `_pinchStart`, `_panLock`, `_holdFired`, `_mode`, `_modeTransitionBusy`, `_continuity`, `_vacFit`) |
|
||||
| Прямые смоки (23 из «прямое совпадение») + `smoke_room_fit.mjs`, `smoke_kiosk.mjs`, `smoke_smooth_zoom.mjs` (канонические для #449, отмечены инструментом только слабой связью по `_mode`) | `node demo/smoke_*.mjs` по каждому (26 всего) | все 26 PASS |
|
||||
| `golden:verify` | не прогонялся | дельта не меняет разметку/геометрию/стили — только арбитраж жеста и числовые константы; наследует вывод r1 |
|
||||
| `pytest tests_backend` | не прогонялся | дельта не касается `custom_components/**/*.py` |
|
||||
| model-invariants | не прогонялся | дельта не меняет геометрию/`layout`/толщину стен |
|
||||
| «Слабая связь» smoke-select (33 файла, не считая трёх названных выше) | не прогонялись | символы (`_mode`, `_modeTransitionBusy`) относятся к неизменённым ветками логики этих файлов — то же решение, что в r1 |
|
||||
|
||||
Working tree после каждого гейта, менявшего файлы (`npm run build`,
|
||||
`npm run bundle:sync`, `mutation-gate`), возвращён к состоянию коммита; в
|
||||
репозитории изменений не оставлено (`git status --short` — пусто).
|
||||
|
||||
## Находки
|
||||
|
||||
Новых находок нет. Все 3 High и оба Medium раунда r1 закрыты по существу (см.
|
||||
«Закрытие раунда r1»), закрытие перепроверено запуском гейтов, а не со слов
|
||||
автора — правка не описана отдельным комментарием в issue, поэтому весь
|
||||
разбор в этом документе получен из чтения `git diff 05ef3181..HEAD` и
|
||||
перезапуска гейтов, а не пересказом чужого резюме.
|
||||
|
||||
Отдельно проверено, что исправление гонки High-3 не открыло новую дыру:
|
||||
`_suppressClick` по-прежнему учитывается в `blocked`-аргументе `pointerUp` —
|
||||
это защищает случай короткого движения (4–8 px, до срабатывания
|
||||
`_panLock`/`_pinchStart`), где раньше единственной защитой был именно этот
|
||||
флаг. Убрана только его роль в geiter'е `_doubleFitEnabled`, используемом на
|
||||
`pointerDown` (взвод), — то есть ровно то место, где гонка и была: значение
|
||||
`_suppressClick` на входе в `pointerDown` могло быть устаревшим, потому что
|
||||
сам `pointerDown` сбрасывает флаг в 6792 строке уже ПОСЛЕ вызова
|
||||
`_doubleFit.pointerDown(...)` на 6746 строке. Проверено чтением и подтверждено
|
||||
перепрогоном ранее красного смока.
|
||||
|
||||
## Таблица AC → чем доказан → статус (только AC, затронутые дельтой)
|
||||
|
||||
| AC | Доказательство | Статус |
|
||||
|---|---|---|
|
||||
| AC3 kiosk parity | `smoke_kiosk.mjs` PASS, `smoke_kiosk_pan_lock.mjs` — все 23 поля PASS, включая `doubleTapStillResetsZoom` | **PASS** (было FAIL в r1) |
|
||||
| AC11 perf/bundle budget | `no-new-any` зелёный; `npm run bundle:budget` зелёный с явно достаточным запасом (743/1254 Б от границ полосы); `npm test` bundle-assets (`#438`) зелёный | **PASS** (было FAIL в r1) |
|
||||
|
||||
Остальные AC1, AC2, AC4–AC10 — см. «Унаследовано из r1»; их доказательства
|
||||
перепрогнаны попутно (мутанты, `smoke_room_fit.mjs`, `smoke_smooth_zoom.mjs`)
|
||||
и остаются зелёными.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- Правка гонки `_suppressClick` устраняет именно тот механизм, который был
|
||||
разобран в H3 r1 (порядок вызовов внутри `_stagePointerDown`), а не
|
||||
маскирует симптом таймаутом или увеличением окна.
|
||||
- `INITIAL_VIEW_GZIP_CEILING` поднят с задокументированным расчётом запасов
|
||||
по обеим границам полосы — соответствует практике ратчета `#367`,
|
||||
упомянутой в r1.
|
||||
- Удаление мутанта `room-fit-enters-kiosk-double-tap-sequence` — чистое,
|
||||
без остаточных ссылок; функциональное покрытие того же контракта
|
||||
(room-tap не входит в kiosk double-tap sequence) остаётся в unit-тесте и
|
||||
смоке, оба перепрогнаны зелёными.
|
||||
- Три копии бандла (`dist`, `custom_components/houseplan/frontend`,
|
||||
рабочий стенд `demo/srv/assets`) синхронизированы и воспроизводятся
|
||||
детерминированно с закоммиченным деревом.
|
||||
- Оба фикс-коммита несут `Issue: #449` и `User-Visible: no` — обоснованно:
|
||||
они не меняют заявленное пользователю поведение (уже описанное в
|
||||
CHANGELOG фичевым коммитом), а чинят доставку и внутреннюю гонку.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- **`npm run golden:verify`** — не прогонял. Дельта не меняет разметку,
|
||||
геометрию или стили; правка касается только арбитража жеста и двух
|
||||
числовых констант. Риск визуальной регрессии нулевой; предрелизный гейт
|
||||
прогонит его перед бетой.
|
||||
- **`python -m pytest tests_backend`** — не прогонял, дельта не касается
|
||||
`custom_components/**/*.py` по содержанию (только пересобранный бандл).
|
||||
- **`node scripts/model-invariants.mjs`** — не прогонял, дельта не меняет
|
||||
геометрию/`layout`/толщину стен.
|
||||
- **«Слабая связь» smoke-select (33 файла)** — не прогонял, символы
|
||||
(`_mode`, `_modeTransitionBusy`) относятся к неизменённым дельтой ветвям
|
||||
этих файлов.
|
||||
- **Полный набор `demo/smoke_*.mjs` (221 файл)** — не прогонял целиком, это
|
||||
предрелизная обязанность (§8), не гейт ревью.
|
||||
- **AC5 (device/opening double-fit) отдельным смоком** — не перепроверял,
|
||||
дельта его не касается; наследует Low-1 из r1 как есть.
|
||||
- **Реальное устройство/браузер** — вне цикла ревью (PROCESS.md §2.7).
|
||||
|
||||
## Итог
|
||||
|
||||
Все 3 High и оба Medium раунда r1 закрыты и перепроверены запуском
|
||||
соответствующих гейтов (не со слов автора). Новых находок дельта не внесла.
|
||||
High: 0, Medium: 0 → вердикт зелёный.
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/449-double-fit-all`, коммит `1d8edf4a0167` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `87b750f7709b129fda0a1f4d9c16749b751e7a1b`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 87b750f7709b
|
||||
```
|
||||
- ТЗ `docs/specs/449-double-fit-all.md`, блоб `d6ddccbb3920bdffbb8414f4b6c225a4cdf4f420`
|
||||
```
|
||||
git log --all --find-object=d6ddccbb3920bdffbb8414f4b6c225a4cdf4f420 -- docs/specs/449-double-fit-all.md
|
||||
```
|
||||
Reference in New Issue
Block a user