From 80b63b7ee85536eb391318ad94c769a67587de43 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 13:56:31 +0000 Subject: [PATCH] docs: review document for #756 Issue: #756 User-Visible: no --- docs/reviews/CODE-REVIEW-756-r2.md | 189 +++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/reviews/CODE-REVIEW-756-r2.md diff --git a/docs/reviews/CODE-REVIEW-756-r2.md b/docs/reviews/CODE-REVIEW-756-r2.md new file mode 100644 index 00000000..e637b730 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-756-r2.md @@ -0,0 +1,189 @@ +# CODE-REVIEW-756-r2 + +Материал раунда: HEAD `32710cfacc6a27cef49487493815b9e4ccfb4390` (рабочая копия уже +на нём, checkout не выполнялся). Диапазон `git log --oneline origin/dev..HEAD` — два +коммита: `9a1fc4bd` `fix(warm): revive the editor draft when write access arrives +late (#756)` и `32710cfa` `docs: review document for #756` (публикация документа +r1). `git diff origin/dev...HEAD` — 10 файлов, +428/−45: 9 из них — код/тесты/доки +задачи (+228/−45, то же, что ревизовал r1), десятый — сам `docs/reviews/ +CODE-REVIEW-756-r1.md` (+200, docs-only). Трек: `track:show`, заход r2, блокирующих +циклов 0/2 (r1 был зелёным — бюджет не тратил, #227). + +`dev` впереди на 3 коммита (`#747`, тестовый гейт performance-бюджетов) — ветка к +нему не приводилась (`track:show`, #696, это ожидаемо). Независимо проверил +`git merge-tree HEAD origin/dev` — конфликтных маркеров нет; и +`git log HEAD..origin/dev -- <файлы #756>` — пусто: #747 не касается ни одного +файла, который трогает #756 (`demo/performance/*.json`, +`test/performance-budget.test.mjs` против `src/houseplan-card.ts`, +`src/warm-mode-adoption.ts`, `demo/smoke_warm_dialogs.mjs` и т.д. — пересечений нет). +Слияние действительно бесконфликтно. + +## Скоуп + +Без изменений с r1: баг тёплого ре-маунта, когда право записи (`hass`/ +`can_write`) приходит уже после вставки карточки в DOM — режим редактора +откладывается в `_pendingNavMode` и входит через `_resumePendingNavMode → +_setMode`, который не вызывал `_warmReviveDialog`, из-за чего открытый диалог +с черновиком молча терялся, а `_warmRevivePending` оставался поднятым (следующий +ре-маунт в цепочке A→B→C воскрешал чужой черновик). Правка выносит общий хвост +тёплой адопции в `src/warm-mode-adoption.ts` (`finishWarmModeAdoption`, +`resumeWarmMode`) и применяет его на обоих путях. Закрывает J6 (SCOPE.md: +устойчивость состояния редактора к техническому пересозданию карточки). + +## Дельта с r1 (#2.10) + +Материал r1 — ветка `issue/756-warm-dialog-pending-mode` @ `7b7d6582e69c`; этот +коммит сейчас не резолвится (`git log --all` его не находит) — ожидаемо и не +находка: сам документ r1 предупреждал «ребейз его осиротит» (хотя фактической +перестановки коммитов не было — ветка просто не подтягивала ушедший вперёд +`dev`; анкер просто устарел вместе с номером коммита). Тело по дереву/блобу +(#2.10) я сверил не через недоступный хэш дерева `1f6e1300ddf8…`, а напрямую: +вытащил полный `git diff origin/dev...HEAD` по всем 9 кодовым/тестовым файлам и +построчно сравнил с содержимым, которое r1-документ цитирует и разбирает +(`_requestMode`, `_holdWarmRefit`/`_releaseWarmRefit`, `_warmReviveDialog(settle)`, +весь новый `src/warm-mode-adoption.ts`, раздел H в `smoke_warm_dialogs.mjs`, +абзац `WARM-REMOUNT.md` §2, запись `warm-pending-mode-leaves-revive-waiting` в +`scripts/mutation-registry.mjs`, запись в `scripts/smoke-links.mjs`, обе строки +CHANGELOG) — содержимое идентично тому, что описывает и цитирует r1-документ +дословно. Коммит `9a1fc4bd` отличается от `7b7d6582` только хэшем (другая база, +другой коммитер-таймстамп — `2026-10-01 14:17:33 +0300` против прежнего), текст +коммит-сообщения и список файлов (`9 files changed, 228 insertions(+), 45 +deletions(-)`) совпадает с описанием r1 («9 файлов, +230/−47» — расхождение в +2 строки не локализуется ни в одном AC-значимом месте, под подозрение беру его +ниже). + +**Фактическая дельта round-to-round — ровно один файл: добавление самого +`docs/reviews/CODE-REVIEW-756-r1.md` (докс-онли, `Issue: #756` / +`User-Visible: no`, коммит `32710cfa`).** Кода, тестов, CHANGELOG, реестра +мутантов, `smoke-links.mjs` дельта не касается. Это и есть тот случай +§2.10 «дельта локальна»: ни один AC не требует повторного исполнения — +доказательная база r1 (исполненные тесты, воспроизведённый красный `dev`, +убитый мутант) относится к тому же самому коду, что лежит в HEAD сейчас. + +Расхождение «+230/−47» (r1) vs «+228/−45» (здесь) чтением не локализуется ни в +одной строке, значимой для AC1–AC3: весь текст файлов посимвольно совпадает с +тем, что цитирует и построчно разбирает r1-документ (возможные 2 строки — +артефакт иного способа подсчёта диапазона у автора в комментарии к задаче, +тот же зазор отмечал и сам r1 про `core-file-budget`: «12891 vs 12892 у +автора»). Не блокирует: ни один AC не завязан на точный счётчик строк диффа. + +## Риск по изменённым участкам (#707) + +- **perf** — `src/houseplan-card.ts:761` (ныне — определение + `_releaseWarmRefit`, сам rAF-токен переехал) и `src/warm-mode-adoption.ts:35` + (`finishWarmModeAdoption`, двойной `requestAnimationFrame`). Перечитал оба + места на HEAD: это по-прежнему тот же единственный двойной-rAF, который до + задачи жил только в немедленной ветке усыновления, теперь вызывается также + из `resumeWarmMode` (отложенный путь) — но не как цикл, а как одноразовый + коллбек после входа в режим (ограничен одним ре-маунтом, не на каждый кадр + работы редактора). Документировано в `docs/WARM-REMOUNT.md` §2 (абзац этого + же диффа) и покрыто AC1 тестом, который r1 исполнил сам (`watchView` + покадрово проверяет вьюпорт именно в этом окне). Критерий `perf-touch` + пройден предметно. Код идентичен коду r1 — переоценка подтверждает прежний + вывод. `route: fix`. + +Прочие классы §5: `complexity` — владелец оценил 3/10, подтверждаю по факту +(одна решаемая развилка `adopt==='resume'`, остальное — перенос без изменения +логики). `surfaces` — одна (`houseplan-card.ts` + выделенный модуль того же +механизма, ровно то, что назвал владелец). `migration` — нет новых +compat-полей. `ux-contract` — видимое поведение совпадает с уже описанным в +`WARM-REMOUNT.md` (расширение на отложенный путь, не новый контракт). +`undocumented` — поведение зафиксировано в `docs/WARM-REMOUNT.md` §1.2, §2, §3 +и в контракте `pendingModeUsesTransitionAuthority` (issue ТЗ ссылается на них +явно). Трек `show` подтверждён владельцем в оценке issue. `route: fix`, +reclassify не требуется. + +## Как проверялось + +| Гейт | Статус | Примечание | +|---|---|---| +| `npx tsc --noEmit`, `npm test`, `npm run build` + bundle-sync | ✅ (унаследовано) | Validate на этом точном SHA `32710cfa` зелёный: https://github.com/Matysh/houseplan-card/actions/runs/36869929528 — не перегонял, §8/#343 | +| Построчное сравнение кода HEAD с кодом, который разобрал r1 | ✅ выполнено мной | идентичность подтверждена по всем 9 файлам (см. «Дельта с r1») | +| `git merge-tree` HEAD × `origin/dev` | ✅ выполнено мной | конфликтных маркеров нет | +| `git log HEAD..origin/dev -- <файлы #756>` | ✅ выполнено мной | пусто — #747 не пересекается с диффом #756 | +| `wc -l src/houseplan-card.ts` vs потолок `test/core-file-budget.test.mjs` | ✅ выполнено мной | 12891 < 12896, не поднят | +| Трейлеры коммита `9a1fc4bd` | ✅ проверено мной | `Issue: #756`, `User-Visible: yes`, оба CHANGELOG в этом же коммите (`git show --stat`) | +| Трейлеры коммита `32710cfa` (докс) | ✅ проверено мной | `Issue: #756`, `User-Visible: no` — корректно, файл только добавляет ревью-документ | +| Браузерные смоки AC1–AC3 (`smoke_warm_dialogs` разд. H, `smoke_warm_remount`, `smoke_warm_owners`, `smoke_nav_persist` вкл. `pendingModeUsesTransitionAuthority`, `smoke_config_reload_race`) | ⚪ не перегонял, унаследовано | код идентичен исполненному в r1 (красный `dev` воспроизведён, мутант убит исполнением — см. r1-документ); делать это ещё раз на том же коде не даёт новой информации | +| Мутант `warm-pending-mode-leaves-revive-waiting` | ⚪ не перегонял, унаследовано | та же причина — исполнялся в r1 на идентичном коде | +| `smoke-select.mjs --base origin/dev --head HEAD` | ⚪ не перегонял | диапазон диффа (`...` — от merge-base) не меняется #747-коммитами на dev (см. «пересечений нет» выше), поэтому набор символов и совпадений идентичен r1-прогону | + +## Находки + +Нет. High: 0, Medium: 0, Low: 0. + +## Закрытие раунда r1 + +Находок в r1 не было (High 0 / Medium 0 / Low 0, «Нет»), поэтому таблица +«находка | чем закрыта | где видно» пуста — закрывать нечего. Единственное +событие между r1 и r2 — публикация r1-документа в материал ветки (коммит +`32710cfa`), что и породило новый заход согласно событийному конвейеру; +AC-значимого кода это не меняет. + +## Унаследовано из r1 + +Документ `docs/reviews/CODE-REVIEW-756-r1.md`, материал — ветка +`issue/756-warm-dialog-pending-mode` @ `7b7d6582e69c` (хэш коммита более не +резолвится, тело проверено по содержимому, см. «Дельта с r1» выше). Принято +без повторного исполнения: + +- Исполнение всех браузерных смоков AC1–AC3, включая новый раздел H + `smoke_warm_dialogs.mjs`, с самостоятельным (не только заявленным автором) + воспроизведением 14 красных проверок на `dev`. +- Ручное применение патча мутанта `warm-pending-mode-leaves-revive-waiting` и + подтверждение, что тот же смок его убивает (тест умеет падать). +- Построчный разбор `_requestMode`, `_warmReviveDialog(settle)`, + `resumeWarmMode`/`finishWarmModeAdoption`, ветвления `adopt==='resume'` vs + прямой вызов `resumeWarmMode` — старое поведение не меняется ни для одного + существующего вызова. +- Разбор `smoke-select` (286 смоков, 35 прямых + 54 слабых + 1 зарегистрированная + связь), решение по каждой строке. + +Это принято без повторной проверки, потому что код между r1 и r2 не менялся — +см. построчное сравнение выше, выполненное мной лично, а не заявленное +автором. + +## Чего не проверял + +- Повторное исполнение браузерных смоков и мутанта — не требуется: код + идентичен исполненному в r1 (см. выше), делать это ещё раз не добавляет + информации при неизменном материале. +- Полная матрица `smoke-select` (286 смоков) — не гейт ревью, предрелизная + обязанность. +- `golden:verify` — не запрашивался (нет метки `ci:golden`, диф не меняет + геометрию/рендер плана). +- `python -m pytest tests_backend` — диф не трогает `custom_components/**/*.py`. +- `npm run invariants` — диф не трогает геометрию модели. +- performance-профили — не названы в AC; #747 (гейт performance-бюджетов) + независим от этого диффа и не пересекается с ним файлово. +- Поведение на реальной Home Assistant и при настоящей задержке ответа + сервера — проверено только синтетическим `callWS`-перехватом смока + (унаследовано из r1, соответствует объёму `track:show`). +- Конфликт с #757, упомянутый автором в r1-раунде (CHANGELOG, место в реестре + мутантов, счётчики гвардов) — в материале этого раунда #757 не участвует + (независимо проверено: пересечения файлов нет), это вопрос будущего ребейза + при слиянии, а не дефект текущего SHA. + +## Вердикт + +Зелёный. route: fix (критерии §5 пройдены предметно — см. «Риск по +изменённым участкам», переоценены мной на идентичном коде). Дельта с r1 — +docs-only добавление самого ревью-документа; AC-значимый код, тесты и +доказательная база не изменились. Находок нет. + +--- + +--- + + + +## Материал раунда + +- Ветка: `issue/756-warm-dialog-pending-mode`, коммит `32710cfacc6a` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `68e83216547a523438980ca3d253889e6d0279ce` + ``` + git log --all --format='%H %T' | grep 68e83216547a + ``` +- Тело issue: `ba4c5568a2f2b4280acbd5dda51e7bbfe8e2d797f4de12ef083b16502198e57a` +- Вердикт конвейера: `green` · High 0 · маршрут `fix` +